1"use strict";(self.webpackChunkig_website=self.webpackChunkig_website||[]).push([[13120],{71229:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>a,toc:()=>l});const a=JSON.parse('{"id":"reference/verify-assets","title":"Verifying Assets","description":"Verify assets provided by Inspektor Gadget","source":"@site/versioned_docs/version-v0.54.1/reference/verify-assets.mdx","sourceDirName":"reference","slug":"/reference/verify-assets","permalink":"/docs/v0.54.1/reference/verify-assets","draft":false,"unlisted":false,"editUrl":"https://github.com/inspektor-gadget/inspektor-gadget/edit/main/versioned_docs/version-v0.54.1/reference/verify-assets.mdx","tags":[],"version":"v0.54.1","sidebarPosition":1000,"frontMatter":{"title":"Verifying Assets","sidebar_position":1000,"description":"Verify assets provided by Inspektor Gadget"},"sidebar":"mainSidebar","previous":{"title":"Kubernetes CLI Options","permalink":"/docs/v0.54.1/reference/kubernetes-flags"},"next":{"title":"Verifying Gadgets","permalink":"/docs/v0.54.1/reference/verify-gadgets"}}');var i=t(74848),s=t(28453);t(49489),t(7227);const r={title:"Verifying Assets",sidebar_position:1e3,description:"Verify assets provided by Ins
1pektor Gadget"},o=void 0,c={},l=[{value:"Verify the container image manually",id:"verify-the-container-image-manually",level:2},{value:"Verify the container Source Code Bill Of Materials (SBOMs)",id:"verify-the-container-source-code-bill-of-materials-sboms",level:2},{value:"Verify the gadget-builder image",id:"verify-the-gadget-builder-image",level:2},{value:"Verify an asset",id:"verify-an-asset",level:2},{value:"Verifying the checksums file",id:"verifying-the-checksums-file",level:3},{value:"Verify an asset",id:"verify-an-asset-1",level:3}];function d(e){const n={a:"a",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["Inspektor Gadget container image and release assets are signed using\n",(0,i.jsx)(n.a,{href:"https://github.com/sigstore/cosign",children:(0,i.jsx)(n.code,{children:"cosign"})}),".\nIn this guide, we will see how you can verify them with this tool.\nNote that, You would need to have ",(0,i.jsx)(n.code,{children:"cosign"})," ",(0,i.jsx)(n.a,{href:"https://github.com/sigstore/cosign/blob/main/README.md#developer-installation",children:"v2.0"})," installed."]}),"\n",(0,i.jsx)(n.h2,{id:"verify-the-container-image-manually",children:"Verify the container image manually"}),"\n",(0,i.jsx)(n.p,{children:"Verifying the container image is pretty straightforward:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'$ RELEASE=\'v0.27.0\'\n$ cosign verify --key https://raw.githubusercontent.com/inspektor-gadget/inspektor-gadget/${RELEASE}/inspektor-gadget.pub ghcr.io/inspektor-gadget/inspektor-gadget:${RELEASE}\nThe following checks were performed on each of these signatures:\n - The cosign claims were validated\n - Existence of the claims in the transparency log was verified offline\n - The signatures were verified against the specified public key\n\n[{"critical":{"identity":{"docker-reference":"ghcr.io/inspektor-gadget/inspektor-gadget"}, ...\n]\n'})}),"\n",(0,i.jsx)(n.p,{children:"Getting the above output followed by a JSON array of payloads, ensures you the\ncontainer image was signed using our private key."}),"\n",(0,i.jsx)(n.h2,{id:"verify-the-container-source-code-bill-of-materials-sboms",children:"Verify the container Source Code Bill Of Materials (SBOMs)"}),"\n",(0,i.jsxs)(n.p,{children:["A Software Bill of Materials (SBOM) is a detailed list of all the components of a software.\nIt facilitates security and license compliance assessments.\nThe Inspektor Gadget project publishes SBOMs in the ",(0,i.jsx)(n.a,{href:"https://cyclonedx.org/specification/overview/",children:"CycloneDX format"})," for all our container images and CLI tools:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["SBOMs for container images are attached to the corresponding image and can be found in our ",(0,i.jsx)(n.a,{href:"https://github.com/orgs/inspektor-gadget/packages",children:"registry"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["SBOMs for CLI tools are available as ",(0,i.jsx)(n.a,{href:"https://github.com/inspektor-gadget/inspektor-gadget/releases",children:"release"})," assets."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["In this section, we will see how you can verify and inspect the SBOMs attached to our container images.\nTo do so, you will need ",(0,i.jsx)(n.code,{children:"cosign"}),", ",(0,i.jsx)(n.a,{href:"https://oras.land/docs/installation",children:(0,i.jsx)(n.code,{children:"oras"})})," and Inspektor Gadget public key."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'# We will demo this for amd64, but it works the same for arm64.\n$ arch=amd64\n$ oras discover --platform linux/${arch} --artifact-type example/sbom ghcr.io/inspektor-gadget/inspektor-gadget:latest\nDiscovered 1 artifact referencing latest\nDigest: sha256:...\n\nArtifact Type Digest\nexample/sbom sha256:hash_of_sbom_manifest\n# SBOMs are signed individually per platform.\n# Let\'s check the SBOM is signed with our private key:\n$ cosign verify --key inspektor-gadget.pub ghcr.io/inspektor-gadget/inspektor-gadget@sha256:hash_of_sbom_manifest\n\nVerification for ghcr.io/inspektor-gadget/inspektor-gadget@sha256:hash_of_sbom_manifest\nThe following checks were performed on each of these signatures:\n - The cosign claims were validated\n - Existence of the claims in the transparency log was verified offline\n - The signatures were verified against the specified public key\n\n[{"critical":{"identity":{"docker-reference":"ghcr.io/inspektor-gadget/inspektor-gadget"}, ...\n]\n# Let\'s download the SBOM and look at it:\n$ oras pull --allow-path-traversal ghcr.io/inspektor-gadget/inspektor-gadget@sha256:hash_of_sbom_manifest\nDownloading 1d479bb51392 /tmp/gadget-container-image-linux-amd64/sbom_cyclonedx.json\nDownloaded 1d479bb51392 /tmp/gadget-container-image-linux-amd64/sbom_cyclonedx.json\nPulled [registry] ghcr.io/eiffel-fl/inspektor-gadget@sha256:hash_of_sbom_manifest\nDigest: sha256:hash_of_sbom_manifest\n$ jq \'\' /tmp/gadget-container-image-linux-amd64/sbom_cyclonedx.json\n{\n "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",\n "bomFormat": "CycloneDX",\n "specVersion": "1.5",\n "serialNumber": "urn:uuid:cf132c3d-5960-4536-9c03-9724babd76e9",\n "version": 1,\n "metadata": {\n "timestamp": "2024-03-21T17:22:38Z",\n "tools": {\n "components": [\n {\n "type": "application",\n "author": "anchore",\n "name": "syft",\n "version": "1.0.1"\n }\n ]\n },\n "component": {\n "bom-ref": "af63bd4c8601b7f1",\n "type": "file",\n "name": "."\n }\n },\n "components": [\n {\n "bom-ref": "pkg:deb/debian/[email protected]+deb12u5?arch=amd64&distro=debian-12&package-id=854ccee33785ad46",\n "type": "library",\n "publisher": "Santiago Vila <[email protected]>",\n "name": "base-files",\n...\n}\n'})}),"\n",(0,i.jsx)(n.p,{children:"As the SBOM was signed with our private key, you can now inspect it to track down every depen
1dencies we use to build our container image."}),"\n",(0,i.jsx)(n.h2,{id:"verify-the-gadget-builder-image",children:"Verify the gadget-builder image"}),"\n",(0,i.jsxs)(n.p,{children:["We also sign the ",(0,i.jsx)(n.code,{children:"gadget-builder"})," image which is used to build gadgets.\nYou can verify it using the following command:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"$ cosign verify --key inspektor-gadget.pub ghcr.io/inspektor-gadget/gadget-builder:v0.54.1\n"})}),"\n",(0,i.jsxs)(n.p,{children:["We highly recommend you to verify by digest and specify the digest when building.\nThis helps to protect against ",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use",children:"TOCTOU"})," issue, as the image tag that got verified could have been manipulated to point to another malicious image after the check."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'$ DIGEST=\'sha256:4967ad1a11a9ed32867dc0d63e137dc2196c560db4b3644c7c0c97fccea7c522\'\n$ cosign verify --key inspektor-gadget.pub ghcr.io/inspektor-gadget/gadget-builder@$DIGEST\n\nVerification for ghcr.io/inspektor-gadget/gadget-builder@sha256:4967ad1a11a9ed32867dc0d63e137dc2196c560db4b3644c7c0c97fccea7c522 --\nThe following checks were performed on each of these signatures:\n - The cosign claims were validated\n - Existence of the claims in the transparency log was verified offline\n - The signatures were verified against the specified public key\n\n[{"critical":{"identity":{"docker-reference":"ghcr.io/inspektor-gadget/gadget-builder"}, ...\n]\n$ sudo ig image build -t trace_exec:build-verified -f gadgets/trace_exec/gadget.yaml --builder-image ghcr.io/inspektor-gadget/gadget-builder@$DIGEST gadgets/trace_exec\nPulling builder image ghcr.io/inspektor-gadget/gadget-builder@sha256:4967ad1a11a9ed32867dc0d63e137dc2196c560db4b3644c7c0c97fccea7c522\nghcr.io/inspektor-gadget/gadget-builder@sha256:4967ad1a11a9ed32867dc0d63e137dc2196c560db4b3644c7c0c97fccea7c522: Pulling from inspektor-gadget/gadget-builder\n...\nDigest: sha256:4967ad1a11a9ed32867dc0d63e137dc2196c560db4b3644c7c0c97fccea7c522\nStatus: Downloaded newer image for ghcr.io/inspektor-gadget/gadget-builder@sha256:4967ad1a11a9ed32867dc0d63e137dc2196c560db4b3644c7c0c97fccea7c522\nSuccessfully built ghcr.io/inspektor-gadget/gadget/trace_exec:build-verified@sha256:c85eac1e4615a08c74883402f73c5f8667b64597bd51f9e46275dfdbb0a04703\n'})}),"\n",(0,i.jsx)(n.h2,{id:"verify-an-asset",children:"Verify an asset"}),"\n",(0,i.jsx)(n.p,{children:"Rather than signing all the assets, we only sign the checksums file.\nSo, by verifying this file, you can then verify the assets themselves by\nchecking their checksums."}),"\n",(0,i.jsx)(n.h3,{id:"verifying-the-checksums-file",children:"Verifying the checksums file"}),"\n",(0,i.jsx)(n.p,{children:"The following snippet show you how to verify the checksums file:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'$ RELEASE=\'v0.19.0\'\n$ ASSET="SHA256SUMS"\n$ URL="https://github.com/inspektor-gadget/inspektor-gadget/releases/download/${RELEASE}"\n# We need to get the asset itself, its signature file and the corresponding bundle:\n$ for i in $URL/$ASSET $URL/$ASSET.sig $URL/$ASSET.bundle; do\n\twget $i\ndone\n...\n# We need to get the public key too.\n$ wget $URL/inspektor-gadget.pub\n...\n$ cosign verify-blob $ASSET --bundle ${ASSET}.bundle --signature ${ASSET}.sig --key inspektor-gadget.pub --offline\nVerified OK\n'})}),"\n",(0,i.jsx)(n.p,{children:"As you can see, the checksum file was correctly verified which means this file was indeed signed by us.\nSo, you can use this file to verify other release assets."}),"\n",(0,i.jsx)(n.h3,{id:"verify-an-asset-1",children:"Verify an asset"}),"\n",(0,i.jsx)(n.p,{children:"Once you verified the checksums file, you can now verify the integrity of an asset using such checksums file:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'$ RELEASE=\'v0.19.0\'\n$ ASSET="inspektor-gadget-${RELEASE}.yaml"\n$ URL="https://github.com/inspektor-gadget/inspektor-gadget/releases/download/${RELEASE}"\n$ wget $URL/$ASSET\n$ grep $ASSET SHA256SUMS | shasum -a 256 -c -s || echo "Error: ${ASSET} didn\'t pass the checksum verification. You must not use it!"\n'})})]})}function u(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},7227:(e,n,t)=>{t.d(n,{A:()=>r});t(96540);var a=t(34164);const i={tabItem:"tabItem_Ymn6"};var s=t(74848);function r(e){let{children:n,hidden:t,className:r}=e;return(0,s.jsx)("div",{role:"tabpanel",className:(0,a.A)(i.tabItem,r),hidden:t,children:n})}},49489:(e,n,t)=>{t.d(n,{A:()=>x});var a=t(96540),i=t(34164),s=t(24245),r=t(56347),o=t(36494),c=t(62814),l=t(45167),d=t(69900);function u(e){return a.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,a.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs>
1 child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function g(e){const{values:n,children:t}=e;return(0,a.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:a,default:i}}=e;return{value:n,label:t,attributes:a,default:i}}))}(t);return function(e){const n=(0,l.XI)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e}),[n,t])}function h(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function f(e){let{queryString:n=!1,groupId:t}=e;const i=(0,r.W6)(),s=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,c.aZ)(s),(0,a.useCallback)((e=>{if(!s)return;const n=new URLSearchParams(i.location.search);n.set(s,e),i.replace({...i.location,search:n.toString()})}),[s,i])]}function p(e){const{defaultValue:n,queryString:t=!1,groupId:i}=e,s=g(e),[r,c]=(0,a.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(n){if(!h({value:n,tabValues:t}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const a=t.find((e=>e.default))??t[0];if(!a)throw new Error("Unexpected error: 0 tabValues");return a.value}({defaultValue:n,tabValues:s}))),[l,u]=f({queryString:t,groupId:i}),[p,m]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[i,s]=(0,d.Dv)(t);return[i,(0,a.useCallback)((e=>{t&&s.set(e)}),[t,s])]}({groupId:i}),b=(()=>{const e=l??p;return h({value:e,tabValues:s})?e:null})();(0,o.A)((()=>{b&&c(b)}),[b]);return{selectedValue:r,selectValue:(0,a.useCallback)((e=>{if(!h({value:e,tabValues:s}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),m(e)}),[u,m,s]),tabValues:s}}var m=t(11062);const b={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var y=t(74848);function v(e){let{className:n,block:t,selectedValue:a,selectValue:r,tabValues:o}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,s.a_)(),d=e=>{const n=e.currentTarget,t=c.indexOf(n),i=o[t].value;i!==a&&(l(n),r(i))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,y.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.A)("tabs",{"tabs--block":t},n),children:o.map((e=>{let{value:n,label:t,attributes:s}=e;return(0,y.jsx)("li",{role:"tab",tabIndex:a===n?0:-1,"aria-selected":a===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...s,className:(0,i.A)("tabs__item",b.tabItem,s?.className,{"tabs__item--active":a===n}),children:t??n},n)}))})}function k(e){let{lazy:n,children:t,selectedValue:s}=e;const r=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===s));return e?(0,a.cloneElement)(e,{className:(0,i.A)("margin-top--md",e.props.className)}):null}return(0,y.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,a.cloneElement)(e,{key:n,hidden:e.props.value!==s})))})}function w(e){const n=p(e);return(0,y.jsxs)("div",{className:(0,i.A)("tabs-container",b.tabList),children:[(0,y.jsx)(v,{...n,...e}),(0,y.jsx)(k,{...n,...e})]})}function x(e){const n=(0,m.A)();return(0,y.jsx)(w,{...e,children:u(e.children)},String(n))}},28453:(e,n,t)=>{t.d(n,{R:()=>r,x:()=>o});var a=t(96540);const i={},s=a.createContext(i);function r(e){const n=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),a.createElement(s.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.