This is a minimal recipe to reproduce the problem:
$ mmdebstrap bookworm /dev/null --include=emacs-nox,ca-certificates,gnupg --customize-hook='chroot $1 emacs -Q -f package-list-packages'
Inside the emacs that starts, you'll see (after a couple of seconds):
Failed to verify signature archive-contents.sig:
No public key for 645357D2883A0966 created at 2024-06-10T21:10:04+0000 using EDDSA
Command output:
gpg: Signature made Mon Jun 10 21:10:04 2024 UTC
gpg: using EDDSA key 0327BE68D64D9A1A66859F15645357D2883A0966
gpg: Can't check signature: No public key
You will need a controlling terminal better than TERM=dumb.
If you don't have a terminal (e.g. you're a CI/CD script), this variant hook works:
bash5$ D12_shell --quiet --include=emacs-nox,ca-certificates,gnupg --customize-hook='chroot $1 emacs --batch -Q -f package-list-packages -f package--download-and-read-archives'
Contacting host: elpa.gnu.org:443
Contacting host: elpa.gnu.org:443
Failed to download ‘gnu’ archive.
Debugger entered--Lisp error: (bad-signature "archive-contents.sig")
signal(bad-signature ("archive-contents.sig"))
package--check-signature-content("\210u\4\0\26\10\0\35\26!\4\3'\276h\326M\232\32f\205\237\25dSW\322\210:\11f\5\2fgk\254\0\n\11\20dSW\322\210:\11f\346..." "(1\n (adoc-mode .\n\11 [(0 7 0)\n\11 ((emacs\n\11 ..." "archive-contents.sig")
#f(compiled-function () #<bytecode -0xe41cb77d185892b>)()
#f(compiled-function (status) #<bytecode 0xf5987e8eb8f468e>)((:peer (:certificates ((:version 3 :serial-number "04:dd:78:56:c7:1c:b9:a5:4d:6b:b6:ee:e1:5b:8f:6a:4b..." :issuer "C=US,O=Let's Encrypt,CN=R3" :valid-from "2024-06-03" :valid-to "2024-09-01" :subject "CN=elpa.nongnu.org" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:8b:16:cb:f9:32:f1:7d:1f:ec:8a:14:d2:91:ff:3f:..." :certificate-id "sha1:0c:09:41:11:91:e6:fd:59:a3:26:24:60:90:e4:bd:..." :pem "-----BEGIN CERTIFICATE-----\nMIIE6zCCA9OgAwIBAgISBN...") (:version 3 :serial-number "00:91:2b:08:4a:cf:0c:18:a7:53:f6:d6:2e:25:a7:5f:5a" :issuer "C=US,O=Internet Security Research Group,CN=ISRG Ro..." :valid-from "2020-09-04" :valid-to "2025-09-15" :subject "C=US,O=Let's Encrypt,CN=R3" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:8a:93:82:f4:c8:04:08:34:5e:5b:c2:f8:d7:55:d3:..." :certificate-id "sha1:a0:53:37:5b:fe:84:e8:b7:48:78:2c:7c:ee:15:82:..." :pem "-----BEGIN CERTIFICATE-----\nMIIFFjCCAv6gAwIBAgIRAJ...")) :certificate (:version 3 :serial-number "04:dd:78:56:c7:1c:b9:a5:4d:6b:b6:ee:e1:5b:8f:6a:4b..." :issuer "C=US,O=Let's Encrypt,CN=R3" :valid-from "2024-06-03" :valid-to "2024-09-01" :subject "CN=elpa.nongnu.org" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:8b:16:cb:f9:32:f1:7d:1f:ec:8a:14:d2:91:ff:3f:..." :certificate-id "sha1:0c:09:41:11:91:e6:fd:59:a3:26:24:60:90:e4:bd:..." :pem "-----BEGIN CERTIFICATE-----\nMIIE6zCCA9OgAwIBAgISBN...") :key-exchange "ECDHE-RSA" :protocol "TLS1.3" :cipher "AES-256-GCM" :mac "AEAD" :encrypt-then-mac nil)))
apply(#f(compiled-function (status) #<bytecode 0xf5987e8eb8f468e>) (:peer (:certificates ((:version 3 :serial-number "04:dd:78:56:c7:1c:b9:a5:4d:6b:b6:ee:e1:5b:8f:6a:4b..." :issuer "C=US,O=Let's Encrypt,CN=R3" :valid-from "2024-06-03" :valid-to "2024-09-01" :subject "CN=elpa.nongnu.org" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:8b:16:cb:f9:32:f1:7d:1f:ec:8a:14:d2:91:ff:3f:..." :certificate-id "sha1:0c:09:41:11:91:e6:fd:59:a3:26:24:60:90:e4:bd:..." :pem "-----BEGIN CERTIFICATE-----\nMIIE6zCCA9OgAwIBAgISBN...") (:version 3 :serial-number "00:91:2b:08:4a:cf:0c:18:a7:53:f6:d6:2e:25:a7:5f:5a" :issuer "C=US,O=Internet Security Research Group,CN=ISRG Ro..." :valid-from "2020-09-04" :valid-to "2025-09-15" :subject "C=US,O=Let's Encrypt,CN=R3" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:8a:93:82:f4:c8:04:08:34:5e:5b:c2:f8:d7:55:d3:..." :certificate-id "sha1:a0:53:37:5b:fe:84:e8:b7:48:78:2c:7c:ee:15:82:..." :pem "-----BEGIN CERTIFICATE-----\nMIIFFjCCAv6gAwIBAgIRAJ...")) :certificate (:version 3 :serial-number "04:dd:78:56:c7:1c:b9:a5:4d:6b:b6:ee:e1:5b:8f:6a:4b..." :issuer "C=US,O=Let's Encrypt,CN=R3" :valid-from "2024-06-03" :valid-to "2024-09-01" :subject "CN=elpa.nongnu.org" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:8b:16:cb:f9:32:f1:7d:1f:ec:8a:14:d2:91:ff:3f:..." :certificate-id "sha1:0c:09:41:11:91:e6:fd:59:a3:26:24:60:90:e4:bd:..." :pem "-----BEGIN CERTIFICATE-----\nMIIE6zCCA9OgAwIBAgISBN...") :key-exchange "ECDHE-RSA" :protocol "TLS1.3" :cipher "AES-256-GCM" :mac "AEAD" :encrypt-then-mac nil)))
url-http-activate-callback()
url-http-content-length-after-change-function(288 408 120)
url-http-wait-for-headers-change-function(1 419 418)
url-http-generic-filter(#<process elpa.nongnu.org> "HTTP/1.1 200 OK\15\nDate: Tue, 11 Jun 2024 00:51:19 G...")
accept-process-output(nil 0.05)
url-retrieve-synchronously("https://elpa.nongnu.org/nongnu/archive-contents")
package--with-response-buffer-1("https://elpa.nongnu.org/nongnu/" #f(compiled-function () #<bytecode 0x45308209f3a0e3a>) :file "archive-contents" :async nil :error-function #f(compiled-function () #<bytecode -0xb2279de53cccd7d>) :noerror nil)
package--download-one-archive(("nongnu" . "https://elpa.nongnu.org/nongnu/") "archive-contents" nil)
package--download-and-read-archives()
command-line-1(("-f" "package-list-packages" "-f" "package--download-and-read-archives"))
command-line()
normal-top-level()
umount: /tmp/mmdebstrap.3ylx2Gj6Gt/dev/null: target is busy.
W: umount ./dev/null failed: 8192
W: cannot unlink ./dev/null: Device or resource busy
E: setup failed: E: command failed: chroot $1 emacs --batch -Q -f package-list-packages -f package--download-and-read-archives
I: main() received signal PIPE: waiting for setup...
I: removing tempdir /tmp/mmdebstrap.3ylx2Gj6Gt...
E: mmdebstrap failed to run
This issue is not reproducible on Debian 13 (testing / trixie):
bash5$ D13_shell --quiet --include=emacs-nox,ca-certificates,gnupg --customize-hook='chroot $1 emacs --batch -Q -f package-list-packages -f package--download-and-read-archives'
Contacting host: elpa.gnu.org:443
Contacting host: elpa.gnu.org:443
Package refresh done
Package refresh done
Contacting host: elpa.nongnu.org:443
Package refresh done
I: running --customize-hook in shell: sh -c 'env -i TERM=screen PATH=/bin:/sbin chroot $1 bash; false' exec /tmp/mmdebstrap.IGMUZRQL75
This issue is not reproducible on Debian 12 with backported emacs:
bash5$ mmdebstrap bookworm /dev/null --quiet --include=emacs-nox,ca-certificates,gnupg --customize-hook='chroot $1 emacs --batch -Q -f package-list-packages -f package--download-and-read-archives' --essential-hook='(echo Package: src:emacs; echo Pin: release a=stable-backports; echo Pin-Priority: 500) >>"$1/etc/apt/preferences.d/fuck"' 'deb https://deb.debian.org/debian-security bookworm-security main' 'deb http://deb.debian.org/debian bookworm main' 'deb http://deb.debian.org/debian bookworm-updates main' 'deb http://deb.debian.org/debian bookworm-proposed-updates main' 'deb http://deb.debian.org/debian bookworm-backports main'
Contacting host: elpa.gnu.org:443
Contacting host: elpa.gnu.org:443
Contacting host: elpa.nongnu.org:443
Package refresh done
Package refresh done
umount: /tmp/mmdebstrap.LxIijFjees/dev/null: target is busy.
Note that if GPG isn't installed, package.el silently ignores the problem and allows MITM attacks:
bash5$ D12_shell --quiet --include=emacs-nox,ca-certificates --customize-hook='chroot $1 emacs --batch -Q -f package-list-packages -f package--download-and-read-archives'
Contacting host: elpa.gnu.org:443
Contacting host: elpa.nongnu.org:443
Package refresh done
Package refresh done
Package refresh done
I: running --customize-hook in shell: sh -c 'env -i TERM=screen PATH=/bin:/sbin chroot $1 bash; false' exec /tmp/mmdebstrap.TOmuTk5OVd
Note that by default package.el ALSO allows MITM attacks simply by
having the attacker supply a downgraded repo, without signatures:
(defcustom package-check-signature 'allow-unsigned ⋯) ; should be t!