Skip to content

Commit adc131b

Browse files
committed
ROX-34488: add e2e tests for referrer-based signature verification
Add two test images (public-key and keyless sigstore bundle) with corresponding deployments, integrations, and test matrix entries. Includes a runbook for recreating the signed test images. Partially generated by AI.
1 parent ac1bfaf commit adc131b

3 files changed

Lines changed: 180 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Changes should still be described appropriately in JIRA/doc input pages, for inc
5757
would leak to all images sharing the same digest.
5858
- Policies now evaluate correctly per the deployed/checked image name and respect its
5959
vulnerability exceptions, rather than being affected by shared-digest exception leakage.
60+
- ROX-34488: Added support for cosign signature discovery via OCI 1.1 referrers, including
61+
DSSE envelope verification for sigstore bundle-format signatures.
6062

6163
### Removed Features
6264

qa-tests-backend/src/test/groovy/ImageSignatureVerificationTest.groovy

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class ImageSignatureVerificationTest extends BaseSpecification {
4242
static final private String KEYLESS_SIGSTORE_UNVERIFIABLE = "Keyless-Sigstore-Unverifiable"
4343
static final private String KEYLESS_RHTAS_MATCHING = "Keyless-RHTAS-Matching"
4444
static final private String KEYLESS_RHTAS_UNVERIFIABLE = "Keyless-RHTAS-Unverifiable"
45+
static final private String REFERRER_SIGSTORE_BUNDLE_PUBKEY = "Referrer-SigstoreBundle-PubKey"
46+
static final private String REFERRER_SIGSTORE_BUNDLE_KEYLESS = "Referrer-SigstoreBundle-Keyless"
4547

4648
// List of integration names used within tests.
4749
// NOTE: If you add a new name, make sure to add it here.
@@ -59,6 +61,8 @@ class ImageSignatureVerificationTest extends BaseSpecification {
5961
KEYLESS_SIGSTORE_UNVERIFIABLE,
6062
KEYLESS_RHTAS_MATCHING,
6163
KEYLESS_RHTAS_UNVERIFIABLE,
64+
REFERRER_SIGSTORE_BUNDLE_PUBKEY,
65+
REFERRER_SIGSTORE_BUNDLE_KEYLESS,
6266
]
6367

6468
// Public keys used within signature integrations.
@@ -139,6 +143,17 @@ nzTe7BpOmVwmqLkIefEJe5L4PSXtp2KFLZqGO/kY5A==
139143
static final private String BYOPKI_UNVERIFIABLE_ISSUER = "invalid"
140144
static final private String BYOPKI_UNVERIFIABLE_IDENTITY = "invalid"
141145

146+
// Public key used to sign the referrer-based test images (both SimpleSigning and sigstore bundle).
147+
// Source: https://vault.bitwarden.com/#/vault?itemId=95313e19-de46-4533-b160-af620120452a.
148+
static final private Map<String, String> REFERRER_COSIGN_PUBLIC_KEY = [
149+
"Referrer": """\
150+
-----BEGIN PUBLIC KEY-----
151+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu8+OtJR+dVIDHk89zl8WnzcQskow
152+
bfbvZqSDudXtfGwpKu6tZ1CtH9utaLkglXynrLNecl0QvrhuCuKA/htSwA==
153+
-----END PUBLIC KEY-----
154+
""",
155+
]
156+
142157
static final private String KEYLESS_SIGSTORE_ISSUER = "https://github.com/login/oauth"
143158
static final private String KEYLESS_SIGSTORE_IDENTITY = ".*@redhat.com"
144159
static final private String KEYLESS_SIGSTORE_REKOR_URL = "https://rekor.sigstore.dev"
@@ -191,6 +206,10 @@ YTuoC8lh1tt0nLkIQpdAJMuWndZJkRHcZriW1Qc2l3Mau0DtuYK17uz7pEwci+tK
191206
"sha256:37f7b378a29ceb4c551b1b5582e27747b855bbfaa73fa11914fe0df028dc581f"
192207
static final private String KEYLESS_RHTAS_IMAGE_DIGEST =
193208
"sha256:e246aa22ad2cbdfbd19e2a6ca2b275e26245a21920e2b2d0666324cee3f15549"
209+
static final private String REFERRER_SIGSTORE_BUNDLE_PUBKEY_IMAGE_DIGEST =
210+
"sha256:9e2bbca079387d7965c3a9cee6d0c53f4f4e63ff7637877a83c4c05f2a666112"
211+
static final private String REFERRER_SIGSTORE_BUNDLE_KEYLESS_IMAGE_DIGEST =
212+
"sha256:98ad9d1a2be345201bb0709b0d38655eb1b370145c7d94ca1fe9c421f76e245a"
194213

195214
static final private List<String> IMAGE_DIGESTS = [
196215
DISTROLESS_IMAGE_DIGEST,
@@ -202,6 +221,8 @@ YTuoC8lh1tt0nLkIQpdAJMuWndZJkRHcZriW1Qc2l3Mau0DtuYK17uz7pEwci+tK
202221
BYOPKI_IMAGE_DIGEST,
203222
KEYLESS_SIGSTORE_IMAGE_DIGEST,
204223
KEYLESS_RHTAS_IMAGE_DIGEST,
224+
REFERRER_SIGSTORE_BUNDLE_PUBKEY_IMAGE_DIGEST,
225+
REFERRER_SIGSTORE_BUNDLE_KEYLESS_IMAGE_DIGEST,
205226
]
206227

207228
// Deployment holding an image which has a cosign signature that is verifiable with the DISTROLESS_PUBLIC_KEY.
@@ -291,6 +312,26 @@ YTuoC8lh1tt0nLkIQpdAJMuWndZJkRHcZriW1Qc2l3Mau0DtuYK17uz7pEwci+tK
291312
.setNamespace(SIGNATURE_TESTING_NAMESPACE)
292313
.setImagePrefetcherAffinity()
293314

315+
// Deployment holding an image signed with a public key using the sigstore bundle format.
316+
static final private Deployment REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT = new Deployment()
317+
.setName("referrer-sigstore-bundle-pubkey")
318+
.setImage("quay.io/rhacs-eng/qa-signatures:referrer-sigstore-bundle-pubkey" +
319+
"@$REFERRER_SIGSTORE_BUNDLE_PUBKEY_IMAGE_DIGEST")
320+
.addLabel("app", "image-with-referrer-sigstore-bundle-pubkey")
321+
.setCommand(["sleep", "600"])
322+
.setNamespace(SIGNATURE_TESTING_NAMESPACE)
323+
.setImagePrefetcherAffinity()
324+
325+
// Deployment holding an image signed keyless using the sigstore bundle format.
326+
static final private Deployment REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT = new Deployment()
327+
.setName("referrer-sigstore-bundle-keyless")
328+
.setImage("quay.io/rhacs-eng/qa-signatures:referrer-sigstore-bundle-keyless" +
329+
"@$REFERRER_SIGSTORE_BUNDLE_KEYLESS_IMAGE_DIGEST")
330+
.addLabel("app", "image-with-referrer-sigstore-bundle-keyless")
331+
.setCommand(["sleep", "600"])
332+
.setNamespace(SIGNATURE_TESTING_NAMESPACE)
333+
.setImagePrefetcherAffinity()
334+
294335
// List of deployments used within the tests. This will be used during setup of the spec / teardown to create /
295336
// delete all deployments.
296337
// NOTE: If you add another deployment, make sure to add it here as well.
@@ -304,6 +345,8 @@ YTuoC8lh1tt0nLkIQpdAJMuWndZJkRHcZriW1Qc2l3Mau0DtuYK17uz7pEwci+tK
304345
BYOPKI_DEPLOYMENT,
305346
KEYLESS_SIGSTORE_DEPLOYMENT,
306347
KEYLESS_RHTAS_DEPLOYMENT,
348+
REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT,
349+
REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT,
307350
]
308351

