-
Notifications
You must be signed in to change notification settings - Fork 187
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
Add support for verifying helm charts using provenance file #605
base: main
Are you sure you want to change the base?
Conversation
internal/helm/chart/builder_local.go
Outdated
} | ||
} | ||
return nil | ||
} |
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.
Maybe it'd be better to move this as an independent function. The only thing it's using from the surrounding is the keyring
, which can just be passed as another function argument.
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.
No?
f3b4a81
to
903d29b
Compare
controllers/helmchart_controller.go
Outdated
@@ -95,6 +97,8 @@ var helmChartReadyCondition = summarize.Conditions{ | |||
}, | |||
} | |||
|
|||
const KeyringFileName = "pubring.gpg" |
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 does not appear to be actively used (and can be omitted in any case due to the API setting a default).
// Deal with the underlying byte slice to avoid having to read the buffer multiple times. | ||
chartBuf := res.Bytes() |
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.
Better to deal with err
first and only then load the bytes.
properties: | ||
key: | ||
default: pubring.gpg | ||
description: The key that corresponds to the keyring value. |
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.
Looks like old definition.
Running make generate
, make manifests
and make api-docs
seems to result in updates to these and a few other files.
|
||
type VerificationKeyring struct { | ||
// +required | ||
SecretRef meta.LocalObjectReference `json:"secretRef,omitempty"` |
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.
SecretRef
and VerificationKeyring
can have some description for documentation purposes.
controllers/helmchart_controller.go
Outdated
Err: fmt.Errorf("failed to get public key for chart signature verification: %w", err), | ||
Reason: sourcev1.AuthenticationFailedReason, | ||
} | ||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Error()) |
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.
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Error()) | |
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Err.Error()) |
We've been doing it this way in other places with an undocumented agreement that e
is an error Event, which just internally calls e.Err.Error()
for now, but if the event error string changes in the future, it'd affect the condition message value too.
Event error may not be equal to just the underlying error in the future.
controllers/helmchart_controller.go
Outdated
if err != nil { | ||
e := &serror.Event{ | ||
Err: fmt.Errorf("failed to get public key for chart signature verification: %w", err), | ||
Reason: sourcev1.AuthenticationFailedReason, |
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 reason doesn't quite fit here. It's not an authentication failure, but we couldn't verify because we couldn't get the material needed to perform the verification.
GitRepository reconciler uses VerificationError
as the reason, which sounds more accurate. Let's create a new reason constant for it and use the same.
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 doc of AuthentiactionFailedReason
, states that :
AuthenticationFailedReason signals that a Secret does not have the required fields, or the provided credentials do not match.
This seems like the appropriate reason to have here, because getProvenanceKeyring
returns an error only when it can't find a secret or the secret doesn't have the required key.
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.
Yes, but only if the Secret is related to authentication, which provenance isn't.
controllers/helmchart_controller.go
Outdated
// Build the chart | ||
ref := chart.RemoteReference{Name: obj.Spec.Chart, Version: obj.Spec.Version} | ||
build, err := cb.Build(ctx, ref, util.TempPathForObj("", ".tgz", obj), opts) | ||
|
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.
don't think this line break is intentional?
internal/helm/chart/builder.go
Outdated
@@ -104,6 +104,10 @@ type BuildOptions struct { | |||
// Force can be set to force the build of the chart, for example | |||
// because the list of ValuesFiles has changed. | |||
Force bool | |||
|
|||
// Keyring can be configured with the bytes of a public kering in legacy |
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.
// Keyring can be configured with the bytes of a public kering in legacy | |
// Keyring can be set to the bytes of a public keyring in legacy |
Maybe more consistent with the other descriptions above it?
internal/helm/chart/builder.go
Outdated
@@ -125,6 +129,13 @@ type Build struct { | |||
// Path is the absolute path to the packaged chart. | |||
// Can be empty, in which case a failure should be assumed. | |||
Path string | |||
// ProvFilePath is the absolute path to a provenance file. | |||
// Can be empty, in which case it should be assumed that the packaged |
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.
// Can be empty, in which case it should be assumed that the packaged | |
// It can be empty, in which case it should be assumed that the packaged |
internal/helm/chart/builder.go
Outdated
// chart is not verified. | ||
ProvFilePath string | ||
// VerificationSignature is populated when a chart's signature | ||
// is successfully verified using it's provenance file. |
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 successfully verified using it's provenance file. | |
// is successfully verified using its provenance file. |
internal/helm/chart/builder_local.go
Outdated
} | ||
} | ||
return nil | ||
} |
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.
No?
@@ -0,0 +1,91 @@ | |||
/* | |||
Copyright 2021 The Flux authors |
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.
Copyright 2021 The Flux authors | |
Copyright 2022 The Flux authors |
internal/util/file.go
Outdated
"path/filepath" | ||
) | ||
|
||
func writeBytesToFile(bytes []byte, out string, temp bool) (*os.File, error) { |
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.
Now that you've two wrapper functions below, how about creating the file in the wrapper functions and don't need to pass a temp
variable to this function, pass it os.File
that you can just write directly to. Since the wrapper function created the file, it already has the os.File
, this function can just return an error.
17d6668
to
c3e0c81
Compare
controllers/helmchart_controller.go
Outdated
var sigVerMsg strings.Builder | ||
sigVerMsg.WriteString(fmt.Sprintf("chart signed by: '%v'", strings.Join(build.VerificationSignature.Identities[:], ","))) | ||
sigVerMsg.WriteString(fmt.Sprintf(" using key with fingeprint: '%X'", build.VerificationSignature.KeyFingerprint)) | ||
sigVerMsg.WriteString(fmt.Sprintf(" and hash verified: '%s'", build.VerificationSignature.FileHash)) |
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.
sigVerMsg.WriteString(fmt.Sprintf(" and hash verified: '%s'", build.VerificationSignature.FileHash)) | |
sigVerMsg.WriteString(fmt.Sprintf(" and hash verified: %q", build.VerificationSignature.FileHash)) |
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 think it makes more sense to be consistent, since we are using %s
instead of %q
in a lot of other places.
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.
We don't use %q
because it borks the json we used for logging.
if err := testStorage.MkdirAll(*obj.Status.Artifact); err != nil { | ||
return err | ||
} | ||
if err := testStorage.AtomicWriteFile(obj.Status.Artifact, strings.NewReader(v), 0644); err != nil { | ||
return err | ||
} | ||
if err := testStorage.AtomicWriteFile(provArtifact, strings.NewReader(v), 0644); err != nil { |
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.
Let's use octal format for representing os-level octal values:
if err := testStorage.AtomicWriteFile(provArtifact, strings.NewReader(v), 0644); err != nil { | |
if err := testStorage.AtomicWriteFile(provArtifact, strings.NewReader(v), 0o644); err != nil { |
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.
Rather than making a couple of changes in this PR, I think this change should be addressed overall in the PR regarding #603
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 do agree on this for existing code, but this line specifically is a new introduction.
c252e68
to
94abac5
Compare
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Sanskar Jaiswal <[email protected]>
94abac5
to
fcd34a8
Compare
Converting PR to draft, as this is being put on hold until we reach GA (ref: fluxcd/flux2#2592) |
Ref: #541