Skip to content

Commit 2b0e355

Browse files
committed
Add cosign verification to the chart Template
If implemented users can reconcile charts with cosign verification enabled. Signed-off-by: Soule BA <bah.soule@gmail.com>
1 parent e543544 commit 2b0e355

8 files changed

+229
-9
lines changed

api/v2beta1/helmrelease_types.go

+21
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,14 @@ type HelmChartTemplateSpec struct {
286286
// +optional
287287
// +deprecated
288288
ValuesFile string `json:"valuesFile,omitempty"`
289+
290+
// Verify contains the secret name containing the trusted public keys
291+
// used to verify the signature and specifies which provider to use to check
292+
// whether OCI image is authentic.
293+
// This field is only supported for OCI sources.
294+
// Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.
295+
// +optional
296+
Verify *HelmChartTemplateVerification `json:"verify,omitempty"`
289297
}
290298

291299
// GetInterval returns the configured interval for the v1beta2.HelmChart,
@@ -306,6 +314,19 @@ func (in HelmChartTemplate) GetNamespace(defaultNamespace string) string {
306314
return in.Spec.SourceRef.Namespace
307315
}
308316

317+
// HelmChartTemplateVerification verifies the authenticity of an OCI Helm chart.
318+
type HelmChartTemplateVerification struct {
319+
// Provider specifies the technology used to sign the OCI Helm chart.
320+
// +kubebuilder:validation:Enum=cosign
321+
// +kubebuilder:default:=cosign
322+
Provider string `json:"provider"`
323+
324+
// SecretRef specifies the Kubernetes Secret containing the
325+
// trusted public keys.
326+
// +optional
327+
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
328+
}
329+
309330
// DeploymentAction defines a consistent interface for Install and Upgrade.
310331
// +kubebuilder:object:generate=false
311332
type DeploymentAction interface {

api/v2beta1/zz_generated.deepcopy.go

+25
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml

+28
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,34 @@ spec:
117117
items:
118118
type: string
119119
type: array
120+
verify:
121+
description: Verify contains the secret name containing the
122+
trusted public keys used to verify the signature and specifies
123+
which provider to use to check whether OCI image is authentic.
124+
This field is only supported for OCI sources. Chart dependencies,
125+
which are not bundled in the umbrella chart artifact, are
126+
not verified.
127+
properties:
128+
provider:
129+
default: cosign
130+
description: Provider specifies the technology used to
131+
sign the OCI Helm chart.
132+
enum:
133+
- cosign
134+
type: string
135+
secretRef:
136+
description: SecretRef specifies the Kubernetes Secret
137+
containing the trusted public keys.
138+
properties:
139+
name:
140+
description: Name of the referent.
141+
type: string
142+
required:
143+
- name
144+
type: object
145+
required:
146+
- provider
147+
type: object
120148
version:
121149
default: '*'
122150
description: Version semver expression, ignored for charts

controllers/helmrelease_controller_chart.go

+15
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ func buildHelmChartFromTemplate(hr *v2.HelmRelease) *sourcev1.HelmChart {
211211
ReconcileStrategy: template.Spec.ReconcileStrategy,
212212
ValuesFiles: template.Spec.ValuesFiles,
213213
ValuesFile: template.Spec.ValuesFile,
214+
Verify: templateVerificationToSourceVerification(template.Spec.Verify),
214215
},
215216
}
216217
}
@@ -239,7 +240,21 @@ func helmChartRequiresUpdate(hr *v2.HelmRelease, chart *sourcev1.HelmChart) bool
239240
return true
240241
case template.Spec.ValuesFile != chart.Spec.ValuesFile:
241242
return true
243+
case !reflect.DeepEqual(templateVerificationToSourceVerification(template.Spec.Verify), chart.Spec.Verify):
244+
return true
242245
default:
243246
return false
244247
}
245248
}
249+
250+
// templateVerificationToSourceVerification converts the HelmChartTemplateVerification to the OCIRepositoryVerification.
251+
func templateVerificationToSourceVerification(template *v2.HelmChartTemplateVerification) *sourcev1.OCIRepositoryVerification {
252+
if template == nil {
253+
return nil
254+
}
255+
256+
return &sourcev1.OCIRepositoryVerification{
257+
Provider: template.Provider,
258+
SecretRef: template.SecretRef,
259+
}
260+
}

controllers/helmrelease_controller_chart_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ package controllers
1818

