-
Notifications
You must be signed in to change notification settings - Fork 165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SLSA attestations for the Build resource (aka app image attestations) #1449
Conversation
Codecov ReportAttention:
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## main #1449 +/- ##
==========================================
+ Coverage 67.17% 67.19% +0.02%
==========================================
Files 134 140 +6
Lines 8276 8827 +551
==========================================
+ Hits 5559 5931 +372
- Misses 2264 2389 +125
- Partials 453 507 +54 ☔ View full report in Codecov by Sentry. |
09bc5a4
to
755cdc1
Compare
@matthewmcnew @samj1912 @AidanDelaney can I get some non-Broadcom 👀 on this or the RFC? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hate to provide a partial review, but I have limited time today. Will complete the review tomorrow. Most of my comments are trivial and can be ignored as style changes. I have one question on whether the start/stop time is necessary in an attestation.
if _, found := os.LookupEnv(cosignutil.CosignDockerMediaTypesEnv); found { | ||
err = os.Unsetenv(cosignutil.CosignDockerMediaTypesEnv) | ||
if _, found := os.LookupEnv(CosignDockerMediaTypesEnv); found { | ||
err = os.Unsetenv(CosignDockerMediaTypesEnv) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this to prevent an end-user from injecting an incorrect media type through an environmental variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is to avoid pollution from having multiple signing secrets. Since each signing secret specifies the media type to be generated, if the current secret doesn't specify a format, we should use cosign's default type instead of whatever the previous secret used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way we do cosign is also a bit interesting because the builder signing component (SignBuilder
) is done in the controller container, but the app image signing (Sign
) is done in the build pod.
I do want to unify them sometime in the future (currently playing around with the idea of a new pod/deployment in the kpack namespace that handles both signing and attestation), but for now the 2 methods of this ImageSigner
object is used in completely different codepaths
|
||
report = createReportToml(t, expectedImageName, imageDigest) | ||
|
||
os.Unsetenv(cosignutil.CosignRepositoryEnv) | ||
os.Unsetenv(cosignutil.CosignDockerMediaTypesEnv) | ||
os.Unsetenv(CosignRepositoryEnv) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any guarantees about the order of the tests? AFAIK unsetting these env vars here have an impact on tests that run subsequently. Do we want to change the environment for subsequent tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more of a preliminary cleaning of the test environment, any test that requires the env var will set it explicitly. Technically it should be enough to unset it at the end of the tests, but I guess cleaning before the tests protects against accidentally inheriting env vars from the shell when running the tests.
return extractAttestationKeyFromSecrets(cosignSecrets, privKeySecrets) | ||
} | ||
|
||
func filterCosignSecrets(serviceAccountSecrets []*corev1.Secret, annotation string) []*corev1.Secret { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this filterCosignSecrets
or gatherCosignSecrets
? I'd expect a filter to apply a predicate to some collection. Here it looks like we're gathering the secrets and packing them into a struct, should they exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm indifferent to either name. I would say that this function does apply a predicate: it throws away any items that doesn't have certain annotations. It's return value is also a list of the same type as the input list, it's even in the same order.
7401311
to
592cada
Compare
264b3bc
to
46fd2ec
Compare
before we were only generating it for the git source type. this eventually gets turned into a label on the image as specified in https://github.com/buildpacks/spec/blob/main/platform.md#iobuildpacksprojectmetadata-json Signed-off-by: Bohan Chen <[email protected]>
todo: - extract source from app image labels - record builder image Signed-off-by: Bohan Chen <[email protected]>
The source metadata is read in from the project-metadata label on the built image. This is done for 2 reasons: 1. It resolves floating pointers to concrete shas, this can be the case with a git source pointing to a branch, or a registry source pointing to a tag. We also can't figure out a blob's checksum without actually pulling it 2. It guarantees that this is the version pulled down by the `prepare` step and avoid weird TOCTOU edge cases where the underlying bits changed between the creation of the Build and the execution of the container By exporting the Project, Source, Metadata, and Version structs, I hope to create a code relation between the different packages, so that if we every change one in the future, the compiler will yell at us instead of us having to remember the relationship. Signed-off-by: Bohan Chen <[email protected]>
supported key types are rsa, ecdsa, ed25519, and cosign (which is basically a wrapper around the other 3). there are some smoke tests with hard coded signatures, but i feel the main tests should be the interop with cosign cli Signed-off-by: Bohan Chen <[email protected]>
since we already have a package dedicated to grabbing secrets out of k8s, lets expand it further so that it knows how to extract signing keys out of it. Signed-off-by: Bohan Chen <[email protected]>
0cbef2f
to
6323fde
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is awesome
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing work. I only reviewed the docs files
- split out signing and pushing into different functions mainly to make the error messages clearer - move attestation sign and write functions to obj so that we can mock out the attester interface later - also change the slsa buildid to include `slsa` as a subpath to make it clearer what its used for Signed-off-by: Bohan Chen <[email protected]>
- the controller is now aware of its own service account name, this is to fetch all the secrets associated with it (currently for attestation only, but can be expanded to cosign later) - the feature flag for enabling slsa is marked experimental since the RFC hasn't been merged in yet Signed-off-by: Bohan Chen <[email protected]>
rename slsa.go as the name wasn't really descriptive of what its doing bugs: - client-go strips the TypeMeta from the object so we have to pass it in manually. - granting rbac for the controller to get namespaces - and using version instead of identifier so we don't get the git sha when generating the build type link - ecdsa signing requires digest not full msg - stick to consistent tense for err msgs - and make the start/stop time parsing best effort - the slsa spec says its optional so we shouldn't fail the whole attestation if we can't find it Signed-off-by: Bohan Chen <[email protected]>
since tests are run in parallel, cluster-scoped resources needed to be created with a different name for each of the suites Signed-off-by: Bohan Chen <[email protected]>
Signed-off-by: Bohan Chen <[email protected]>
Signed-off-by: Bohan Chen <[email protected]>
6323fde
to
2046f40
Compare
Implementation of #1374, the feature is marked experimental until the RFC has been merged.
This PR adds SLSA attestations to the Build resource:
EXPERIMENTAL_GENERATE_SLSA_ATTESTATION="true"
can be set on the controller to enable these behavioursA new status
.status.latestAttestationImage
will stamp out the immutable digest of the attestationThe attestation is also pushed to a mutable image tag based off of the app image's digest (digest
my-app-image@sha:1234
will have the attestation stored at tagmy-app-image:sha256-1234
). This is consistent with howcosign
stores their attestations and can be verified withcosign verify-attestation ...
Recommend viewing signed images with
cosign verify-attestation --type=slsaprovenance1 ...
, alternatively unsigned or signed attestations can be viewed withAny secrets attached to the Build's service account, or the kpack controller's service account with the annotation
kpack.io/slsa: ""
will be used as private keys for signingSigning RSA, ECDSA, ED25519, and Cosign keys are supported, the way to use them is specified in the RFC
Things still needed: