Merge pull request #1176 from fluxcd/cosign-insecure
ocirepo: add cosign support for insecure HTTP registries
This commit is contained in:
commit
66b93aad31
2
go.mod
2
go.mod
|
@ -41,6 +41,7 @@ require (
|
|||
github.com/fluxcd/pkg/testserver v0.4.0
|
||||
github.com/fluxcd/pkg/version v0.2.2
|
||||
github.com/fluxcd/source-controller/api v1.0.0
|
||||
github.com/foxcpp/go-mockdns v1.0.0
|
||||
github.com/go-git/go-billy/v5 v5.4.1
|
||||
github.com/go-git/go-git/v5 v5.8.1
|
||||
github.com/go-logr/logr v1.2.4
|
||||
|
@ -251,6 +252,7 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -418,6 +418,7 @@ github.com/fluxcd/pkg/testserver v0.4.0/go.mod h1:gjOKX41okmrGYOa4oOF2fiLedDAfPo
|
|||
github.com/fluxcd/pkg/version v0.2.2 h1:ZpVXECeLA5hIQMft11iLp6gN3cKcz6UNuVTQPw/bRdI=
|
||||
github.com/fluxcd/pkg/version v0.2.2/go.mod h1:NGnh/no8S6PyfCDxRFrPY3T5BUnqP48MxfxNRU0z8C0=
|
||||
github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI=
|
||||
github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
|
@ -862,7 +863,9 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
|||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
|
@ -1261,6 +1264,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
@ -1341,6 +1345,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -1367,6 +1372,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
|
|||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
|
@ -1431,6 +1437,8 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1549,6 +1557,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
|
|||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
@ -1591,6 +1600,7 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
|
|||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
|
|
|
@ -2285,8 +2285,12 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) {
|
|||
|
||||
workspaceDir := t.TempDir()
|
||||
|
||||
tt.registryOpts.disableDNSMocking = true
|
||||
server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts)
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
server.Close()
|
||||
})
|
||||
|
||||
// Load a test chart
|
||||
chartData, err := os.ReadFile(chartPath)
|
||||
|
@ -2395,8 +2399,13 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T
|
|||
g := NewWithT(t)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
server, err := setupRegistryServer(ctx, tmpDir, registryOptions{})
|
||||
server, err := setupRegistryServer(ctx, tmpDir, registryOptions{
|
||||
disableDNSMocking: true,
|
||||
})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
server.Close()
|
||||
})
|
||||
|
||||
const (
|
||||
chartPath = "testdata/charts/helmchart-0.1.0.tgz"
|
||||
|
|
|
@ -250,8 +250,12 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) {
|
|||
WithStatusSubresource(&helmv1.HelmRepository{})
|
||||
|
||||
workspaceDir := t.TempDir()
|
||||
tt.registryOpts.disableDNSMocking = true
|
||||
server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts)
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
server.Close()
|
||||
})
|
||||
|
||||
obj := &helmv1.HelmRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
|
@ -425,16 +425,6 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
|
|||
conditions.GetObservedGeneration(obj, sourcev1.SourceVerifiedCondition) != obj.Generation ||
|
||||
conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) {
|
||||
|
||||
// Insecure is not supported for verification
|
||||
if obj.Spec.Insecure {
|
||||
e := serror.NewGeneric(
|
||||
fmt.Errorf("cosign does not support insecure registries"),
|
||||
sourcev1.VerificationError,
|
||||
)
|
||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
err := r.verifySignature(ctx, obj, url, opts.verifyOpts...)
|
||||
if err != nil {
|
||||
provider := obj.Spec.Verify.Provider
|
||||
|
@ -633,7 +623,11 @@ func (r *OCIRepositoryReconciler) verifySignature(ctx context.Context, obj *ociv
|
|||
soci.WithRemoteOptions(opt...),
|
||||
}
|
||||
|
||||
ref, err := name.ParseReference(url)
|
||||
var nameOpts []name.Option
|
||||
if obj.Spec.Insecure {
|
||||
nameOpts = append(nameOpts, name.Insecure)
|
||||
}
|
||||
ref, err := name.ParseReference(url, nameOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package controller
|
|||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
|
@ -26,9 +25,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -39,7 +36,6 @@ import (
|
|||
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/crane"
|
||||
"github.com/google/go-containerregistry/pkg/registry"
|
||||
gcrv1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||
. "github.com/onsi/gomega"
|
||||
|
@ -80,8 +76,11 @@ func TestOCIRepository_Reconcile(t *testing.T) {
|
|||
if err != nil {
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
regServer.Close()
|
||||
})
|
||||
|
||||
podinfoVersions, err := pushMultiplePodinfoImages(regServer.registryHost, "6.1.4", "6.1.5", "6.1.6")
|
||||
podinfoVersions, err := pushMultiplePodinfoImages(regServer.registryHost, true, "6.1.4", "6.1.5", "6.1.6")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -146,6 +145,7 @@ func TestOCIRepository_Reconcile(t *testing.T) {
|
|||
URL: tt.url,
|
||||
Interval: metav1.Duration{Duration: 60 * time.Minute},
|
||||
Reference: &ociv1.OCIRepositoryRef{},
|
||||
Insecure: true,
|
||||
},
|
||||
}
|
||||
obj := origObj.DeepCopy()
|
||||
|
@ -262,8 +262,11 @@ func TestOCIRepository_Reconcile_MediaType(t *testing.T) {
|
|||
if err != nil {
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
regServer.Close()
|
||||
})
|
||||
|
||||
podinfoVersions, err := pushMultiplePodinfoImages(regServer.registryHost, "6.1.4", "6.1.5", "6.1.6")
|
||||
podinfoVersions, err := pushMultiplePodinfoImages(regServer.registryHost, true, "6.1.4", "6.1.5", "6.1.6")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -314,6 +317,7 @@ func TestOCIRepository_Reconcile_MediaType(t *testing.T) {
|
|||
LayerSelector: &ociv1.OCILayerSelector{
|
||||
MediaType: tt.mediaType,
|
||||
},
|
||||
Insecure: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -373,6 +377,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
craneOpts []crane.Option
|
||||
secretOpts secretOptions
|
||||
tlsCertSecret *corev1.Secret
|
||||
insecure bool
|
||||
provider string
|
||||
providerImg string
|
||||
want sreconcile.Result
|
||||
|
@ -380,8 +385,10 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
assertConditions []metav1.Condition
|
||||
}{
|
||||
{
|
||||
name: "HTTP without basic auth",
|
||||
want: sreconcile.ResultSuccess,
|
||||
name: "HTTP without basic auth",
|
||||
want: sreconcile.ResultSuccess,
|
||||
craneOpts: []crane.Option{crane.Insecure},
|
||||
insecure: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
|
||||
|
@ -393,10 +400,13 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
registryOpts: registryOptions{
|
||||
withBasicAuth: true,
|
||||
},
|
||||
craneOpts: []crane.Option{crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
insecure: true,
|
||||
craneOpts: []crane.Option{
|
||||
crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
crane.Insecure,
|
||||
},
|
||||
secretOpts: secretOptions{
|
||||
username: testRegistryUsername,
|
||||
|
@ -414,10 +424,13 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
registryOpts: registryOptions{
|
||||
withBasicAuth: true,
|
||||
},
|
||||
craneOpts: []crane.Option{crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
insecure: true,
|
||||
craneOpts: []crane.Option{
|
||||
crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
crane.Insecure,
|
||||
},
|
||||
secretOpts: secretOptions{
|
||||
username: testRegistryUsername,
|
||||
|
@ -435,11 +448,14 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
registryOpts: registryOptions{
|
||||
withBasicAuth: true,
|
||||
},
|
||||
wantErr: true,
|
||||
craneOpts: []crane.Option{crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
insecure: true,
|
||||
wantErr: true,
|
||||
craneOpts: []crane.Option{
|
||||
crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
crane.Insecure,
|
||||
},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, ociv1.OCIPullFailedReason, "failed to determine artifact digest"),
|
||||
|
@ -452,10 +468,13 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
registryOpts: registryOptions{
|
||||
withBasicAuth: true,
|
||||
},
|
||||
craneOpts: []crane.Option{crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
insecure: true,
|
||||
craneOpts: []crane.Option{
|
||||
crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
crane.Insecure,
|
||||
},
|
||||
secretOpts: secretOptions{
|
||||
username: "wrong-pass",
|
||||
|
@ -467,16 +486,19 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "HTTP registry - basic auth with invalid serviceaccount",
|
||||
want: sreconcile.ResultEmpty,
|
||||
wantErr: true,
|
||||
name: "HTTP registry - basic auth with invalid serviceaccount",
|
||||
want: sreconcile.ResultEmpty,
|
||||
wantErr: true,
|
||||
insecure: true,
|
||||
registryOpts: registryOptions{
|
||||
withBasicAuth: true,
|
||||
},
|
||||
craneOpts: []crane.Option{crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
craneOpts: []crane.Option{
|
||||
crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
crane.Insecure,
|
||||
},
|
||||
secretOpts: secretOptions{
|
||||
username: "wrong-pass",
|
||||
|
@ -559,25 +581,32 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
wantErr: true,
|
||||
provider: "aws",
|
||||
providerImg: "oci://123456789000.dkr.ecr.us-east-2.amazonaws.com/test",
|
||||
craneOpts: []crane.Option{
|
||||
crane.Insecure,
|
||||
},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get credential from"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with contextual login provider and secretRef",
|
||||
name: "secretRef takes precedence over provider",
|
||||
want: sreconcile.ResultSuccess,
|
||||
registryOpts: registryOptions{
|
||||
withBasicAuth: true,
|
||||
},
|
||||
craneOpts: []crane.Option{crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
})},
|
||||
craneOpts: []crane.Option{
|
||||
crane.WithAuth(&authn.Basic{
|
||||
Username: testRegistryUsername,
|
||||
Password: testRegistryPassword,
|
||||
}),
|
||||
crane.Insecure,
|
||||
},
|
||||
secretOpts: secretOptions{
|
||||
username: testRegistryUsername,
|
||||
password: testRegistryPassword,
|
||||
includeSecret: true,
|
||||
},
|
||||
insecure: true,
|
||||
provider: "azure",
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
|
||||
|
@ -607,8 +636,10 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
|
||||
workspaceDir := t.TempDir()
|
||||
server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts)
|
||||
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
server.Close()
|
||||
})
|
||||
|
||||
img, err := createPodinfoImageFromTar("podinfo-6.1.6.tar", "6.1.6", server.registryHost, tt.craneOpts...)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
@ -664,6 +695,9 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
Name: tt.tlsCertSecret.Name,
|
||||
}
|
||||
}
|
||||
if tt.insecure {
|
||||
obj.Spec.Insecure = true
|
||||
}
|
||||
|
||||
r := &OCIRepositoryReconciler{
|
||||
Client: clientBuilder.Build(),
|
||||
|
@ -672,7 +706,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
patchOptions: getPatchOptions(ociRepositoryReadyCondition.Owned, "sc"),
|
||||
}
|
||||
|
||||
opts := craneOptions(ctx, true)
|
||||
opts := craneOptions(ctx, tt.insecure)
|
||||
opts = append(opts, crane.WithAuthFromKeychain(authn.DefaultKeychain))
|
||||
repoURL, err := r.getArtifactURL(obj, opts)
|
||||
g.Expect(err).To(BeNil())
|
||||
|
@ -706,34 +740,36 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
|
|||
func TestOCIRepository_CertSecret(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err := createTLSServer()
|
||||
tmpDir := t.TempDir()
|
||||
regServer, err := setupRegistryServer(ctx, tmpDir, registryOptions{
|
||||
withTLS: true,
|
||||
withClientCertAuth: true,
|
||||
})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
regServer.Close()
|
||||
})
|
||||
|
||||
pool := x509.NewCertPool()
|
||||
pool.AppendCertsFromPEM(tlsCA)
|
||||
clientTLSCert, err := tls.X509KeyPair(clientPublicKey, clientPrivateKey)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
srv.StartTLS()
|
||||
defer srv.Close()
|
||||
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{},
|
||||
transport := http.DefaultTransport.(*http.Transport)
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
RootCAs: pool,
|
||||
Certificates: []tls.Certificate{clientTLSCert},
|
||||
}
|
||||
// Use the server cert as a CA cert, so the client trusts the
|
||||
// server cert. (Only works because the server uses the same
|
||||
// cert in both roles).
|
||||
pool := x509.NewCertPool()
|
||||
pool.AddCert(srv.Certificate())
|
||||
transport.TLSClientConfig.RootCAs = pool
|
||||
transport.TLSClientConfig.Certificates = []tls.Certificate{clientTLSCert}
|
||||
|
||||
srv.Client().Transport = transport
|
||||
pi, err := createPodinfoImageFromTar("podinfo-6.1.5.tar", "6.1.5", srv.URL, []crane.Option{
|
||||
crane.WithTransport(srv.Client().Transport),
|
||||
pi, err := createPodinfoImageFromTar("podinfo-6.1.5.tar", "6.1.5", regServer.registryHost, []crane.Option{
|
||||
crane.WithTransport(transport),
|
||||
}...)
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
tlsSecretClientCert := corev1.Secret{
|
||||
StringData: map[string]string{
|
||||
oci.CACert: string(rootCertPEM),
|
||||
oci.ClientCert: string(clientCertPEM),
|
||||
oci.ClientKey: string(clientKeyPEM),
|
||||
Data: map[string][]byte{
|
||||
oci.CACert: tlsCA,
|
||||
oci.ClientCert: clientPublicKey,
|
||||
oci.ClientKey: clientPrivateKey,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -758,17 +794,17 @@ func TestOCIRepository_CertSecret(t *testing.T) {
|
|||
url: pi.url,
|
||||
digest: pi.digest,
|
||||
expectreadyconition: false,
|
||||
expectedstatusmessage: "unexpected status code 400 Bad Request: Client sent an HTTP request to an HTTPS server",
|
||||
expectedstatusmessage: "tls: failed to verify certificate: x509:",
|
||||
},
|
||||
{
|
||||
name: "test connection with with incorrect private key",
|
||||
url: pi.url,
|
||||
digest: pi.digest,
|
||||
certSecret: &corev1.Secret{
|
||||
StringData: map[string]string{
|
||||
oci.CACert: string(rootCertPEM),
|
||||
oci.ClientCert: string(clientCertPEM),
|
||||
oci.ClientKey: string("invalid-key"),
|
||||
Data: map[string][]byte{
|
||||
oci.CACert: tlsCA,
|
||||
oci.ClientCert: clientPublicKey,
|
||||
oci.ClientKey: []byte("invalid-key"),
|
||||
},
|
||||
},
|
||||
expectreadyconition: false,
|
||||
|
@ -859,8 +895,11 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
|
|||
tmpDir := t.TempDir()
|
||||
server, err := setupRegistryServer(ctx, tmpDir, registryOptions{})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
server.Close()
|
||||
})
|
||||
|
||||
podinfoVersions, err := pushMultiplePodinfoImages(server.registryHost, "6.1.4", "6.1.5", "6.1.6")
|
||||
podinfoVersions, err := pushMultiplePodinfoImages(server.registryHost, true, "6.1.4", "6.1.5", "6.1.6")
|
||||
img6 := podinfoVersions["6.1.6"]
|
||||
img5 := podinfoVersions["6.1.5"]
|
||||
|
||||
|
@ -1001,6 +1040,7 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
|
|||
URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost),
|
||||
Interval: metav1.Duration{Duration: interval},
|
||||
Timeout: &metav1.Duration{Duration: timeout},
|
||||
Insecure: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1034,26 +1074,16 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) {
|
|||
func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
server, err := setupRegistryServer(ctx, tmpDir, registryOptions{})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
podinfoVersions, err := pushMultiplePodinfoImages(server.registryHost, "6.1.4", "6.1.5")
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
img4 := podinfoVersions["6.1.4"]
|
||||
img5 := podinfoVersions["6.1.5"]
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
reference *ociv1.OCIRepositoryRef
|
||||
insecure bool
|
||||
digest string
|
||||
want sreconcile.Result
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
shouldSign bool
|
||||
keyless bool
|
||||
beforeFunc func(obj *ociv1.OCIRepository)
|
||||
beforeFunc func(obj *ociv1.OCIRepository, tag, revision string)
|
||||
assertConditions []metav1.Condition
|
||||
}{
|
||||
{
|
||||
|
@ -1061,7 +1091,6 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
reference: &ociv1.OCIRepositoryRef{
|
||||
Tag: "6.1.4",
|
||||
},
|
||||
digest: img4.digest.String(),
|
||||
shouldSign: true,
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
|
@ -1075,7 +1104,6 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
reference: &ociv1.OCIRepositoryRef{
|
||||
Tag: "6.1.5",
|
||||
},
|
||||
digest: img5.digest.String(),
|
||||
wantErr: true,
|
||||
wantErrMsg: "failed to verify the signature using provider 'cosign': no matching signatures were found for '<url>'",
|
||||
want: sreconcile.ResultEmpty,
|
||||
|
@ -1090,7 +1118,6 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
reference: &ociv1.OCIRepositoryRef{
|
||||
Tag: "6.1.5",
|
||||
},
|
||||
digest: img5.digest.String(),
|
||||
wantErr: true,
|
||||
want: sreconcile.ResultEmpty,
|
||||
keyless: true,
|
||||
|
@ -1103,21 +1130,19 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
{
|
||||
name: "verify failed before, removed from spec, remove condition",
|
||||
reference: &ociv1.OCIRepositoryRef{Tag: "6.1.4"},
|
||||
digest: img4.digest.String(),
|
||||
beforeFunc: func(obj *ociv1.OCIRepository) {
|
||||
beforeFunc: func(obj *ociv1.OCIRepository, tag, revision string) {
|
||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, "VerifyFailed", "fail msg")
|
||||
obj.Spec.Verify = nil
|
||||
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", img4.tag, img4.digest.String())}
|
||||
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", tag, revision)}
|
||||
},
|
||||
want: sreconcile.ResultSuccess,
|
||||
},
|
||||
{
|
||||
name: "same artifact, verified before, change in obj gen verify again",
|
||||
reference: &ociv1.OCIRepositoryRef{Tag: "6.1.4"},
|
||||
digest: img4.digest.String(),
|
||||
shouldSign: true,
|
||||
beforeFunc: func(obj *ociv1.OCIRepository) {
|
||||
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", img4.tag, img4.digest.String())}
|
||||
beforeFunc: func(obj *ociv1.OCIRepository, tag, revision string) {
|
||||
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", tag, revision)}
|
||||
// Set Verified with old observed generation and different reason/message.
|
||||
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Verified", "verified")
|
||||
// Set new object generation.
|
||||
|
@ -1131,11 +1156,10 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
{
|
||||
name: "no verify for already verified, verified condition remains the same",
|
||||
reference: &ociv1.OCIRepositoryRef{Tag: "6.1.4"},
|
||||
digest: img4.digest.String(),
|
||||
shouldSign: true,
|
||||
beforeFunc: func(obj *ociv1.OCIRepository) {
|
||||
beforeFunc: func(obj *ociv1.OCIRepository, tag, revision string) {
|
||||
// Artifact present and custom verified condition reason/message.
|
||||
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", img4.tag, img4.digest.String())}
|
||||
obj.Status.Artifact = &sourcev1.Artifact{Revision: fmt.Sprintf("%s@%s", tag, revision)}
|
||||
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Verified", "verified")
|
||||
},
|
||||
want: sreconcile.ResultSuccess,
|
||||
|
@ -1144,19 +1168,17 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "insecure registries are not supported",
|
||||
name: "signed image on an insecure registry passes verification",
|
||||
reference: &ociv1.OCIRepositoryRef{
|
||||
Tag: "6.1.4",
|
||||
Tag: "6.1.6",
|
||||
},
|
||||
digest: img4.digest.String(),
|
||||
shouldSign: true,
|
||||
insecure: true,
|
||||
wantErr: true,
|
||||
want: sreconcile.ResultEmpty,
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
|
||||
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "cosign does not support insecure registries"),
|
||||
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of revision <revision>"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1179,6 +1201,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
keys, err := cosign.GenerateKeyPair(pf)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
err = os.WriteFile(path.Join(tmpDir, "cosign.key"), keys.PrivateBytes, 0600)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
|
@ -1190,15 +1213,34 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
"cosign.pub": keys.PublicBytes,
|
||||
}}
|
||||
|
||||
err = r.Create(ctx, secret)
|
||||
if err != nil {
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
g.Expect(r.Create(ctx, secret)).NotTo(HaveOccurred())
|
||||
|
||||
caSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ca-cert-cosign",
|
||||
Generation: 1,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"caFile": tlsCA,
|
||||
},
|
||||
}
|
||||
|
||||
g.Expect(r.Create(ctx, caSecret)).ToNot(HaveOccurred())
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
workspaceDir := t.TempDir()
|
||||
regOpts := registryOptions{
|
||||
withTLS: !tt.insecure,
|
||||
}
|
||||
server, err := setupRegistryServer(ctx, workspaceDir, regOpts)
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
server.Close()
|
||||
})
|
||||
|
||||
obj := &ociv1.OCIRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "verify-oci-source-signature-",
|
||||
|
@ -1216,6 +1258,10 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
|
||||
if tt.insecure {
|
||||
obj.Spec.Insecure = true
|
||||
} else {
|
||||
obj.Spec.CertSecretRef = &meta.LocalObjectReference{
|
||||
Name: "ca-cert-cosign",
|
||||
}
|
||||
}
|
||||
|
||||
if !tt.keyless {
|
||||
|
@ -1226,12 +1272,15 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
obj.Spec.Reference = tt.reference
|
||||
}
|
||||
|
||||
podinfoVersions, err := pushMultiplePodinfoImages(server.registryHost, tt.insecure, tt.reference.Tag)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
keychain, err := r.keychain(ctx, obj)
|
||||
if err != nil {
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
opts := craneOptions(ctx, true)
|
||||
opts := craneOptions(ctx, false)
|
||||
opts = append(opts, crane.WithAuthFromKeychain(keychain))
|
||||
artifactURL, err := r.getArtifactURL(obj, opts)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
@ -1250,21 +1299,22 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
|
|||
SkipConfirmation: true,
|
||||
TlogUpload: false,
|
||||
|
||||
Registry: coptions.RegistryOptions{Keychain: keychain, AllowInsecure: true},
|
||||
Registry: coptions.RegistryOptions{Keychain: keychain, AllowInsecure: true, AllowHTTPRegistry: tt.insecure},
|
||||
}, []string{artifactURL})
|
||||
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
image := podinfoVersions[tt.reference.Tag]
|
||||
assertConditions := tt.assertConditions
|
||||
for k := range assertConditions {
|
||||
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<revision>", fmt.Sprintf("%s@%s", tt.reference.Tag, tt.digest))
|
||||
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<revision>", fmt.Sprintf("%s@%s", tt.reference.Tag, image.digest.String()))
|
||||
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<url>", artifactURL)
|
||||
assertConditions[k].Message = strings.ReplaceAll(assertConditions[k].Message, "<provider>", "cosign")
|
||||
}
|
||||
|
||||
if tt.beforeFunc != nil {
|
||||
tt.beforeFunc(obj)
|
||||
tt.beforeFunc(obj, image.tag, image.digest.String())
|
||||
}
|
||||
|
||||
g.Expect(r.Client.Create(ctx, obj)).ToNot(HaveOccurred())
|
||||
|
@ -1297,8 +1347,11 @@ func TestOCIRepository_reconcileSource_noop(t *testing.T) {
|
|||
tmpDir := t.TempDir()
|
||||
server, err := setupRegistryServer(ctx, tmpDir, registryOptions{})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
server.Close()
|
||||
})
|
||||
|
||||
_, err = pushMultiplePodinfoImages(server.registryHost, "6.1.5")
|
||||
_, err = pushMultiplePodinfoImages(server.registryHost, true, "6.1.5")
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// NOTE: The following verifies if it was a noop run by checking the
|
||||
|
@ -1431,6 +1484,7 @@ func TestOCIRepository_reconcileSource_noop(t *testing.T) {
|
|||
Reference: &ociv1.OCIRepositoryRef{Tag: "6.1.5"},
|
||||
Interval: metav1.Duration{Duration: interval},
|
||||
Timeout: &metav1.Duration{Duration: timeout},
|
||||
Insecure: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1709,9 +1763,11 @@ func TestOCIRepository_getArtifactURL(t *testing.T) {
|
|||
|
||||
tmpDir := t.TempDir()
|
||||
server, err := setupRegistryServer(ctx, tmpDir, registryOptions{})
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
t.Cleanup(func() {
|
||||
server.Close()
|
||||
})
|
||||
|
||||
imgs, err := pushMultiplePodinfoImages(server.registryHost, "6.1.4", "6.1.5", "6.1.6")
|
||||
imgs, err := pushMultiplePodinfoImages(server.registryHost, true, "6.1.4", "6.1.5", "6.1.6")
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tests := []struct {
|
||||
|
@ -1778,6 +1834,7 @@ func TestOCIRepository_getArtifactURL(t *testing.T) {
|
|||
URL: tt.url,
|
||||
Interval: metav1.Duration{Duration: interval},
|
||||
Timeout: &metav1.Duration{Duration: timeout},
|
||||
Insecure: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2299,11 +2356,25 @@ func createPodinfoImageFromTar(tarFileName, tag, registryURL string, opts ...cra
|
|||
}, nil
|
||||
}
|
||||
|
||||
func pushMultiplePodinfoImages(serverURL string, versions ...string) (map[string]podinfoImage, error) {
|
||||
func pushMultiplePodinfoImages(serverURL string, insecure bool, versions ...string) (map[string]podinfoImage, error) {
|
||||
podinfoVersions := make(map[string]podinfoImage)
|
||||
|
||||
var opts []crane.Option
|
||||
// If the registry is insecure then instruct configure an insecure HTTP client,
|
||||
// otherwise add the root CA certificate since the HTTPS server is self signed.
|
||||
if insecure {
|
||||
opts = append(opts, crane.Insecure)
|
||||
} else {
|
||||
transport := http.DefaultTransport.(*http.Transport)
|
||||
pool := x509.NewCertPool()
|
||||
pool.AppendCertsFromPEM(tlsCA)
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
RootCAs: pool,
|
||||
}
|
||||
opts = append(opts, crane.WithTransport(transport))
|
||||
}
|
||||
for i := 0; i < len(versions); i++ {
|
||||
pi, err := createPodinfoImageFromTar(fmt.Sprintf("podinfo-%s.tar", versions[i]), versions[i], serverURL)
|
||||
pi, err := createPodinfoImageFromTar(fmt.Sprintf("podinfo-%s.tar", versions[i]), versions[i], serverURL, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2362,75 +2433,6 @@ func createCert(template, parent *x509.Certificate, pub interface{}, parentPriv
|
|||
return
|
||||
}
|
||||
|
||||
func createTLSServer() (*httptest.Server, []byte, []byte, []byte, tls.Certificate, error) {
|
||||
var clientTLSCert tls.Certificate
|
||||
var rootCertPEM, clientCertPEM, clientKeyPEM []byte
|
||||
|
||||
srv := httptest.NewUnstartedServer(registry.New())
|
||||
|
||||
// Create a self-signed cert to use as the CA and server cert.
|
||||
rootKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err
|
||||
}
|
||||
rootCertTmpl, err := certTemplate()
|
||||
if err != nil {
|
||||
return srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err
|
||||
}
|
||||
rootCertTmpl.IsCA = true
|
||||
rootCertTmpl.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature
|
||||
rootCertTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
|
||||
rootCertTmpl.IPAddresses = []net.IP{net.ParseIP("127.0.0.1")}
|
||||
var rootCert *x509.Certificate
|
||||
rootCert, rootCertPEM, err = createCert(rootCertTmpl, rootCertTmpl, &rootKey.PublicKey, rootKey)
|
||||
if err != nil {
|
||||
return srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err
|
||||
}
|
||||
|
||||
rootKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rootKey),
|
||||
})
|
||||
|
||||
// Create a TLS cert using the private key and certificate.
|
||||
rootTLSCert, err := tls.X509KeyPair(rootCertPEM, rootKeyPEM)
|
||||
if err != nil {
|
||||
return srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err
|
||||
}
|
||||
|
||||
// To trust a client certificate, the server must be given a
|
||||
// CA cert pool.
|
||||
pool := x509.NewCertPool()
|
||||
pool.AddCert(rootCert)
|
||||
|
||||
srv.TLS = &tls.Config{
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
Certificates: []tls.Certificate{rootTLSCert},
|
||||
ClientCAs: pool,
|
||||
}
|
||||
|
||||
// Create a client cert, signed by the "CA".
|
||||
clientKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err
|
||||
}
|
||||
clientCertTmpl, err := certTemplate()
|
||||
if err != nil {
|
||||
return srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err
|
||||
}
|
||||
clientCertTmpl.KeyUsage = x509.KeyUsageDigitalSignature
|
||||
clientCertTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
|
||||
_, clientCertPEM, err = createCert(clientCertTmpl, rootCert, &clientKey.PublicKey, rootKey)
|
||||
if err != nil {
|
||||
return srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err
|
||||
}
|
||||
// Encode and load the cert and private key for the client.
|
||||
clientKeyPEM = pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientKey),
|
||||
})
|
||||
clientTLSCert, err = tls.X509KeyPair(clientCertPEM, clientKeyPEM)
|
||||
return srv, rootCertPEM, clientCertPEM, clientKeyPEM, clientTLSCert, err
|
||||
}
|
||||
|
||||
func TestOCIContentConfigChanged(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
@ -21,12 +21,16 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/foxcpp/go-mockdns"
|
||||
"github.com/phayes/freeport"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
@ -95,9 +99,11 @@ var (
|
|||
)
|
||||
|
||||
var (
|
||||
tlsPublicKey []byte
|
||||
tlsPrivateKey []byte
|
||||
tlsCA []byte
|
||||
tlsPublicKey []byte
|
||||
tlsPrivateKey []byte
|
||||
tlsCA []byte
|
||||
clientPublicKey []byte
|
||||
clientPrivateKey []byte
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -114,11 +120,18 @@ type registryClientTestServer struct {
|
|||
registryHost string
|
||||
workspaceDir string
|
||||
registryClient *helmreg.Client
|
||||
dnsServer *mockdns.Server
|
||||
}
|
||||
|
||||
type registryOptions struct {
|
||||
withBasicAuth bool
|
||||
withTLS bool
|
||||
withBasicAuth bool
|
||||
withTLS bool
|
||||
withClientCertAuth bool
|
||||
// Allow disbaling DNS mocking since Helm OCI doesn't yet suppot
|
||||
// insecure OCI registries, which means we need Docker's automatic
|
||||
// connection downgrading if the registry is hosted on localhost.
|
||||
// Once Helm OCI supports insecure registries, we can get rid of this.
|
||||
disableDNSMocking bool
|
||||
}
|
||||
|
||||
func setupRegistryServer(ctx context.Context, workspaceDir string, opts registryOptions) (*registryClientTestServer, error) {
|
||||
|
@ -150,7 +163,28 @@ func setupRegistryServer(ctx context.Context, workspaceDir string, opts registry
|
|||
}
|
||||
|
||||
server.registryHost = fmt.Sprintf("localhost:%d", port)
|
||||
config.HTTP.Addr = fmt.Sprintf("127.0.0.1:%d", port)
|
||||
|
||||
// Change the registry host to a host which is not localhost and
|
||||
// mock DNS to map example.com to 127.0.0.1.
|
||||
// This is required because Docker enforces HTTP if the registry
|
||||
// is hosted on localhost/127.0.0.1.
|
||||
if !opts.disableDNSMocking {
|
||||
server.registryHost = fmt.Sprintf("example.com:%d", port)
|
||||
// Disable DNS server logging as it is extremely chatty.
|
||||
dnsLog := log.Default()
|
||||
dnsLog.SetOutput(ioutil.Discard)
|
||||
server.dnsServer, err = mockdns.NewServerWithLogger(map[string]mockdns.Zone{
|
||||
"example.com.": {
|
||||
A: []string{"127.0.0.1"},
|
||||
},
|
||||
}, dnsLog, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server.dnsServer.PatchNet(net.DefaultResolver)
|
||||
}
|
||||
|
||||
config.HTTP.Addr = fmt.Sprintf(":%d", port)
|
||||
config.HTTP.DrainTimeout = time.Duration(10) * time.Second
|
||||
config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}}
|
||||
|
||||
|
@ -178,6 +212,10 @@ func setupRegistryServer(ctx context.Context, workspaceDir string, opts registry
|
|||
if opts.withTLS {
|
||||
config.HTTP.TLS.Certificate = "testdata/certs/server.pem"
|
||||
config.HTTP.TLS.Key = "testdata/certs/server-key.pem"
|
||||
// Configure CA certificates only if client cert authentication is enabled.
|
||||
if opts.withClientCertAuth {
|
||||
config.HTTP.TLS.ClientCAs = []string{"testdata/certs/ca.pem"}
|
||||
}
|
||||
}
|
||||
|
||||
// setup logger options
|
||||
|
@ -198,6 +236,13 @@ func setupRegistryServer(ctx context.Context, workspaceDir string, opts registry
|
|||
return server, nil
|
||||
}
|
||||
|
||||
func (r *registryClientTestServer) Close() {
|
||||
if r.dnsServer != nil {
|
||||
mockdns.UnpatchNet(net.DefaultResolver)
|
||||
r.dnsServer.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
initTestTLS()
|
||||
|
||||
|
@ -229,11 +274,13 @@ func TestMain(m *testing.M) {
|
|||
panic(fmt.Sprintf("failed to create workspace directory: %v", err))
|
||||
}
|
||||
testRegistryServer, err = setupRegistryServer(ctx, testWorkspaceDir, registryOptions{
|
||||
withBasicAuth: true,
|
||||
withBasicAuth: true,
|
||||
disableDNSMocking: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to create a test registry server: %v", err))
|
||||
}
|
||||
defer testRegistryServer.Close()
|
||||
|
||||
if err := (&GitRepositoryReconciler{
|
||||
Client: testEnv,
|
||||
|
@ -355,6 +402,14 @@ func initTestTLS() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
clientPrivateKey, err = os.ReadFile("testdata/certs/client-key.pem")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
clientPublicKey, err = os.ReadFile("testdata/certs/client.pem")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func newTestStorage(s *testserver.HTTPServer) (*Storage, error) {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
all: server-key.pem
|
||||
all: server-key.pem client-key.pem
|
||||
|
||||
ca-key.pem: ca-csr.json
|
||||
cfssl gencert -initca ca-csr.json | cfssljson -bare ca –
|
||||
|
@ -28,3 +28,13 @@ server-key.pem: server-csr.json ca-config.json ca-key.pem
|
|||
server-csr.json | cfssljson -bare server
|
||||
sever.pem: server-key.pem
|
||||
server.csr: server-key.pem
|
||||
|
||||
client-key.pem: client-csr.json ca-config.json ca-key.pem
|
||||
cfssl gencert \
|
||||
-ca=ca.pem \
|
||||
-ca-key=ca-key.pem \
|
||||
-config=ca-config.json \
|
||||
-profile=web-servers \
|
||||
client-csr.json | cfssljson -bare client
|
||||
client.pem: client-key.pem
|
||||
client.csr: client-key.pem
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"CN": "example.com",
|
||||
"hosts": [
|
||||
"127.0.0.1",
|
||||
"localhost",
|
||||
"example.com",
|
||||
"www.example.com"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEICpqb1p1TH98yoFXEEt6JmWc/Snb8NaYyz8jfTOVDBLOoAoGCCqGSM49
|
||||
AwEHoUQDQgAERjzob4CCuyv+cYPyTYCPHwGuqSNGNuX3UGWpxvzwEqjYEWiePlOz
|
||||
eJLk4DWaVX8CmVakNLsK/EHnBv9ErG7QYQ==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,8 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBHDCBwwIBADAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG
|
||||
CCqGSM49AwEHA0IABEY86G+Agrsr/nGD8k2Ajx8BrqkjRjbl91Blqcb88BKo2BFo
|
||||
nj5Ts3iS5OA1mlV/AplWpDS7CvxB5wb/RKxu0GGgSzBJBgkqhkiG9w0BCQ4xPDA6
|
||||
MDgGA1UdEQQxMC+CCWxvY2FsaG9zdIILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxl
|
||||
LmNvbYcEfwAAATAKBggqhkjOPQQDAgNIADBFAiAHmtr9fDDx5eyFfY7r5m8xA4Wh
|
||||
Jm+TB6/czvXRNNOKzAIhAN7ln6BpneEm2oqIBGqvfc3pETC6jdGJxCfYw+X+7von
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,13 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIB7DCCAZKgAwIBAgIUPJmKtZ6CfSxybx2BSsVS5EVun0swCgYIKoZIzj0EAwIw
|
||||
GTEXMBUGA1UEAxMOZXhhbXBsZS5jb20gQ0EwHhcNMjMwNzE5MTExMzAwWhcNMzMw
|
||||
NzE2MTExMzAwWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG
|
||||
CCqGSM49AwEHA0IABEY86G+Agrsr/nGD8k2Ajx8BrqkjRjbl91Blqcb88BKo2BFo
|
||||
nj5Ts3iS5OA1mlV/AplWpDS7CvxB5wb/RKxu0GGjgbowgbcwDgYDVR0PAQH/BAQD
|
||||
AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAA
|
||||
MB0GA1UdDgQWBBTgAyCQoH/EJqz/nY5DJa/uvWWshzAfBgNVHSMEGDAWgBQGyUiU
|
||||
1QEZiMAqjsnIYTwZ4yp5wzA4BgNVHREEMTAvgglsb2NhbGhvc3SCC2V4YW1wbGUu
|
||||
Y29tgg93d3cuZXhhbXBsZS5jb22HBH8AAAEwCgYIKoZIzj0EAwIDSAAwRQIgKSJH
|
||||
YvhKiXcUUzRoL6FsXQeAlhemSg3lD9se+BhRF8ECIQDx2UpWFLDe5NOPqhrcR1Sd
|
||||
haFriAG8eR1yD3u3nJvY6g==
|
||||
-----END CERTIFICATE-----
|
Loading…
Reference in New Issue