1919
import (
2020
"context"
21+
"fmt"
2122
"testing"
2223
"time"
2324

25+
"github.com/fluxcd/pkg/apis/meta"
2426
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
2527
"github.com/go-logr/logr"
2628
. "github.com/onsi/gomega"
@@ -371,6 +373,39 @@ func Test_buildHelmChartFromTemplate(t *testing.T) {
371373
},
372374
},
373375
},
376+
{
377+
name: "take cosign verification into account",
378+
modify: func(hr *v2.HelmRelease) {
379+
hr.Spec.Chart.Spec.Verify = &v2.HelmChartTemplateVerification{
380+
Provider: "cosign",
381+
SecretRef: &meta.LocalObjectReference{
382+
Name: "cosign-key",
383+
},
384+
}
385+
},
386+
want: &sourcev1.HelmChart{
387+
ObjectMeta: metav1.ObjectMeta{
388+
Name: "default-test-release",
389+
Namespace: "default",
390+
},
391+
Spec: sourcev1.HelmChartSpec{
392+
Chart: "chart",
393+
Version: "1.0.0",
394+
SourceRef: sourcev1.LocalHelmChartSourceReference{
395+
Name: "test-repository",
396+
Kind: "HelmRepository",
397+
},
398+
Interval: metav1.Duration{Duration: 2 * time.Minute},
399+
ValuesFiles: []string{"values.yaml"},
400+
Verify: &sourcev1.OCIRepositoryVerification{
401+
Provider: "cosign",
402+
SecretRef: &meta.LocalObjectReference{
403+
Name: "cosign-key",
404+
},
405+
},
406+
},
407+
},
408+
},
374409
}
375410
for _, tt := range tests {
376411
t.Run(tt.name, func(t *testing.T) {
@@ -398,6 +433,9 @@ func Test_helmChartRequiresUpdate(t *testing.T) {
398433
Kind: "HelmRepository",
399434
},
400435
Interval: &metav1.Duration{Duration: 2 * time.Minute},
436+
Verify: &v2.HelmChartTemplateVerification{
437+
Provider: "cosign",
438+
},
401439
},
402440
},
403441
},
@@ -469,16 +507,26 @@ func Test_helmChartRequiresUpdate(t *testing.T) {
469507
},
470508
want: true,
471509
},
510+
{
511+
name: "detects verify change",
512+
modify: func(hr *v2.HelmRelease, hc *sourcev1.HelmChart) {
513+
hr.Spec.Chart.Spec.Verify.Provider = "foo-bar"
514+
},
515+
want: true,
516+
},
472517
}
473518
for _, tt := range tests {
474519
t.Run(tt.name, func(t *testing.T) {
475520
g := NewWithT(t)
476521

477522
hr := hrWithChartTemplate.DeepCopy()
478523
hc := buildHelmChartFromTemplate(hr)
524+
// second copy to avoid modifying the original
525+
hr = hrWithChartTemplate.DeepCopy()
479526
g.Expect(helmChartRequiresUpdate(hr, hc)).To(Equal(false))
480527

481528
tt.modify(hr, hc)
529+
fmt.Println("verify", hr.Spec.Chart.Spec.Verify.Provider, hc.Spec.Verify.Provider)
482530
g.Expect(helmChartRequiresUpdate(hr, hc)).To(Equal(tt.want))
483531
})
484532
}

docs/api/helmrelease.md

+83
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,24 @@ for backwards compatibility the file defined here is merged before the
566566
ValuesFiles items. Ignored when omitted.</p>
567567
</td>
568568
</tr>
569+
<tr>
570+
<td>
571+
<code>verify</code><br>
572+
<em>
573+
<a href="#helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplateVerification">
574+
HelmChartTemplateVerification
575+
</a>
576+
</em>
577+
</td>
578+
<td>
579+
<em>(Optional)</em>
580+
<p>Verify contains the secret name containing the trusted public keys
581+
used to verify the signature and specifies which provider to use to check
582+
whether OCI image is authentic.
583+
This field is only supported for OCI sources.
584+
Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.</p>
585+
</td>
586+
</tr>
569587
</table>
570588
</td>
571589
</tr>
@@ -688,6 +706,71 @@ for backwards compatibility the file defined here is merged before the
688706
ValuesFiles items. Ignored when omitted.</p>
689707
</td>
690708
</tr>
709+
<tr>
710+
<td>
711+
<code>verify</code><br>
712+
<em>
713+
<a href="#helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplateVerification">
714+
HelmChartTemplateVerification
715+
</a>
716+
</em>
717+
</td>
718+
<td>
719+
<em>(Optional)</em>
720+
<p>Verify contains the secret name containing the trusted public keys
721+
used to verify the signature and specifies which provider to use to check
722+
whether OCI image is authentic.
723+
This field is only supported for OCI sources.
724+
Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.</p>
725+
</td>
726+
</tr>
727+
</tbody>
728+
</table>
729+
</div>
730+
</div>
731+
<h3 id="helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplateVerification">HelmChartTemplateVerification
732+
</h3>
733+
<p>
734+
(<em>Appears on:</em>
735+
<a href="#helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplateSpec">HelmChartTemplateSpec</a>)
736+
</p>
737+
<p>HelmChartTemplateVerification verifies the authenticity of an OCI Helm chart.</p>
738+
<div class="md-typeset__scrollwrap">
739+
<div class="md-typeset__table">
740+
<table>
741+
<thead>
742+
<tr>
743+
<th>Field</th>
744+
<th>Description</th>
745+
</tr>
746+
</thead>
747+
<tbody>
748+
<tr>
749+
<td>
750+
<code>provider</code><br>
751+
<em>
752+
string
753+
</em>
754+
</td>
755+
<td>
756+
<p>Provider specifies the technology used to sign the OCI Helm chart.</p>
757+
</td>
758+
</tr>
759+
<tr>
760+
<td>
761+
<code>secretRef</code><br>
762+
<em>
763+
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
764+
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
765+
</a>
766+
</em>
767+
</td>
768+
<td>
769+
<em>(Optional)</em>
770+
<p>SecretRef specifies the Kubernetes Secret containing the
771+
trusted public keys.</p>
772+
</td>
773+
</tr>
691774
</tbody>
692775
</table>
693776
</div>

