Fix verification condition
Delete a failed verification condition at the beginning of the source reconciliation and set `SourceVerifiedCondition` to false approprietly. Set the `BuildOptions.Verify` to true as long as Verify is enabled in the API fields. Signed-off-by: Soule BA <soule@weave.works>
This commit is contained in:
parent
25673ac512
commit
06a55590a5
|
@ -90,7 +90,7 @@ type HelmChartSpec struct {
|
||||||
// Verify contains the secret name containing the trusted public keys
|
// Verify contains the secret name containing the trusted public keys
|
||||||
// used to verify the signature and specifies which provider to use to check
|
// used to verify the signature and specifies which provider to use to check
|
||||||
// whether OCI image is authentic.
|
// whether OCI image is authentic.
|
||||||
// This field is only supported for OCI sources.
|
// This field is only supported when using HelmRepository source with spec.type 'oci'.
|
||||||
// Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.
|
// Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.
|
||||||
// +optional
|
// +optional
|
||||||
Verify *OCIRepositoryVerification `json:"verify,omitempty"`
|
Verify *OCIRepositoryVerification `json:"verify,omitempty"`
|
||||||
|
|
|
@ -407,8 +407,9 @@ spec:
|
||||||
description: Verify contains the secret name containing the trusted
|
description: Verify contains the secret name containing the trusted
|
||||||
public keys used to verify the signature and specifies which provider
|
public keys used to verify the signature and specifies which provider
|
||||||
to use to check whether OCI image is authentic. This field is only
|
to use to check whether OCI image is authentic. This field is only
|
||||||
supported for OCI sources. Chart dependencies, which are not bundled
|
supported when using HelmRepository source with spec.type 'oci'.
|
||||||
in the umbrella chart artifact, are not verified.
|
Chart dependencies, which are not bundled in the umbrella chart
|
||||||
|
artifact, are not verified.
|
||||||
properties:
|
properties:
|
||||||
provider:
|
provider:
|
||||||
default: cosign
|
default: cosign
|
||||||
|
|
|
@ -372,6 +372,12 @@ func (r *HelmChartReconciler) reconcileStorage(ctx context.Context, obj *sourcev
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *HelmChartReconciler) reconcileSource(ctx context.Context, obj *sourcev1.HelmChart, build *chart.Build) (_ sreconcile.Result, retErr error) {
|
func (r *HelmChartReconciler) reconcileSource(ctx context.Context, obj *sourcev1.HelmChart, build *chart.Build) (_ sreconcile.Result, retErr error) {
|
||||||
|
// Remove any failed verification condition.
|
||||||
|
// The reason is that a failing verification should be recalculated.
|
||||||
|
if conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) {
|
||||||
|
conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve the source
|
// Retrieve the source
|
||||||
s, err := r.getSource(ctx, obj)
|
s, err := r.getSource(ctx, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -577,10 +583,10 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
|
||||||
if obj.Spec.Verify.SecretRef == nil {
|
if obj.Spec.Verify.SecretRef == nil {
|
||||||
provider = fmt.Sprintf("%s keyless", provider)
|
provider = fmt.Sprintf("%s keyless", provider)
|
||||||
}
|
}
|
||||||
e := serror.NewGeneric(
|
e := &serror.Event{
|
||||||
fmt.Errorf("failed to verify the signature using provider '%s': %w", provider, err),
|
Err: fmt.Errorf("failed to verify the signature using provider '%s': %w", provider, err),
|
||||||
sourcev1.VerificationError,
|
Reason: sourcev1.VerificationError,
|
||||||
)
|
}
|
||||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Err.Error())
|
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Err.Error())
|
||||||
return sreconcile.ResultEmpty, e
|
return sreconcile.ResultEmpty, e
|
||||||
}
|
}
|
||||||
|
@ -650,15 +656,8 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
|
||||||
Force: obj.Generation != obj.Status.ObservedGeneration,
|
Force: obj.Generation != obj.Status.ObservedGeneration,
|
||||||
// The remote builder will not attempt to download the chart if
|
// The remote builder will not attempt to download the chart if
|
||||||
// an artifact exists with the same name and version and `Force` is false.
|
// an artifact exists with the same name and version and `Force` is false.
|
||||||
// It will try to verify the chart if:
|
// It will however try to verify the chart if `obj.Spec.Verify` is set, at every reconciliation.
|
||||||
// - we are on the first reconciliation
|
Verify: obj.Spec.Verify != nil && obj.Spec.Verify.Provider != "",
|
||||||
// - the HelmChart spec has changed (generation drift)
|
|
||||||
// - the previous reconciliation resulted in a failed artifact verification
|
|
||||||
// - there is no artifact in storage
|
|
||||||
Verify: obj.Spec.Verify != nil && (obj.Generation <= 0 ||
|
|
||||||
conditions.GetObservedGeneration(obj, sourcev1.SourceVerifiedCondition) != obj.Generation ||
|
|
||||||
conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) ||
|
|
||||||
obj.GetArtifact() == nil),
|
|
||||||
}
|
}
|
||||||
if artifact := obj.GetArtifact(); artifact != nil {
|
if artifact := obj.GetArtifact(); artifact != nil {
|
||||||
opts.CachedChart = r.Storage.LocalPath(*artifact)
|
opts.CachedChart = r.Storage.LocalPath(*artifact)
|
||||||
|
@ -1293,9 +1292,13 @@ func observeChartBuild(obj *sourcev1.HelmChart, build *chart.Build, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch buildErr.Reason {
|
switch buildErr.Reason {
|
||||||
case chart.ErrChartMetadataPatch, chart.ErrValuesFilesMerge, chart.ErrDependencyBuild, chart.ErrChartPackage, chart.ErrChartVerification:
|
case chart.ErrChartMetadataPatch, chart.ErrValuesFilesMerge, chart.ErrDependencyBuild, chart.ErrChartPackage:
|
||||||
conditions.Delete(obj, sourcev1.FetchFailedCondition)
|
conditions.Delete(obj, sourcev1.FetchFailedCondition)
|
||||||
conditions.MarkTrue(obj, sourcev1.BuildFailedCondition, buildErr.Reason.Reason, buildErr.Error())
|
conditions.MarkTrue(obj, sourcev1.BuildFailedCondition, buildErr.Reason.Reason, buildErr.Error())
|
||||||
|
case chart.ErrChartVerification:
|
||||||
|
conditions.Delete(obj, sourcev1.FetchFailedCondition)
|
||||||
|
conditions.MarkTrue(obj, sourcev1.BuildFailedCondition, buildErr.Reason.Reason, buildErr.Error())
|
||||||
|
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, buildErr.Error())
|
||||||
default:
|
default:
|
||||||
conditions.Delete(obj, sourcev1.BuildFailedCondition)
|
conditions.Delete(obj, sourcev1.BuildFailedCondition)
|
||||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, buildErr.Reason.Reason, buildErr.Error())
|
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, buildErr.Reason.Reason, buildErr.Error())
|
||||||
|
|
|
@ -2262,11 +2262,11 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
shouldSign bool
|
||||||
|
beforeFunc func(obj *sourcev1.HelmChart)
|
||||||
want sreconcile.Result
|
want sreconcile.Result
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantErrMsg string
|
wantErrMsg string
|
||||||
shouldSign bool
|
|
||||||
beforeFunc func(obj *sourcev1.HelmChart)
|
|
||||||
assertConditions []metav1.Condition
|
assertConditions []metav1.Condition
|
||||||
cleanFunc func(g *WithT, build *chart.Build)
|
cleanFunc func(g *WithT, build *chart.Build)
|
||||||
}{
|
}{
|
||||||
|
@ -2280,11 +2280,12 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T
|
||||||
SecretRef: &meta.LocalObjectReference{Name: "cosign-key"},
|
SecretRef: &meta.LocalObjectReference{Name: "cosign-key"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
want: sreconcile.ResultEmpty,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
wantErrMsg: "chart verification error: failed to verify <url>: no matching signatures:",
|
wantErrMsg: "chart verification error: failed to verify <url>: no matching signatures:",
|
||||||
want: sreconcile.ResultEmpty,
|
|
||||||
assertConditions: []metav1.Condition{
|
assertConditions: []metav1.Condition{
|
||||||
*conditions.TrueCondition(sourcev1.BuildFailedCondition, "ChartVerificationError", "chart verification error: failed to verify <url>: no matching signatures:"),
|
*conditions.TrueCondition(sourcev1.BuildFailedCondition, "ChartVerificationError", "chart verification error: failed to verify <url>: no matching signatures:"),
|
||||||
|
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "chart verification error: failed to verify <url>: no matching signatures:"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2296,14 +2297,16 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T
|
||||||
Provider: "cosign",
|
Provider: "cosign",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
wantErr: true,
|
|
||||||
want: sreconcile.ResultEmpty,
|
want: sreconcile.ResultEmpty,
|
||||||
|
wantErr: true,
|
||||||
assertConditions: []metav1.Condition{
|
assertConditions: []metav1.Condition{
|
||||||
*conditions.TrueCondition(sourcev1.BuildFailedCondition, "ChartVerificationError", "chart verification error: failed to verify <url>: no matching signatures:"),
|
*conditions.TrueCondition(sourcev1.BuildFailedCondition, "ChartVerificationError", "chart verification error: failed to verify <url>: no matching signatures:"),
|
||||||
|
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "chart verification error: failed to verify <url>: no matching signatures:"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "signed charts should pass verification",
|
name: "signed charts should pass verification",
|
||||||
|
shouldSign: true,
|
||||||
beforeFunc: func(obj *sourcev1.HelmChart) {
|
beforeFunc: func(obj *sourcev1.HelmChart) {
|
||||||
obj.Spec.Chart = metadata.Name
|
obj.Spec.Chart = metadata.Name
|
||||||
obj.Spec.Version = metadata.Version
|
obj.Spec.Version = metadata.Version
|
||||||
|
@ -2312,7 +2315,6 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T
|
||||||
SecretRef: &meta.LocalObjectReference{Name: "cosign-key"},
|
SecretRef: &meta.LocalObjectReference{Name: "cosign-key"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shouldSign: true,
|
|
||||||
want: sreconcile.ResultSuccess,
|
want: sreconcile.ResultSuccess,
|
||||||
assertConditions: []metav1.Condition{
|
assertConditions: []metav1.Condition{
|
||||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewChart", "pulled '<name>' chart with version '<version>'"),
|
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewChart", "pulled '<name>' chart with version '<version>'"),
|
||||||
|
|
|
@ -684,7 +684,7 @@ OCIRepositoryVerification
|
||||||
<p>Verify contains the secret name containing the trusted public keys
|
<p>Verify contains the secret name containing the trusted public keys
|
||||||
used to verify the signature and specifies which provider to use to check
|
used to verify the signature and specifies which provider to use to check
|
||||||
whether OCI image is authentic.
|
whether OCI image is authentic.
|
||||||
This field is only supported for OCI sources.
|
This field is only supported when using HelmRepository source with spec.type ‘oci’.
|
||||||
Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.</p>
|
Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -2269,7 +2269,7 @@ OCIRepositoryVerification
|
||||||
<p>Verify contains the secret name containing the trusted public keys
|
<p>Verify contains the secret name containing the trusted public keys
|
||||||
used to verify the signature and specifies which provider to use to check
|
used to verify the signature and specifies which provider to use to check
|
||||||
whether OCI image is authentic.
|
whether OCI image is authentic.
|
||||||
This field is only supported for OCI sources.
|
This field is only supported when using HelmRepository source with spec.type ‘oci’.
|
||||||
Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.</p>
|
Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -290,7 +290,7 @@ data:
|
||||||
|
|
||||||
Note that the keys must have the `.pub` extension for Flux to make use of them.
|
Note that the keys must have the `.pub` extension for Flux to make use of them.
|
||||||
|
|
||||||
Flux will loop over the public keys and use them verify a HelmChart's signature.
|
Flux will loop over the public keys and use them to verify a HelmChart's signature.
|
||||||
This allows for older HelmCharts to be valid as long as the right key is in the secret.
|
This allows for older HelmCharts to be valid as long as the right key is in the secret.
|
||||||
|
|
||||||
#### Keyless verification
|
#### Keyless verification
|
||||||
|
@ -309,6 +309,7 @@ metadata:
|
||||||
name: podinfo
|
name: podinfo
|
||||||
spec:
|
spec:
|
||||||
interval: 5m
|
interval: 5m
|
||||||
|
chart: podinfo
|
||||||
reconcileStrategy: ChartVersion
|
reconcileStrategy: ChartVersion
|
||||||
sourceRef:
|
sourceRef:
|
||||||
kind: HelmRepository
|
kind: HelmRepository
|
||||||
|
|
|
@ -462,7 +462,7 @@ data:
|
||||||
|
|
||||||
Note that the keys must have the `.pub` extension for Flux to make use of them.
|
Note that the keys must have the `.pub` extension for Flux to make use of them.
|
||||||
|
|
||||||
Flux will loop over the public keys and use them verify an artifact's signature.
|
Flux will loop over the public keys and use them to verify an artifact's signature.
|
||||||
This allows for older artifacts to be valid as long as the right key is in the secret.
|
This allows for older artifacts to be valid as long as the right key is in the secret.
|
||||||
|
|
||||||
#### Keyless verification
|
#### Keyless verification
|
||||||
|
|
|
@ -105,7 +105,7 @@ func KeychainAdaptHelper(keyChain authn.Keychain) func(string) (registry.LoginOp
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthAdaptHelper returns an ORAS credentials callback configured with the authorization data
|
// AuthAdaptHelper returns an ORAS credentials callback configured with the authorization data
|
||||||
// from the given authn authenticator This allows for example to make use of credential helpers from
|
// from the given authn authenticator. This allows for example to make use of credential helpers from
|
||||||
// cloud providers.
|
// cloud providers.
|
||||||
// Ref: https://github.com/google/go-containerregistry/tree/main/pkg/authn
|
// Ref: https://github.com/google/go-containerregistry/tree/main/pkg/authn
|
||||||
func AuthAdaptHelper(auth authn.Authenticator) (registry.LoginOption, error) {
|
func AuthAdaptHelper(auth authn.Authenticator) (registry.LoginOption, error) {
|
||||||
|
|
|
@ -523,10 +523,8 @@ func (r *ChartRepository) RemoveCache() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyChart verifies the chart against a signature.
|
// VerifyChart verifies the chart against a signature.
|
||||||
// If no signature is provided, a keyless verification is performed.
|
|
||||||
// It returns an error on failure.
|
// It returns an error on failure.
|
||||||
func (r *ChartRepository) VerifyChart(_ context.Context, _ *repo.ChartVersion) error {
|
func (r *ChartRepository) VerifyChart(_ context.Context, _ *repo.ChartVersion) error {
|
||||||
// no-op
|
|
||||||
// this is a no-op because this is not implemented yet.
|
// this is a no-op because this is not implemented yet.
|
||||||
return fmt.Errorf("not implemented")
|
return fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue