You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(appstore): sign the catalogue and verify it fail-closed
Replace the placeholder catalogue trust anchor with a real embedded
ed25519 key (internal/catalogtrust, ldflags-overridable for rotation):
- pilotctl loadCatalogue fetches a detached <url>.sig and verifies it
against the embedded key before trusting any entry; unsigned, missing,
or tampered catalogues are refused.
- New 'pilotctl appstore sign-catalogue' writes the detached signature and
refuses to sign with a key that doesn't match the embedded public key.
- cmd/daemon passes the embedded key into appstore.Config.CatalogPubkey,
retiring the all-zeros placeholder at the wire-up point.
- Sign catalogue.json; document signing + key rotation in catalogue README.
Tests: catalogtrust verify (happy/tamper/short/no-key), signed-catalogue
load + fail-closed (missing sig, tamper).
To rotate: generate a new keypair, store the private key securely, update
185
+
the embedded public key (source default or `-ldflags`), and re-sign the
186
+
catalogue. `sign-catalogue` refuses to sign with a key that doesn't match
187
+
the embedded public key, so a mismatch is caught before publishing a dead
188
+
signature.
153
189
154
190
## Trust model
155
191
156
192
| Layer | Trust anchor | Verifies |
157
193
|---|---|---|
158
194
| User trusts pilotctl | Project release pipeline (signed pilotctl binary) | The catalogue URL is correct |
159
-
| pilotctl trusts the catalogue |Future: signed against `EmbeddedCatalogPubkey`; today: the raw URL itself | App IDs map to specific bundle URLs + SHAs |
195
+
| pilotctl trusts the catalogue |Detached ed25519 signature against the embedded catalogue key (`internal/catalogtrust`) | The app list (IDs → bundle URLs + SHAs) is authentic; a substituted catalogue is rejected|
160
196
| pilotctl trusts the bundle | Embedded `bundle_sha256` matches downloaded bytes | A CDN substitute is rejected |
161
197
| pilotctl trusts the detail doc | Index `metadata_sha256` matches fetched `metadata.json`| A substituted listing is rejected (`view` falls back to the teaser) |
162
198
| Daemon trusts the manifest | Embedded ed25519 publisher pubkey verifies the signature | The bundle's manifest hasn't been tampered with |
// Guard: refuse to sign with a key that doesn't match the embedded
207
+
// trust anchor — the resulting .sig would never verify in the wild.
208
+
embed:=catalogtrust.PublicKey()
209
+
ifembed==nil {
210
+
fatalHint("internal_error", "rebuild pilotctl with a valid embedded catalogue key", "embedded catalogue public key is missing/malformed")
211
+
}
212
+
if!bytes.Equal(pub, embed) {
213
+
fatalHint("invalid_argument",
214
+
"this key does not match the embedded catalogue public key; pilotctl would reject the signature. Use the release catalogue key, or rebuild pilotctl with -ldflags overriding catalogtrust.publicKeyB64",
0 commit comments