Pular para o conteúdo principal

Signed downloads

Everything load.sh downloads from shellscript.download is signed, and the loader uses a file only when its signature is the one of the ByJG key. A site that was changed by someone else, or a file that changed on its way, is refused, and the loader keeps what it had.

What is signed​

Every file the loader downloads from the site, each with its signature next to it as <file>.sig:

FileDownloaded by
scripts/<script>.shload.sh <script>, load.sh --update
scripts/lib/<file>.sh, the code a script shares with othersload.sh, with the script that needs it
scripts/load.sh, the loader itselfthe installer, load.sh --update
install/load-completion.shthe installer, load.sh --completion, load.sh --update
install/loader, the installeryou, when you check it by hand (below)

The signatures are detached GPG signatures. They are made when the site is deployed, and exist only on the site: they are not in the git repository.

The key​

It is the key that also signs the ByJG APT and RPM repositories:

  • ByJG Opensource <info@byjg.com.br>
  • Fingerprint DB84 6CC3 C109 29D3 43B6 B6DC 4BCC 9780 596C 4BCF
  • Published at https://packages.byjg.com/byjg.gpg (binary) and byjg.asc (armored)

The key is written inside load.sh itself, so an installed loader never downloads it: whoever changes the files on shellscript.download cannot change the key with them, and what they put there would not be signed by it. The private key is never on the site: only the deploy job has it.

The installer, and you when you check by hand, download the key from packages.byjg.com. That is served by Cloudflare, as shellscript.download is, so what makes a downloaded key the right one is its fingerprint, above, not where it came from.

How the loader checks​

load.sh downloads the file and its .sig next to where the file goes, under a temporary name, and runs gpgv with the key written in load.sh:

  • the signature is the one of the key: the file is moved into place;
  • it is not, there is no .sig, or a download fails: the file is not used, and what was there before stays as it was.

The installer (install/loader) does the same for load.sh and its completion, with the key it downloads from https://packages.byjg.com/byjg.gpg. It checks that gpgv is there before it changes anything on the machine.

Nothing is checked, and nothing is downloaded, for a script already in the cache ($HOME/.shellscript/downloads), or with --developer, which runs a local folder. A cached script was checked when it was downloaded, and is not checked again: load.sh --update downloads everything again, and checks it.

gpgv​

Checking needs gpgv. Ubuntu and Debian have it, as apt uses it. Otherwise:

sudo dnf install gnupg2 # Fedora
sudo apk add gpgv # Alpine

The first install​

The one-line install runs what the site serves:

/bin/bash -c "$(curl -fsSL https://shellscript.download/install/loader)"

The installer then checks what it downloads, but nothing checked the installer itself. To check it first, with the key from packages.byjg.com:

curl -fsSLO https://shellscript.download/install/loader
curl -fsSLO https://shellscript.download/install/loader.sig
curl -fsSL https://packages.byjg.com/byjg.gpg -o byjg.gpg
gpg --show-keys byjg.gpg # the fingerprint must be DB84 6CC3 C109 29D3 43B6 B6DC 4BCC 9780 596C 4BCF
gpgv --keyring ./byjg.gpg loader.sig loader && sh loader

From then on, every download is checked against the key written in load.sh.

Any file of the site can be checked the same way:

curl -fsSLO https://shellscript.download/scripts/jq.sh
curl -fsSLO https://shellscript.download/scripts/jq.sh.sig
gpgv --keyring ./byjg.gpg jq.sh.sig jq.sh

When a check fails​

MessageWhat it meansWhat to do
gpgv is needed to check the signature of what comes from shellscript.downloadgpgv is not installedinstall it (above)
<url> is not signed by the key of shellscript.download: not usedthe file is not the one that was signed: changed on the site or on its waydo not work around it: try again later, and if it stays, open an issue
Failed to download <url>the file or its .sig could not be downloaded: no network, or no such scriptcheck the name with load.sh --list, and the network
Failed to download <url>, or it is not signed by https://packages.byjg.com/byjg.gpgthe installer could not download, or check, load.sh or its completionas the two lines above
lib/<file>.sh, which this script needs, is missing: run 'load.sh --update'the loader that downloaded the script is older than signed downloadsload.sh --update, which replaces the loader first

In each case the loader stops with exit code 3, and what was installed before is left as it was.

What is not signed​

  • list.json, the catalog that load.sh --list shows: it is only displayed.
  • What the scripts download from the vendor of a tool (the jq binary, a JDK, Docker packages). That comes from the vendor, over HTTPS, and is not checked against this key.

When the key changes​

A loader knows the key written in it, and refuses what is signed by another one. After the key is replaced, the site is signed with the new key, and an installed loader refuses its updates. Install the loader again, with the one-line install (or checking it first, as above): the installer takes the new key from packages.byjg.com, and the new load.sh carries it.

For contributors​

  • A script names the shared file it needs in its SHARED_LIB line, and load.sh downloads and checks it: see Contributing.
  • Everything under public/scripts/ and public/install/ is signed by the deploy job (scripts/sign-dist.sh), which runs on main after the end-to-end tests. Nothing is signed by hand, and no .sig goes into the repository.
  • The end-to-end test of the loader (tests_e2e/testcases/load.sh) serves a copy of the site signed by a key of the test, and checks what is refused: a file changed after it was signed, one with no signature, one signed by another key.