Problem
docs/how-to/operate-a-native-package-repository.md
tells an adopter to create four key pairs and base64-encode the private halves,
but never states the PEM encoding each consumer requires. Two of the four
have a hard requirement that the obvious OpenSSL 3 command does not satisfy, and
neither failure surfaces until after a tag exists.
Producer APK key must be a legacy-encrypted traditional RSA PEM
sign-native-packages: true passes APK_SIGNING_KEY through to nFPM, which
signs with internal/sign/rsa.go (nfpm v2.47.0):
decryptedBlockData, err = x509.DecryptPEMBlock(block, []byte(passphrase))
...
priv, err = x509.ParsePKCS1PrivateKey(blockData)
x509.DecryptPEMBlock only understands the legacy Proc-Type: 4,ENCRYPTED /
DEK-Info: PEM headers. The natural command an adopter reaches for on OpenSSL 3
openssl genrsa -aes256 -out apk-signing-key.pem 4096
emits a PKCS#8 -----BEGIN ENCRYPTED PRIVATE KEY----- block, which fails. The
working form is:
openssl genrsa -out apk.plain 4096
openssl rsa -in apk.plain -traditional -aes256 -passout file:pass.txt -out apk-signing-key.pem
which yields -----BEGIN RSA PRIVATE KEY----- with Proc-Type: 4,ENCRYPTED.
Aggregate APK index key must be unencrypted
PACKAGE_REPOSITORY_APK_PRIVATE_KEY is materialized by
.github/actions/setup-package-repository and handed to
repogen.generateAPK, which mounts it into Alpine and runs:
abuild-sign -k "<mounted key>" APKINDEX.tar.gz
abuild-sign is non-interactive, so this key must be an unencrypted PEM.
The only current signal is the absence of a PACKAGE_REPOSITORY_APK_PASSPHRASE
environment secret in the table, which is easy to read as an oversight rather
than a requirement.
Why this matters
Neither encoding is validated early. go-pre-publish.yml checks only that the
four secrets are non-empty:
const required = ['RPM_SIGNING_KEY','RPM_SIGNING_PASSPHRASE','APK_SIGNING_KEY','APK_SIGNING_PASSPHRASE']
const missing = required.filter((name) => !process.env[name])
so a wrong producer encoding fails inside goreleaser release after the tag
and draft exist, and a wrong aggregate encoding fails inside the central
receiver after the producer release is already public. Both land the adopter
in the recovery path documented in
operate-and-recover-releases.md
for a cause that is purely a key-format detail.
Suggested change
- In
operate-a-native-package-repository.md, replace "one RSA private key for
aggregate APK indexes" and the producer key bullet with exact generation
commands and the resulting PEM header for each of the four keys.
- Add an encoding column to the secret tables in
docs/reference/release-system.md: OpenPGP armored secret key (producer RPM
and aggregate), traditional RSA PEM + legacy encryption (producer APK),
unencrypted traditional RSA PEM (aggregate APK).
- Optionally, have
go-pre-publish.yml and setup-package-repository
pem.Decode each key and fail on the wrong block type, so the error arrives
before the tag rather than mid-publication.
Context
Found while onboarding a second organization (componere) at v0.1.17. All four
keys were verified by decrypting them with the same primitives nFPM and
abuild-sign use before any release was attempted; the producer APK key had to
be regenerated in the traditional form to pass.
Problem
docs/how-to/operate-a-native-package-repository.mdtells an adopter to create four key pairs and base64-encode the private halves,
but never states the PEM encoding each consumer requires. Two of the four
have a hard requirement that the obvious OpenSSL 3 command does not satisfy, and
neither failure surfaces until after a tag exists.
Producer APK key must be a legacy-encrypted traditional RSA PEM
sign-native-packages: truepassesAPK_SIGNING_KEYthrough to nFPM, whichsigns with
internal/sign/rsa.go(nfpm v2.47.0):x509.DecryptPEMBlockonly understands the legacyProc-Type: 4,ENCRYPTED/DEK-Info:PEM headers. The natural command an adopter reaches for on OpenSSL 3emits a PKCS#8
-----BEGIN ENCRYPTED PRIVATE KEY-----block, which fails. Theworking form is:
which yields
-----BEGIN RSA PRIVATE KEY-----withProc-Type: 4,ENCRYPTED.Aggregate APK index key must be unencrypted
PACKAGE_REPOSITORY_APK_PRIVATE_KEYis materialized by.github/actions/setup-package-repositoryand handed torepogen.generateAPK, which mounts it into Alpine and runs:abuild-signis non-interactive, so this key must be an unencrypted PEM.The only current signal is the absence of a
PACKAGE_REPOSITORY_APK_PASSPHRASEenvironment secret in the table, which is easy to read as an oversight rather
than a requirement.
Why this matters
Neither encoding is validated early.
go-pre-publish.ymlchecks only that thefour secrets are non-empty:
so a wrong producer encoding fails inside
goreleaser releaseafter the tagand draft exist, and a wrong aggregate encoding fails inside the central
receiver after the producer release is already public. Both land the adopter
in the recovery path documented in
operate-and-recover-releases.mdfor a cause that is purely a key-format detail.
Suggested change
operate-a-native-package-repository.md, replace "one RSA private key foraggregate APK indexes" and the producer key bullet with exact generation
commands and the resulting PEM header for each of the four keys.
docs/reference/release-system.md: OpenPGP armored secret key (producer RPMand aggregate), traditional RSA PEM + legacy encryption (producer APK),
unencrypted traditional RSA PEM (aggregate APK).
go-pre-publish.ymlandsetup-package-repositorypem.Decodeeach key and fail on the wrong block type, so the error arrivesbefore the tag rather than mid-publication.
Context
Found while onboarding a second organization (
componere) at v0.1.17. All fourkeys were verified by decrypting them with the same primitives nFPM and
abuild-signuse before any release was attempted; the producer APK key had tobe regenerated in the traditional form to pass.