go.mod

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ require (
88
github.com/fluxcd/helm-controller/api v0.25.0
99
github.com/fluxcd/pkg/apis/acl v0.1.0
1010
github.com/fluxcd/pkg/apis/kustomize v0.6.0
11-
github.com/fluxcd/pkg/apis/meta v0.16.0
11+
github.com/fluxcd/pkg/apis/meta v0.17.0
1212
github.com/fluxcd/pkg/runtime v0.19.0
13-
github.com/fluxcd/source-controller/api v0.30.0
13+
github.com/fluxcd/source-controller/api v0.31.0
1414
github.com/go-logr/logr v1.2.3
1515
github.com/hashicorp/go-retryablehttp v0.7.1
1616
github.com/onsi/gomega v1.20.2
1717
github.com/spf13/pflag v1.0.5
1818
helm.sh/helm/v3 v3.10.0
1919
k8s.io/api v0.25.2
2020
k8s.io/apiextensions-apiserver v0.25.2
21-
k8s.io/apimachinery v0.25.2
21+
k8s.io/apimachinery v0.25.3
2222
k8s.io/cli-runtime v0.25.2
2323
k8s.io/client-go v0.25.2
2424
k8s.io/utils v0.0.0-20220922133306-665eaaec4324

go.sum

+6-6
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,12 @@ github.com/fluxcd/pkg/apis/acl v0.1.0 h1:EoAl377hDQYL3WqanWCdifauXqXbMyFuK82NnX6
180180
github.com/fluxcd/pkg/apis/acl v0.1.0/go.mod h1:zfEZzz169Oap034EsDhmCAGgnWlcWmIObZjYMusoXS8=
181181
github.com/fluxcd/pkg/apis/kustomize v0.6.0 h1:Afxv3Uv+xiuettzqm3sP0ceWikDZTfHdHtLv6u2nFM8=
182182
github.com/fluxcd/pkg/apis/kustomize v0.6.0/go.mod h1:iY0zSpK6eUiPfNt/yR6g0q/wQP+wH+Ax/L7KBOx5x2M=
183-
github.com/fluxcd/pkg/apis/meta v0.16.0 h1:6Mj9rB0TtvCeTe3IlQDc1i2DH75Oosea9yUqS7XafVg=
184-
github.com/fluxcd/pkg/apis/meta v0.16.0/go.mod h1:GrOVzWXiu22XjLNgLLe2EBYhQPqZetes5SIADb4bmHE=
183+
github.com/fluxcd/pkg/apis/meta v0.17.0 h1:Y2dfo1syHZDb9Mexjr2SWdcj1FnxnRXm015hEnhl6wU=
184+
github.com/fluxcd/pkg/apis/meta v0.17.0/go.mod h1:GrOVzWXiu22XjLNgLLe2EBYhQPqZetes5SIADb4bmHE=
185185
github.com/fluxcd/pkg/runtime v0.19.0 h1:4lRlnZfJFhWvuaNWgNsAkPQg09633xCRCf9d0SgXIWk=
186186
github.com/fluxcd/pkg/runtime v0.19.0/go.mod h1:9Kh46LjwQeUu6o1DUQulLGyo5e5wfQxeFf4ONNobT3U=
187-
github.com/fluxcd/source-controller/api v0.30.0 h1:rPVPpwXcYG2n0DTRcRagfGDiccvCib5S09K5iMjlpRU=
188-
github.com/fluxcd/source-controller/api v0.30.0/go.mod h1:UkjAqQ6QAXNNesNQDTArTeiTp+UuhOUIA+JyFhGP/+Q=
187+
github.com/fluxcd/source-controller/api v0.31.0 h1:4PZQt2XILTUZ/2JOVGzAIpNDXjx8n10skAhuBHa9tVw=
188+
github.com/fluxcd/source-controller/api v0.31.0/go.mod h1:XOf8hJB7jFcAKiOb8HVZcegkBeNSb4g0nxqnNjeVufg=
189189
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
190190
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
191191
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
@@ -1094,8 +1094,8 @@ k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8=
10941094
k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0=
10951095
k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo=
10961096
k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8=
1097-
k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs=
1098-
k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA=
1097+
k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc=
1098+
k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo=
10991099
k8s.io/apiserver v0.25.2 h1:YePimobk187IMIdnmsMxsfIbC5p4eX3WSOrS9x6FEYw=
11001100
k8s.io/apiserver v0.25.2/go.mod h1:30r7xyQTREWCkG2uSjgjhQcKVvAAlqoD+YyrqR6Cn+I=
11011101
k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc=

0 commit comments

Comments
 (0)