309352
// Base policy which will be used for creating subsequent policies that have signature integration IDs as values.
@@ -487,6 +530,27 @@ YTuoC8lh1tt0nLkIQpdAJMuWndZJkRHcZriW1Qc2l3Mau0DtuYK17uz7pEwci+tK
487530
CREATED_SIGNATURE_INTEGRATIONS.put(KEYLESS_RHTAS_UNVERIFIABLE,
488531
keylessRHTASUnverifiableSignatureIntegrationID)
489532

533+
// Signature integration for referrer sigstore bundle signed with a public key.
534+
String referrerSigstoreBundlePubKeyID = createSignatureIntegration(
535+
REFERRER_SIGSTORE_BUNDLE_PUBKEY, REFERRER_COSIGN_PUBLIC_KEY,
536+
)
537+
assert referrerSigstoreBundlePubKeyID
538+
CREATED_SIGNATURE_INTEGRATIONS.put(REFERRER_SIGSTORE_BUNDLE_PUBKEY, referrerSigstoreBundlePubKeyID)
539+
540+
// Signature integration for referrer sigstore bundle signed keyless via public Sigstore.
541+
String referrerSigstoreBundleKeylessID = createSignatureIntegration(
542+
REFERRER_SIGSTORE_BUNDLE_KEYLESS, NO_PUBLIC_KEYS,
543+
new CertificateVerificationArgs(
544+
chain: "",
545+
identity: KEYLESS_SIGSTORE_IDENTITY,
546+
issuer: KEYLESS_SIGSTORE_ISSUER,
547+
ctlogEnabled: true,
548+
),
549+
new TransparencyLogVerificationArgs(enabled: true, url: KEYLESS_SIGSTORE_REKOR_URL),
550+
)
551+
assert referrerSigstoreBundleKeylessID
552+
CREATED_SIGNATURE_INTEGRATIONS.put(REFERRER_SIGSTORE_BUNDLE_KEYLESS, referrerSigstoreBundleKeylessID)
553+
490554
// Create all required deployments.
491555
orchestrator.batchCreateDeployments(DEPLOYMENTS)
492556
DEPLOYMENTS.each { assert Services.waitForDeployment(it) }
@@ -704,7 +768,50 @@ YTuoC8lh1tt0nLkIQpdAJMuWndZJkRHcZriW1Qc2l3Mau0DtuYK17uz7pEwci+tK
704768
KEYLESS_RHTAS_UNVERIFIABLE | SAME_DIGEST_WITH_SIGNATURE | true
705769
KEYLESS_RHTAS_UNVERIFIABLE | TEKTON_DEPLOYMENT | true
706770
KEYLESS_RHTAS_UNVERIFIABLE | UNVERIFIABLE_DEPLOYMENT | true
707-
KEYLESS_RHTAS_UNVERIFIABLE | WITHOUT_SIGNATURE_DEPLOYMENT | true
771+
KEYLESS_RHTAS_UNVERIFIABLE | WITHOUT_SIGNATURE_DEPLOYMENT | true
772+
// Existing integrations should alert for all referrer-based deployments.
773+
DISTROLESS | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
774+
DISTROLESS | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
775+
TEKTON | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
776+
TEKTON | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
777+
UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
778+
UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
779+
DISTROLESS_AND_TEKTON | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
780+
DISTROLESS_AND_TEKTON | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
781+
POLICY_WITH_DISTROLESS_TEKTON_UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
782+
POLICY_WITH_DISTROLESS_TEKTON_UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
783+
SAME_DIGEST | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
784+
SAME_DIGEST | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
785+
BYOPKI_WILDCARD | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
786+
BYOPKI_WILDCARD | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
787+
BYOPKI_MATCHING | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
788+
BYOPKI_MATCHING | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
789+
BYOPKI_UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
790+
BYOPKI_UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
791+
BYOPKI_WILDCARD_AND_TEKTON | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
792+
BYOPKI_WILDCARD_AND_TEKTON | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
793+
KEYLESS_SIGSTORE_MATCHING | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
794+
KEYLESS_SIGSTORE_MATCHING | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
795+
KEYLESS_SIGSTORE_UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
796+
KEYLESS_SIGSTORE_UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
797+
KEYLESS_RHTAS_MATCHING | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
798+
KEYLESS_RHTAS_MATCHING | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
799+
KEYLESS_RHTAS_UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
800+
KEYLESS_RHTAS_UNVERIFIABLE | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
801+
// Referrer SimpleSigning with public key: only the matching deployment passes.
802+
// Referrer SimpleSigning with keyless: only the matching deployment passes.
803+
// Referrer sigstore bundle with public key: only the matching deployment passes.
804+
REFERRER_SIGSTORE_BUNDLE_PUBKEY | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | false
805+
REFERRER_SIGSTORE_BUNDLE_PUBKEY | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | true
806+
REFERRER_SIGSTORE_BUNDLE_PUBKEY | DISTROLESS_DEPLOYMENT | true
807+
REFERRER_SIGSTORE_BUNDLE_PUBKEY | TEKTON_DEPLOYMENT | true
808+
REFERRER_SIGSTORE_BUNDLE_PUBKEY | WITHOUT_SIGNATURE_DEPLOYMENT | true
809+
// Referrer sigstore bundle with keyless: only the matching deployment passes.
810+
REFERRER_SIGSTORE_BUNDLE_KEYLESS | REFERRER_SIGSTORE_BUNDLE_PUBKEY_DEPLOYMENT | true
811+
REFERRER_SIGSTORE_BUNDLE_KEYLESS | REFERRER_SIGSTORE_BUNDLE_KEYLESS_DEPLOYMENT | false
812+
REFERRER_SIGSTORE_BUNDLE_KEYLESS | DISTROLESS_DEPLOYMENT | true
813+
REFERRER_SIGSTORE_BUNDLE_KEYLESS | TEKTON_DEPLOYMENT | true
814+
REFERRER_SIGSTORE_BUNDLE_KEYLESS | WITHOUT_SIGNATURE_DEPLOYMENT | true
708815
}
709816

