ActivityPub Viewer

A small tool to view real-world ActivityPub objects as JSON! Enter a URL or username from Mastodon or a similar service below, and we'll send a request with the right Accept header to the server to view the underlying object.

Open in browser →
{ "@context": [ "https://join-lemmy.org/context.json", "https://www.w3.org/ns/activitystreams" ], "type": "Page", "id": "https://monero.town/post/4199546", "attributedTo": "https://monero.town/u/maltfield", "to": [ "https://linux.community/c/linux", "https://www.w3.org/ns/activitystreams#Public" ], "name": "How to wget/curl files from OCI registries (docker, github packages)", "cc": [], "content": "<p>This article will describe <a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget\" rel=\"nofollow\">how to download an image from a (docker) container registry</a>.</p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center\"><a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget\" rel=\"nofollow\"><img src=\"https://tech.michaelaltfield.net/wp-content/uploads/sites/5/container-download-curl-wget_featuredImage1.jpg\" alt=\"Manual Download of Container Images with wget and curl\" /></a></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:center\">Manual <a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget\" rel=\"nofollow\">Download of Container Images</a> with wget and curl</td>\n</tr>\n</tbody>\n</table>\n<h1>Intro</h1>\n<p>Remember the good `'ol days when you could just download software by visiting a website and click “download”?</p>\n<p>Even <code>apt</code> and <code>yum</code> repositories were just simple HTTP servers that you could just <code>curl</code> (or <code>wget</code>) from. Using the package manager was, of course, more secure and convenient – but you could always just download packages manually, if you wanted.</p>\n<p>But <strong>have you ever tried to <code>curl</code> an image from a container registry</strong>, such as docker? Well friends, I have tried. And I have the <a href=\"https://github.com/BusKill/buskill-app/issues/78#issuecomment-1987374445\" rel=\"nofollow\">scars</a> to prove it.</p>\n<p>It was a remarkably complex process that took me weeks to figure-out. Lucky you, this article will break it down.</p>\n<h2>Examples</h2>\n<p>Specifically, we’ll look at how to download files from two OCI registries.</p>\n<ol>\n<li><a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#docker-hub\" rel=\"nofollow\">Docker Hub</a></li>\n<li><a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#github-packages\" rel=\"nofollow\">GitHub Packages</a></li>\n</ol>\n<h2>Terms</h2>\n<p>First, here’s some terminology used by OCI</p>\n<ol>\n<li>OCI - <a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#what-oci\" rel=\"nofollow\">Open Container Initiative</a></li>\n<li>blob - A “blob” in the OCI spec just means a file</li>\n<li>manifest - A “manifest” in the OCI spec means a list of files</li>\n</ol>\n<h2>Prerequisites</h2>\n<p>This guide was written in 2024, and it uses the following software and versions:</p>\n<ol>\n<li>debian 12 (bookworm)</li>\n<li>curl 7.88.1</li>\n<li>OCI Distribution Spec v1.1.0 (which, unintuitively, uses the ‘<a href=\"https://github.com/distribution/distribution/blob/5e75227fb213162564bab74b146300ffed9f0bbd/docs/content/spec/api.md\" rel=\"nofollow\">/v2/</a>’ endpoint)</li>\n</ol>\n<p>Of course, you’ll need ‘<code>curl</code>’ installed. And, to parse json, ‘<code>jq</code>’ too.</p>\n<pre style=\"background-color:#ffffff;\">\n<span style=\"color:#323232;\">sudo apt-get install curl jq\n</span></pre>\n<h2>What is OCI?</h2>\n<p>OCI stands for Open Container Initiative.</p>\n<p>OCI was <a href=\"https://opencontainers.org/about/overview/\" rel=\"nofollow\">originally formed</a> in June 2015 for Docker and CoreOS. Today it’s a wider, general-purpose (and annoyingly complex) way that many projects host files (that are extremely non-trivial to download).</p>\n<p>One does not simply download a file from an OCI-complianet container registry. You must:</p>\n<ol>\n<li>Generate an authentication token for the API</li>\n<li>Make an API call to the registry, requesting to download a JSON &quot;Manifest&quot;</li>\n<li>Parse the JSON Manifest to figure out the hash of the file that you want</li>\n<li>Determine the download URL from the hash</li>\n<li>Download the file (which might actually be many distinct file “layers”)</li>\n</ol>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center\"><a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget\" rel=\"nofollow\"><img src=\"https://tech.michaelaltfield.net/wp-content/uploads/sites/5/container-download-curl-wget_one-does-not-simply1.jpg\" alt=\"One does not simply download from a container registry\" /></a></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:center\">One does not simply <a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget\" rel=\"nofollow\">download from a container registry</a></td>\n</tr>\n</tbody>\n</table>\n<p>In order to figure out how to make an API call to the registry, you must first read (and understand) the OCI specs <a href=\"https://opencontainers.org/release-notices/overview/\" rel=\"nofollow\">here</a>.</p>\n<ul>\n<li><a href=\"https://opencontainers.org/release-notices/overview/\">opencontainers.org/release-notices/overview/</a></li>\n</ul>\n<h2>OCI APIs</h2>\n<p>OCI maintains three distinct specifications:</p>\n<ol>\n<li>image spec</li>\n<li>runtime spec</li>\n<li>distribution spec</li>\n</ol>\n<h3>OCI “Distribution Spec” API</h3>\n<p>To figure out how to download a file from a container registry, we’re interested in the “distribution spec”. At the time of writing, the latest “distribution spec” can be downloaded <a href=\"https://github.com/opencontainers/distribution-spec/releases/download/v1.1.0/oci-distribution-spec-v1.1.0.pdf\" rel=\"nofollow\">here</a>:</p>\n<ul>\n<li><a href=\"https://github.com/opencontainers/distribution-spec/releases/tag/v1.1.0\">github.com/opencontainers/…/v1.1.0</a></li>\n<li><a href=\"https://github.com/opencontainers/distribution-spec/releases/download/v1.1.0/oci-distribution-spec-v1.1.0.pdf\">github.com/…/oci-distribution-spec-v1.1.0.pdf</a></li>\n</ul>\n<p>The above PDF file defines a set of API endpoints that we can use to query, parse, and then figure out how to download a file from a container registry. The table from the above PDF is copied below:</p>\n<table>\n<thead>\n<tr>\n<th>ID</th>\n<th>Method</th>\n<th>API Endpoint</th>\n<th>Success</th>\n<th>Failure</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>end-1</td>\n<td><code>GET</code></td>\n<td><code>/v2/</code></td>\n<td><code>200</code></td>\n<td><code>404</code>/<code>401</code></td>\n</tr>\n<tr>\n<td>end-2</td>\n<td><code>GET</code> / <code>HEAD</code></td>\n<td><code>/v2/&lt;name&gt;/blobs/&lt;digest&gt;</code></td>\n<td><code>200</code></td>\n<td><code>404</code></td>\n</tr>\n<tr>\n<td>end-3</td>\n<td><code>GET</code> / <code>HEAD</code></td>\n<td><code>/v2/&lt;name&gt;/manifests/&lt;reference&gt;</code></td>\n<td><code>200</code></td>\n<td><code>404</code></td>\n</tr>\n<tr>\n<td>end-4a</td>\n<td><code>POST</code></td>\n<td><code>/v2/&lt;name&gt;/blobs/uploads/</code></td>\n<td><code>202</code></td>\n<td><code>404</code></td>\n</tr>\n<tr>\n<td>end-4b</td>\n<td><code>POST</code></td>\n<td><code>/v2/&lt;name&gt;/blobs/uploads/?digest=&lt;digest&gt;</code></td>\n<td><code>201</code>/<code>202</code></td>\n<td><code>404</code>/<code>400</code></td>\n</tr>\n<tr>\n<td>end-5</td>\n<td><code>PATCH</code></td>\n<td><code>/v2/&lt;name&gt;/blobs/uploads/&lt;reference&gt;</code></td>\n<td><code>202</code></td>\n<td><code>404</code>/<code>416</code></td>\n</tr>\n<tr>\n<td>end-6</td>\n<td><code>PUT</code></td>\n<td><code>/v2/&lt;name&gt;/blobs/uploads/&lt;reference&gt;?digest=&lt;digest&gt;</code></td>\n<td><code>201</code></td>\n<td><code>404</code>/<code>400</code></td>\n</tr>\n<tr>\n<td>end-7</td>\n<td><code>PUT</code></td>\n<td><code>/v2/&lt;name&gt;/manifests/&lt;reference&gt;</code></td>\n<td><code>201</code></td>\n<td><code>404</code></td>\n</tr>\n<tr>\n<td>end-8a</td>\n<td><code>GET</code></td>\n<td><code>/v2/&lt;name&gt;/tags/list</code></td>\n<td><code>200</code></td>\n<td><code>404</code></td>\n</tr>\n<tr>\n<td>end-8b</td>\n<td><code>GET</code></td>\n<td><code>/v2/&lt;name&gt;/tags/list?n=&lt;integer&gt;&amp;last=&lt;integer&gt;</code></td>\n<td><code>200</code></td>\n<td><code>404</code></td>\n</tr>\n<tr>\n<td>end-9</td>\n<td><code>DELETE</code></td>\n<td><code>/v2/&lt;name&gt;/manifests/&lt;reference&gt;</code></td>\n<td><code>202</code></td>\n<td><code>404</code>/<code>400</code>/<code>405</code></td>\n</tr>\n<tr>\n<td>end-10</td>\n<td><code>DELETE</code></td>\n<td><code>/v2/&lt;name&gt;/blobs/&lt;digest&gt;</code></td>\n<td><code>202</code></td>\n<td><code>404</code>/<code>405</code></td>\n</tr>\n<tr>\n<td>end-11</td>\n<td><code>POST</code></td>\n<td><code>/v2/&lt;name&gt;/blobs/uploads/?mount=&lt;digest&gt;&amp;from=&lt;other_name&gt;</code></td>\n<td><code>201</code></td>\n<td><code>404</code></td>\n</tr>\n<tr>\n<td>end-12a</td>\n<td><code>GET</code></td>\n<td><code>/v2/&lt;name&gt;/referrers/&lt;digest&gt;</code></td>\n<td><code>200</code></td>\n<td><code>404</code>/<code>400</code></td>\n</tr>\n<tr>\n<td>end-12b</td>\n<td><code>GET</code></td>\n<td><code>/v2/&lt;name&gt;/referrers/&lt;digest&gt;?artifactType=&lt;artifactType&gt;</code></td>\n<td><code>200</code></td>\n<td><code>404</code>/<code>400</code></td>\n</tr>\n<tr>\n<td>end-13</td>\n<td><code>GET</code></td>\n<td><code>/v2/&lt;name&gt;/blobs/uploads/&lt;reference&gt;</code></td>\n<td><code>204</code></td>\n<td><code>404</code></td>\n</tr>\n</tbody>\n</table>\n<p>In OCI, files are (cryptically) called “<code>blobs</code>”. In order to figure out the file that we want to download, we must first reference the list of files (called a “<code>manifest</code>”).</p>\n<p>The above table shows us how we can download a list of files (manifest) and then download the actual file (blob).</p>\n<h1>Examples</h1>\n<p>Let’s look at how to download files from a couple different OCI registries:</p>\n<ol>\n<li><a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#docker-hub\" rel=\"nofollow\">Docker Hub</a></li>\n<li><a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#github-packages\" rel=\"nofollow\">GitHub Packages</a></li>\n</ol>\n<h2>Docker Hub</h2>\n<p>To see the full example of downloading images from docker hub, <a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#docker-hub\" rel=\"nofollow\">click here</a></p>\n<h2>GitHub Packages</h2>\n<p>To see the full example of downloading files from GitHub Packages, <a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#github-packages\" rel=\"nofollow\">click here</a>.</p>\n<h1>Why?</h1>\n<p>I wrote this article because many, many folks have inquired about how to manually download files from OCI registries on the Internet, but their simple queries are usually returned with a barrage of useless counter-questions: why the heck would you want to do that!?!</p>\n<p>The answer is varied.</p>\n<p>Some people need to get files onto a restricted environment. Either their org doesn’t grant them permission to install software on the machine, or the system has firewall-restricted internet access – or doesn’t have internet access at all.</p>\n<h2>3TOFU</h2>\n<p>Personally, the reason that I wanted to be able to download files from an OCI registry was for <a href=\"https://tech.michaelaltfield.net/2024/08/04/3tofu/\" rel=\"nofollow\">3TOFU</a>.</p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center\"><a href=\"https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget\" rel=\"nofollow\"><img src=\"https://tech.michaelaltfield.net/wp-content/uploads/sites/5/3tofu_featuredImage.jpg\" alt=\"Verifying Unsigned Releases with 3TOFU\" /></a></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:center\">Verifying Unsigned Releases with <a href=\"https://tech.michaelaltfield.net/2024/08/04/3tofu/\" rel=\"nofollow\">3TOFU</a></td>\n</tr>\n</tbody>\n</table>\n<p>Unfortunaetly, most apps using OCI registries are <em>extremely</em> insecure. Docker, for example, will happily download malicious images. By default, <a href=\"https://security.stackexchange.com/questions/238916/how-to-pin-public-root-key-when-downloading-an-image-with-docker-pull-docker-co?noredirect=1&amp;lq=1\" rel=\"nofollow\">it doesn’t do <em>any</em> authenticity verifications</a> on the payloads it downloaded. Even if you manually enable DCT, there’s loads of <a href=\"https://github.com/docker/cli/issues/2752\" rel=\"nofollow\">pending issues</a> with it.</p>\n<p>Likewise, the macOS package manager <a href=\"https://brew.sh/\" rel=\"nofollow\">brew</a> has this same problem: it will happily download and install malicious code, because it doesn’t use cryptography to verify the authenticity of anything that it downloads. This introduces <a href=\"https://en.wikipedia.org/wiki/Watering_hole_attack\" rel=\"nofollow\">watering hole vulnerabilities</a> when developers use brew to install dependencies in their CI pipelines.</p>\n<p>My solution to this? <a href=\"https://tech.michaelaltfield.net/2024/08/04/3tofu/\" rel=\"nofollow\">3TOFU</a>. And that requires me to be able to download the file (for verification) on three distinct linux VMs using curl or wget.</p>\n<blockquote>\n<p>⚠ NOTE: 3TOFU is an approach to harm reduction.</p>\n<p>It is not wise to download and run binaries or code whose authenticity you cannot verify using a cryptographic signature from a key stored offline. However, sometimes we cannot avoid it. If you’re going to proceed with running untrusted code, then following a <a href=\"https://tech.michaelaltfield.net/2024/08/04/3tofu/\" rel=\"nofollow\">3TOFU procedure</a> may reduce your risk, but it’s better to avoid running unauthenticated code if at all possible.</p>\n</blockquote>\n<h2>Registry (ab)use</h2>\n<p>Container registries were created in 2013 to provide a clever &amp; complex solution to a problem: how to package and serve multiple versions of simplified sources to various consumers spanning multiple operating systems and architectures – while also packaging them into small, discrete “layers”.</p>\n<p>However, if your project is just serving simple files, then the only thing gained by uploading them to a complex system like a container registry is headaches. Why do developers do this?</p>\n<p>In the case of brew, their free hosing provider (JFrog’s Bintray) <a href=\"https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/\" rel=\"nofollow\">shutdown in 2021</a>. Brew was already hosting their code on GitHub, so I guess someone looked at “GitHub Packages” and <a href=\"https://github.com/orgs/Homebrew/discussions/691\" rel=\"nofollow\">figured it was</a> a good (read: free) replacement.</p>\n<p>Many developers using Container Registries don’t need the complexity, but – well – they’re just using it as a free place for their FOSS project to store some files, man.</p>\n", "mediaType": "text/html", "source": { "content": "This article will describe [how to download an image from a (docker) container registry](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget).\n\n| [![Manual Download of Container Images with wget and curl](https://tech.michaelaltfield.net/wp-content/uploads/sites/5/container-download-curl-wget_featuredImage1.jpg)](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget) |\n|:--:| \n| Manual [Download of Container Images](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget) with wget and curl |\n\n# Intro\n\nRemember the good `'ol days when you could just download software by visiting a website and click \"download\"?\n\nEven `apt` and `yum` repositories were just simple HTTP servers that you could just `curl` (or `wget`) from. Using the package manager was, of course, more secure and convenient -- but you could always just download packages manually, if you wanted.\n\nBut **have you ever tried to `curl` an image from a container registry**, such as docker? Well friends, I have tried. And I have the [scars](https://github.com/BusKill/buskill-app/issues/78#issuecomment-1987374445) to prove it.\n\nIt was a remarkably complex process that took me weeks to figure-out. Lucky you, this article will break it down.\n\n## Examples\n\nSpecifically, we'll look at how to download files from two OCI registries.\n\n1. [Docker Hub](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#docker-hub)\n2. [GitHub Packages](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#github-packages)\n\n## Terms\n\nFirst, here's some terminology used by OCI\n\n1. OCI - [Open Container Initiative](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#what-oci)\n2. blob - A \"blob\" in the OCI spec just means a file\n3. manifest - A \"manifest\" in the OCI spec means a list of files\n\n## Prerequisites\n\nThis guide was written in 2024, and it uses the following software and versions:\n\n1. debian 12 (bookworm)\n2. curl 7.88.1\n3. OCI Distribution Spec v1.1.0 (which, unintuitively, uses the '[/v2/](https://github.com/distribution/distribution/blob/5e75227fb213162564bab74b146300ffed9f0bbd/docs/content/spec/api.md)' endpoint)\n\nOf course, you'll need '`curl`' installed. And, to parse json, '`jq`' too.\n\n```\nsudo apt-get install curl jq\n```\n\n## What is OCI?\n\nOCI stands for Open Container Initiative.\n\nOCI was [originally formed](https://opencontainers.org/about/overview/) in June 2015 for Docker and CoreOS. Today it's a wider, general-purpose (and annoyingly complex) way that many projects host files (that are extremely non-trivial to download).\n\nOne does not simply download a file from an OCI-complianet container registry. You must:\n\n1. Generate an authentication token for the API\n2. Make an API call to the registry, requesting to download a JSON \"Manifest\"\n3. Parse the JSON Manifest to figure out the hash of the file that you want\n4. Determine the download URL from the hash\n5. Download the file (which might actually be many distinct file \"layers\")\n\n| [![One does not simply download from a container registry](https://tech.michaelaltfield.net/wp-content/uploads/sites/5/container-download-curl-wget_one-does-not-simply1.jpg)](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget) |\n|:--:| \n| One does not simply [download from a container registry](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget) |\n\nIn order to figure out how to make an API call to the registry, you must first read (and understand) the OCI specs [here](https://opencontainers.org/release-notices/overview/).\n\n- <https://opencontainers.org/release-notices/overview/>\n\n## OCI APIs\n\nOCI maintains three distinct specifications:\n\n1. image spec\n2. runtime spec\n3. distribution spec\n\n### OCI \"Distribution Spec\" API\n\nTo figure out how to download a file from a container registry, we're interested in the \"distribution spec\". At the time of writing, the latest \"distribution spec\" can be downloaded [here](https://github.com/opencontainers/distribution-spec/releases/download/v1.1.0/oci-distribution-spec-v1.1.0.pdf):\n\n- <https://github.com/opencontainers/distribution-spec/releases/tag/v1.1.0>\n- <https://github.com/opencontainers/distribution-spec/releases/download/v1.1.0/oci-distribution-spec-v1.1.0.pdf>\n\nThe above PDF file defines a set of API endpoints that we can use to query, parse, and then figure out how to download a file from a container registry. The table from the above PDF is copied below:\n\n| ID | Method | API Endpoint | Success | Failure |\n|------|----------|------------------------------------|--------|-----------|\n| end-1 | `GET` | `/v2/` | `200` | `404`/`401` |\n| end-2 | `GET` / `HEAD` | `/v2/<name>/blobs/<digest>` | `200` | `404` |\n| end-3 | `GET` / `HEAD` | `/v2/<name>/manifests/<reference>` | `200` | `404` |\n| end-4a | `POST` | `/v2/<name>/blobs/uploads/` | `202` | `404` |\n| end-4b | `POST` | `/v2/<name>/blobs/uploads/?digest=<digest>` | `201`/`202` | `404`/`400` |\n| end-5 | `PATCH` | `/v2/<name>/blobs/uploads/<reference>` | `202` | `404`/`416` |\n| end-6 | `PUT` | `/v2/<name>/blobs/uploads/<reference>?digest=<digest>` | `201` | `404`/`400` |\n| end-7 | `PUT` | `/v2/<name>/manifests/<reference>` | `201` | `404` |\n| end-8a | `GET` | `/v2/<name>/tags/list` | `200` | `404` |\n| end-8b | `GET` | `/v2/<name>/tags/list?n=<integer>&last=<integer>` | `200` | `404` |\n| end-9 | `DELETE` | `/v2/<name>/manifests/<reference>` | `202` | `404`/`400`/`405` |\n| end-10 | `DELETE` | `/v2/<name>/blobs/<digest>` | `202` | `404`/`405` |\n| end-11 | `POST` | `/v2/<name>/blobs/uploads/?mount=<digest>&from=<other_name>` | `201` | `404` |\n| end-12a | `GET` | `/v2/<name>/referrers/<digest>` | `200` | `404`/`400` |\n| end-12b | `GET` | `/v2/<name>/referrers/<digest>?artifactType=<artifactType>` | `200` | `404`/`400` |\n| end-13 | `GET` | `/v2/<name>/blobs/uploads/<reference>` | `204` | `404` |\n\nIn OCI, files are (cryptically) called \"`blobs`\". In order to figure out the file that we want to download, we must first reference the list of files (called a \"`manifest`\").\n\nThe above table shows us how we can download a list of files (manifest) and then download the actual file (blob).\n\n# Examples\n\nLet's look at how to download files from a couple different OCI registries:\n\n1. [Docker Hub](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#docker-hub)\n2. [GitHub Packages](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#github-packages)\n\n## Docker Hub\n\nTo see the full example of downloading images from docker hub, [click here](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#docker-hub)\n\n## GitHub Packages\n\nTo see the full example of downloading files from GitHub Packages, [click here](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget#github-packages).\n\n# Why?\n\nI wrote this article because many, many folks have inquired about how to manually download files from OCI registries on the Internet, but their simple queries are usually returned with a barrage of useless counter-questions: why the heck would you want to do that!?!\n\nThe answer is varied.\n\nSome people need to get files onto a restricted environment. Either their org doesn't grant them permission to install software on the machine, or the system has firewall-restricted internet access -- or doesn't have internet access at all.\n\n## 3TOFU\n\nPersonally, the reason that I wanted to be able to download files from an OCI registry was for [3TOFU](https://tech.michaelaltfield.net/2024/08/04/3tofu/).\n\n| [![Verifying Unsigned Releases with 3TOFU](https://tech.michaelaltfield.net/wp-content/uploads/sites/5/3tofu_featuredImage.jpg)](https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget) |\n|:--:| \n| Verifying Unsigned Releases with [3TOFU](https://tech.michaelaltfield.net/2024/08/04/3tofu/) |\n\nUnfortunaetly, most apps using OCI registries are *extremely* insecure. Docker, for example, will happily download malicious images. By default, [it doesn't do *any* authenticity verifications](https://security.stackexchange.com/questions/238916/how-to-pin-public-root-key-when-downloading-an-image-with-docker-pull-docker-co?noredirect=1&lq=1) on the payloads it downloaded. Even if you manually enable DCT, there's loads of [pending issues](https://github.com/docker/cli/issues/2752) with it.\n\nLikewise, the macOS package manager [brew](https://brew.sh/) has this same problem: it will happily download and install malicious code, because it doesn't use cryptography to verify the authenticity of anything that it downloads. This introduces [watering hole vulnerabilities](https://en.wikipedia.org/wiki/Watering_hole_attack) when developers use brew to install dependencies in their CI pipelines.\n\nMy solution to this? [3TOFU](https://tech.michaelaltfield.net/2024/08/04/3tofu/). And that requires me to be able to download the file (for verification) on three distinct linux VMs using curl or wget.\n\n> ⚠ NOTE: 3TOFU is an approach to harm reduction.\n>\n> It is not wise to download and run binaries or code whose authenticity you cannot verify using a cryptographic signature from a key stored offline. However, sometimes we cannot avoid it. If you're going to proceed with running untrusted code, then following a [3TOFU procedure](https://tech.michaelaltfield.net/2024/08/04/3tofu/) may reduce your risk, but it's better to avoid running unauthenticated code if at all possible.\n\n## Registry (ab)use\n\nContainer registries were created in 2013 to provide a clever & complex solution to a problem: how to package and serve multiple versions of simplified sources to various consumers spanning multiple operating systems and architectures -- while also packaging them into small, discrete \"layers\".\n\nHowever, if your project is just serving simple files, then the only thing gained by uploading them to a complex system like a container registry is headaches. Why do developers do this?\n\nIn the case of brew, their free hosing provider (JFrog's Bintray) [shutdown in 2021](https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/). Brew was already hosting their code on GitHub, so I guess someone looked at \"GitHub Packages\" and [figured it was](https://github.com/orgs/Homebrew/discussions/691) a good (read: free) replacement.\n\nMany developers using Container Registries don't need the complexity, but -- well -- they're just using it as a free place for their FOSS project to store some files, man.\n", "mediaType": "text/markdown" }, "attachment": [ { "href": "https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget/", "mediaType": "text/html; charset=utf-8", "type": "Link" } ], "image": { "type": "Image", "url": "https://monero.town/pictrs/image/f5367313-5e87-4855-bf1b-dc6d073dbd12.jpeg" }, "sensitive": false, "published": "2024-09-03T18:02:52.223692Z", "language": { "identifier": "en", "name": "English" }, "audience": "https://linux.community/c/linux", "tag": [ { "href": "https://monero.town/post/4199546", "name": "#linux", "type": "Hashtag" } ] }