710817
// Helper which creates a policy builder for a policy which uses the image signature policy criteria.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Referrer-based Signature Test Images
2+
3+
Instructions for creating test images with OCI 1.1 referrer-based sigstore
4+
bundle signatures for `ImageSignatureVerificationTest`.
5+
6+
## Prerequisites
7+
8+
```bash
9+
cosign version # requires v3+ for --new-bundle-format
10+
crane version # for copying base images
11+
```
12+
13+
## Generate a key pair
14+
15+
```bash
16+
cosign generate-key-pair
17+
# Produces cosign.key (private) and cosign.pub (public).
18+
# Store both in Bitwarden alongside existing signature test credentials.
19+
```
20+
21+
## 1. referrer-sigstore-bundle-pubkey
22+
23+
Sigstore bundle format, public key, attached as OCI referrer.
24+
25+
```bash
26+
crane cp docker.io/library/alpine:latest \
27+
quay.io/rhacs-eng/qa-signatures:referrer-sigstore-bundle-pubkey
28+
29+
DIGEST=$(crane digest quay.io/rhacs-eng/qa-signatures:referrer-sigstore-bundle-pubkey)
30+
31+
cosign sign \
32+
--key cosign.key \
33+
--new-bundle-format \
34+
"quay.io/rhacs-eng/qa-signatures@$DIGEST"
35+
```
36+
37+
## 2. referrer-sigstore-bundle-keyless
38+
39+
Sigstore bundle format, keyless (public Sigstore), attached as OCI referrer.
40+
Sign with an `@redhat.com` GitHub identity to match the test's OIDC config.
41+
42+
```bash
43+
crane cp docker.io/library/alpine:latest \
44+
quay.io/rhacs-eng/qa-signatures:referrer-sigstore-bundle-keyless
45+
46+
DIGEST=$(crane digest quay.io/rhacs-eng/qa-signatures:referrer-sigstore-bundle-keyless)
47+
48+
cosign sign \
49+
--new-bundle-format \
50+
"quay.io/rhacs-eng/qa-signatures@$DIGEST"
51+
```
52+
53+
## After signing
54+
55+
Update `ImageSignatureVerificationTest.groovy` with:
56+
- `REFERRER_COSIGN_PUBLIC_KEY`: contents of `cosign.pub`
57+
- `REFERRER_SIGSTORE_BUNDLE_PUBKEY_IMAGE_DIGEST`: digest from step 1
58+
- `REFERRER_SIGSTORE_BUNDLE_KEYLESS_IMAGE_DIGEST`: digest from step 2
59+
60+
## Verification
61+
62+
```bash
63+
cosign verify --key cosign.pub --new-bundle-format \
64+
quay.io/rhacs-eng/qa-signatures:referrer-sigstore-bundle-pubkey
65+
66+
cosign verify --new-bundle-format \
67+
--certificate-identity-regexp=".*@redhat.com" \
68+
--certificate-oidc-issuer="https://github.com/login/oauth" \
69+
quay.io/rhacs-eng/qa-signatures:referrer-sigstore-bundle-keyless
70+
```

0 commit comments

Comments
 (0)