From a6ed978523f8ff6ac32dcd99a6a7b59e1beda632 Mon Sep 17 00:00:00 2001 From: Binbin Li Date: Mon, 29 Apr 2024 07:13:57 +0000 Subject: [PATCH] feat: refactor certStore to support multi-tenancy --- .../akv-key-management-provider.yaml | 2 +- .../inline-key-management-provider.yaml | 6 +- .../certificatestore_controller.go | 13 +- .../certificatestore_controller_test.go | 2 +- pkg/controllers/utils/cert_store.go | 28 ---- pkg/controllers/utils/cert_store_test.go | 35 ---- pkg/controllers/verifier_controller.go | 28 +--- pkg/controllers/verifier_controller_test.go | 25 --- pkg/customresources/certificatestores/api.go | 18 +- .../certificatestores/certificatestores.go | 92 +++++----- .../certificatestores_test.go | 158 ++++++++++++++++-- .../keymanagementprovider.go | 26 ++- pkg/manager/manager.go | 4 +- pkg/verifier/cosign/trustpolicy.go | 26 +-- pkg/verifier/cosign/trustpolicy_test.go | 44 +---- pkg/verifier/notation/notation.go | 30 +--- pkg/verifier/notation/notation_test.go | 4 +- pkg/verifier/notation/truststore.go | 8 +- pkg/verifier/notation/truststore_test.go | 34 +++- test/bats/base-test.bats | 15 +- test/bats/high-availability.bats | 4 +- ..._v1beta1_keymanagementprovider_inline.yaml | 2 +- 22 files changed, 289 insertions(+), 315 deletions(-) rename pkg/controllers/{ => namespaceresource}/certificatestore_controller.go (94%) rename pkg/controllers/{ => namespaceresource}/certificatestore_controller_test.go (99%) delete mode 100644 pkg/controllers/utils/cert_store.go delete mode 100644 pkg/controllers/utils/cert_store_test.go diff --git a/charts/ratify/templates/akv-key-management-provider.yaml b/charts/ratify/templates/akv-key-management-provider.yaml index 3700b6ad89..8314967920 100644 --- a/charts/ratify/templates/akv-key-management-provider.yaml +++ b/charts/ratify/templates/akv-key-management-provider.yaml @@ -1,6 +1,6 @@ {{- if or .Values.azurekeyvault.enabled .Values.akvCertConfig.enabled }} apiVersion: config.ratify.deislabs.io/v1beta1 -kind: NamespacedKeyManagementProvider +kind: KeyManagementProvider metadata: name: kmprovider-akv annotations: diff --git a/charts/ratify/templates/inline-key-management-provider.yaml b/charts/ratify/templates/inline-key-management-provider.yaml index 407bacdc25..665ea0b752 100644 --- a/charts/ratify/templates/inline-key-management-provider.yaml +++ b/charts/ratify/templates/inline-key-management-provider.yaml @@ -2,7 +2,7 @@ --- {{- if .Values.notationCert }} apiVersion: config.ratify.deislabs.io/v1beta1 -kind: NamespacedKeyManagementProvider +kind: KeyManagementProvider metadata: name: {{$fullname}}-notation-inline-cert annotations: @@ -17,7 +17,7 @@ spec: --- {{- range $i, $cert := .Values.notationCerts }} apiVersion: config.ratify.deislabs.io/v1beta1 -kind: NamespacedKeyManagementProvider +kind: KeyManagementProvider metadata: name: {{$fullname}}-notation-inline-cert-{{$i}} annotations: @@ -32,7 +32,7 @@ spec: --- {{- range $i, $key := .Values.cosignKeys }} apiVersion: config.ratify.deislabs.io/v1beta1 -kind: NamespacedKeyManagementProvider +kind: KeyManagementProvider metadata: name: {{$fullname}}-cosign-inline-key-{{$i}} annotations: diff --git a/pkg/controllers/certificatestore_controller.go b/pkg/controllers/namespaceresource/certificatestore_controller.go similarity index 94% rename from pkg/controllers/certificatestore_controller.go rename to pkg/controllers/namespaceresource/certificatestore_controller.go index 5b0922d677..3181a085ff 100644 --- a/pkg/controllers/certificatestore_controller.go +++ b/pkg/controllers/namespaceresource/certificatestore_controller.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package controllers +package namespaceresource import ( "context" @@ -23,6 +23,7 @@ import ( "github.com/deislabs/ratify/pkg/certificateprovider" _ "github.com/deislabs/ratify/pkg/certificateprovider/azurekeyvault" // register azure keyvault certificate provider _ "github.com/deislabs/ratify/pkg/certificateprovider/inline" // register inline certificate provider + "github.com/deislabs/ratify/pkg/controllers" "github.com/deislabs/ratify/pkg/utils" "github.com/sirupsen/logrus" @@ -63,8 +64,7 @@ func (r *CertificateStoreReconciler) Reconcile(ctx context.Context, req ctrl.Req if err := r.Get(ctx, req.NamespacedName, &certStore); err != nil { if apierrors.IsNotFound(err) { logger.Infof("deletion detected, removing certificate store %v", resource) - // TODO: pass the actual namespace once multi-tenancy is supported. - NamespacedCertStores.DeleteStore(constants.EmptyNamespace, resource) + controllers.NamespacedCertStores.DeleteStore(resource) } else { logger.Error(err, "unable to fetch certificate store") } @@ -95,8 +95,7 @@ func (r *CertificateStoreReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, fmt.Errorf("Error fetching certificates in store %v with %v provider, error: %w", resource, certStore.Spec.Provider, err) } - // TODO: pass the actual namespace once multi-tenancy is supported. - NamespacedCertStores.AddStore(constants.EmptyNamespace, resource, certificates) + controllers.NamespacedCertStores.AddStore(resource, certificates) isFetchSuccessful = true emptyErrorString := "" writeCertStoreStatus(ctx, r, certStore, logger, isFetchSuccessful, emptyErrorString, lastFetchedTime, certAttributes) @@ -148,8 +147,8 @@ func writeCertStoreStatus(ctx context.Context, r *CertificateStoreReconciler, ce func updateErrorStatus(certStore *configv1beta1.CertificateStore, errorString string, operationTime *metav1.Time) { // truncate brief error string to maxBriefErrLength briefErr := errorString - if len(errorString) > maxBriefErrLength { - briefErr = fmt.Sprintf("%s...", errorString[:maxBriefErrLength]) + if len(errorString) > constants.MaxBriefErrLength { + briefErr = fmt.Sprintf("%s...", errorString[:constants.MaxBriefErrLength]) } certStore.Status.IsSuccess = false certStore.Status.Error = errorString diff --git a/pkg/controllers/certificatestore_controller_test.go b/pkg/controllers/namespaceresource/certificatestore_controller_test.go similarity index 99% rename from pkg/controllers/certificatestore_controller_test.go rename to pkg/controllers/namespaceresource/certificatestore_controller_test.go index f60baddecb..5aed5b13ad 100644 --- a/pkg/controllers/certificatestore_controller_test.go +++ b/pkg/controllers/namespaceresource/certificatestore_controller_test.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package namespaceresource import ( "fmt" diff --git a/pkg/controllers/utils/cert_store.go b/pkg/controllers/utils/cert_store.go deleted file mode 100644 index 3bfb408162..0000000000 --- a/pkg/controllers/utils/cert_store.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright The Ratify Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package utils - -import ( - "context" - "crypto/x509" - - ctxUtils "github.com/deislabs/ratify/internal/context" - "github.com/deislabs/ratify/pkg/controllers" -) - -// returns the internal certificate map -// TODO: returns certificates from both cluster-wide and given namespace as namespaced verifier could access both. -func GetCertificatesMap(ctx context.Context) map[string][]*x509.Certificate { - return controllers.NamespacedCertStores.GetCertStores(ctxUtils.GetNamespace(ctx)) -} diff --git a/pkg/controllers/utils/cert_store_test.go b/pkg/controllers/utils/cert_store_test.go deleted file mode 100644 index a1fb43039f..0000000000 --- a/pkg/controllers/utils/cert_store_test.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright The Ratify Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package utils - -import ( - "context" - "crypto/x509" - "testing" - - "github.com/deislabs/ratify/pkg/controllers" - - ctxUtils "github.com/deislabs/ratify/internal/context" - cs "github.com/deislabs/ratify/pkg/customresources/certificatestores" -) - -func TestGetCertificatesMap(t *testing.T) { - controllers.NamespacedCertStores = cs.NewActiveCertStores() - controllers.NamespacedCertStores.AddStore("default", "default/certStore", []*x509.Certificate{}) - ctx := ctxUtils.SetContextWithNamespace(context.Background(), "default") - - if certs := GetCertificatesMap(ctx); len(certs) != 1 { - t.Fatalf("Expected 1 certificate store, got %d", len(certs)) - } -} diff --git a/pkg/controllers/verifier_controller.go b/pkg/controllers/verifier_controller.go index eb9f174bc7..d91b5bebd9 100644 --- a/pkg/controllers/verifier_controller.go +++ b/pkg/controllers/verifier_controller.go @@ -19,13 +19,10 @@ import ( "context" "encoding/json" "fmt" - "os" configv1beta1 "github.com/deislabs/ratify/api/v1beta1" "github.com/deislabs/ratify/config" - re "github.com/deislabs/ratify/errors" "github.com/deislabs/ratify/internal/constants" - "github.com/deislabs/ratify/pkg/utils" vc "github.com/deislabs/ratify/pkg/verifier/config" vf "github.com/deislabs/ratify/pkg/verifier/factory" "github.com/deislabs/ratify/pkg/verifier/types" @@ -76,13 +73,7 @@ func (r *VerifierReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return ctrl.Result{}, client.IgnoreNotFound(err) } - namespace, err := getCertStoreNamespace(req.Namespace) - if err != nil { - verifierLogger.Error(err, "unable to get default namespace for certstore specified in verifier crd") - return ctrl.Result{}, err - } - - if err = verifierAddOrReplace(verifier.Spec, resource, namespace); err != nil { + if err := verifierAddOrReplace(verifier.Spec, resource, constants.EmptyNamespace); err != nil { verifierLogger.Error(err, "unable to create verifier from verifier crd") writeVerifierStatus(ctx, r, &verifier, verifierLogger, false, err.Error()) return ctrl.Result{}, err @@ -152,23 +143,6 @@ func (r *VerifierReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -// Historically certStore defined in trust policy only contains name which means the CertStore cannot be uniquely identified -// If verifierNamespace is not empty, this method returns the default cert store namespace else returns the ratify deployed namespace -func getCertStoreNamespace(verifierNamespace string) (string, error) { - // first, check if we can use the verifier namespace as the cert store namespace - if verifierNamespace != "" { - return verifierNamespace, nil - } - - // next, return the ratify deployed namespace - ns, found := os.LookupEnv(utils.RatifyNamespaceEnvVar) - if !found { - return "", re.ErrorCodeEnvNotSet.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("environment variable %s not set", utils.RatifyNamespaceEnvVar)) - } - - return ns, nil -} - func writeVerifierStatus(ctx context.Context, r client.StatusClient, verifier *configv1beta1.Verifier, logger *logrus.Entry, isSuccess bool, errorString string) { if isSuccess { verifier.Status.IsSuccess = true diff --git a/pkg/controllers/verifier_controller_test.go b/pkg/controllers/verifier_controller_test.go index 9b5deabc2f..31dc7adcd9 100644 --- a/pkg/controllers/verifier_controller_test.go +++ b/pkg/controllers/verifier_controller_test.go @@ -212,31 +212,6 @@ func TestWriteVerifierStatus(t *testing.T) { } } -func TestGetCertStoreNamespace(t *testing.T) { - // error scenario, everything is empty, expect error - _, err := getCertStoreNamespace("") - if err.Error() == "environment variable" { - t.Fatalf("env not set should trigger an error") - } - - ratifyDeployedNamespace := "sample" - os.Setenv(utils.RatifyNamespaceEnvVar, ratifyDeployedNamespace) - defer os.Unsetenv(utils.RatifyNamespaceEnvVar) - - // scenario1, when default namespace is provided, then we should expect default - verifierNamespace := "verifierNamespace" - ns, _ := getCertStoreNamespace(verifierNamespace) - if ns != verifierNamespace { - t.Fatalf("default namespace expected") - } - - // scenario2, default is empty, should return ratify installed namespace - ns, _ = getCertStoreNamespace("") - if ns != ratifyDeployedNamespace { - t.Fatalf("default namespace expected") - } -} - func resetVerifierMap() { NamespacedVerifiers = verifiers.NewActiveVerifiers() } diff --git a/pkg/customresources/certificatestores/api.go b/pkg/customresources/certificatestores/api.go index e6cfebafd0..11e750ea13 100644 --- a/pkg/customresources/certificatestores/api.go +++ b/pkg/customresources/certificatestores/api.go @@ -13,19 +13,19 @@ limitations under the License. package certificatestores -import "crypto/x509" +import ( + "context" + "crypto/x509" +) // CertStoreManager is an interface that defines the methods for managing certificate stores across different scopes. type CertStoreManager interface { - // GetCertStores returns certificates for the given scope. - GetCertStores(scope string) map[string][]*x509.Certificate + // GetCertsFromStore returns certificates from the given certificate store. + GetCertsFromStore(ctx context.Context, storeName string) []*x509.Certificate - // AddStore adds the given certificate under the given scope. - AddStore(scope, storeName string, cert []*x509.Certificate) + // AddStore adds the given certificate. + AddStore(storeName string, cert []*x509.Certificate) // DeleteStore deletes the certificate from the given scope. - DeleteStore(scope, storeName string) - - // IsEmpty returns true if there are no certificates. - IsEmpty() bool + DeleteStore(storeName string) } diff --git a/pkg/customresources/certificatestores/certificatestores.go b/pkg/customresources/certificatestores/certificatestores.go index 100448a6ff..51ede77581 100644 --- a/pkg/customresources/certificatestores/certificatestores.go +++ b/pkg/customresources/certificatestores/certificatestores.go @@ -14,72 +14,82 @@ limitations under the License. package certificatestores import ( + "context" "crypto/x509" + "os" + "strings" + "sync" "github.com/deislabs/ratify/internal/constants" + ctxUtils "github.com/deislabs/ratify/internal/context" + "github.com/deislabs/ratify/pkg/utils" + vu "github.com/deislabs/ratify/pkg/verifier/utils" ) +const defaultNamespace = "default" + // ActiveCertStores implements the CertStoreManager interface type ActiveCertStores struct { - // TODO: Implement concurrent safety using sync.Map - // The structure of the map is as follows: - // The first level maps from scope to certificate stores. - // The second level maps from certificate store name to certificates. + // scopedCertStores is mapping from cert store name to certificate list. // The certificate store name is prefixed with the namespace. // Example: // { - // "namespace1": { - // "namespace1/store1": []*x509.Certificate, - // "namespace1/store2": []*x509.Certificate - // }, - // "namespace2": { - // "namespace2/store1": []*x509.Certificate, - // "namespace2/store2": []*x509.Certificate - // } + // "namespace1/store1": []*x509.Certificate, + // "namespace2/store2": []*x509.Certificate // } - // Note: Scope is utilized for organizing and isolating cert stores. In a Kubernetes (K8s) environment, the scope can be either a namespace or an empty string ("") for cluster-wide cert stores. - ScopedCertStores map[string]map[string][]*x509.Certificate + // Note: The namespace "default" is reserved for cluster-wide scenario. + scopedCertStores sync.Map } func NewActiveCertStores() CertStoreManager { - return &ActiveCertStores{ - ScopedCertStores: make(map[string]map[string][]*x509.Certificate), - } + return &ActiveCertStores{} } // GetCertStores fulfills the CertStoreManager interface. -// It returns a list of cert stores for the given scope. If no cert stores are found for the given scope, it returns cluster-wide cert stores. -// TODO: Current implementation always fetches cluster-wide cert stores. Will support actual namespaced certStores in future. -func (c *ActiveCertStores) GetCertStores(_ string) map[string][]*x509.Certificate { - return c.ScopedCertStores[constants.EmptyNamespace] +// It returns a list of certificates in the given store. +func (c *ActiveCertStores) GetCertsFromStore(ctx context.Context, storeName string) []*x509.Certificate { + storeName = prependNamespaceToStoreName(storeName) + if !isCompatibleNamespace(ctx, storeName) { + return []*x509.Certificate{} + } + if certs, ok := c.scopedCertStores.Load(storeName); ok { + return certs.([]*x509.Certificate) + } + return []*x509.Certificate{} } // AddStore fulfills the CertStoreManager interface. -// It adds the given certificate under the given scope. -// TODO: Current implementation always adds the given certificate to cluster-wide cert store. Will support actual namespaced certStores in future. -func (c *ActiveCertStores) AddStore(_, storeName string, certs []*x509.Certificate) { - scope := constants.EmptyNamespace - if c.ScopedCertStores[scope] == nil { - c.ScopedCertStores[scope] = make(map[string][]*x509.Certificate) - } - c.ScopedCertStores[scope][storeName] = certs +// It adds the given certificate under cert store. +func (c *ActiveCertStores) AddStore(storeName string, cert []*x509.Certificate) { + c.scopedCertStores.Store(storeName, cert) } // DeleteStore fulfills the CertStoreManager interface. -// It deletes the certificate from the given scope. -// TODO: Current implementation always deletes the cluster-wide cert store. Will support actual namespaced certStores in future. -func (c *ActiveCertStores) DeleteStore(_, storeName string) { - if store, ok := c.ScopedCertStores[constants.EmptyNamespace]; ok { - delete(store, storeName) +// It deletes the given cert store. +func (c *ActiveCertStores) DeleteStore(storeName string) { + c.scopedCertStores.Delete(storeName) +} + +// Namespaced verifiers could access certStores in the same namespace or "default" namespace. +// Cluster-wide verifier could access all certStores. +// Note: the cluster-wide behavior is different from KMP as we need to keep the behavior backward compatible. +func isCompatibleNamespace(ctx context.Context, storeName string) bool { + namespace := ctxUtils.GetNamespace(ctx) + if namespace == constants.EmptyNamespace { + return true } + return strings.HasPrefix(storeName, namespace+constants.NamespaceSeperator) || strings.HasPrefix(storeName, defaultNamespace+constants.NamespaceSeperator) } -// IsEmpty fulfills the CertStoreManager interface. -// It returns true if there are no certificates. -func (c *ActiveCertStores) IsEmpty() bool { - count := 0 - for _, certStores := range c.ScopedCertStores { - count += len(certStores) +// prependNamespaceToStoreName prepends namespace to store name if not already present. +// If the namespace where Ratify deployed is not set, `default` namespace will be prepended. However, this case should never happen. +func prependNamespaceToStoreName(storeName string) string { + if vu.IsNamespacedNamed(storeName) { + return storeName + } + defaultNS := defaultNamespace + if ns, found := os.LookupEnv(utils.RatifyNamespaceEnvVar); found { + defaultNS = ns } - return count == 0 + return defaultNS + constants.NamespaceSeperator + storeName } diff --git a/pkg/customresources/certificatestores/certificatestores_test.go b/pkg/customresources/certificatestores/certificatestores_test.go index 935dc515bb..97f2bafc13 100644 --- a/pkg/customresources/certificatestores/certificatestores_test.go +++ b/pkg/customresources/certificatestores/certificatestores_test.go @@ -14,42 +14,164 @@ limitations under the License. package certificatestores import ( + "context" + "crypto/rand" + "crypto/rsa" "crypto/x509" + "crypto/x509/pkix" + "math/big" + "os" "testing" + "time" + + "github.com/deislabs/ratify/internal/constants" + ctxUtils "github.com/deislabs/ratify/internal/context" + "github.com/deislabs/ratify/pkg/utils" ) const ( - namespace1 = "namespace1" - namespace2 = "namespace2" - name1 = "name1" - name2 = "name2" + namespace1 = "namespace1" + namespace2 = "namespace2" + name1 = "name1" + name2 = "name2" + store1 = namespace1 + "/" + name1 + store2 = namespace2 + "/" + name2 + ratifyDeployedNamespace = "sample" + defaultStore = "default/" + name1 + storeInRatifyNS = ratifyDeployedNamespace + "/" + name1 + storeWithoutNamespace = name1 +) + +var ( + cert1 = generateTestCert() + cert2 = generateTestCert() + certInDefaultNS = generateTestCert() + certInRatifyNS = generateTestCert() ) func TestCertStoresOperations(t *testing.T) { activeCertStores := NewActiveCertStores() + ctx := context.Background() + certStore1 := []*x509.Certificate{cert1} - if !activeCertStores.IsEmpty() { - t.Errorf("Expected activeCertStores to be empty") + activeCertStores.AddStore(store1, certStore1) + if len(activeCertStores.GetCertsFromStore(ctx, store1)) != 1 { + t.Fatalf("expect to get 1 certificate, but got: %d", len(activeCertStores.GetCertsFromStore(ctx, store1))) } - certStore1 := []*x509.Certificate{} - certStore2 := []*x509.Certificate{} + activeCertStores.DeleteStore(store1) + if len(activeCertStores.GetCertsFromStore(ctx, store1)) != 0 { + t.Fatalf("expect to get 0 certificate, but got: %d", len(activeCertStores.GetCertsFromStore(ctx, store1))) + } +} + +func TestGetCertsFromStore(t *testing.T) { + activeCertStores := NewActiveCertStores() + activeCertStores.AddStore(store1, []*x509.Certificate{cert1}) + activeCertStores.AddStore(store2, []*x509.Certificate{cert2}) + activeCertStores.AddStore(defaultStore, []*x509.Certificate{certInDefaultNS}) + activeCertStores.AddStore(storeInRatifyNS, []*x509.Certificate{certInRatifyNS}) + + os.Setenv(utils.RatifyNamespaceEnvVar, ratifyDeployedNamespace) + defer os.Unsetenv(utils.RatifyNamespaceEnvVar) - activeCertStores.AddStore(namespace1, name1, certStore1) - activeCertStores.AddStore(namespace2, name2, certStore2) + testCases := []struct { + name string + scope string + storeName string + expectedCert *x509.Certificate + }{ + { + name: "clustered access to store with namespace", + scope: constants.EmptyNamespace, + storeName: store1, + expectedCert: cert1, + }, + { + name: "clustered access to store without namespace", + scope: constants.EmptyNamespace, + storeName: storeWithoutNamespace, + expectedCert: certInRatifyNS, + }, + { + name: "clustered access to nonexisting store", + scope: constants.EmptyNamespace, + storeName: "nonexisting", + expectedCert: nil, + }, + { + name: "namespaced access to store under default namespace", + scope: namespace1, + storeName: defaultStore, + expectedCert: certInDefaultNS, + }, + { + name: "namespaced access to store under same namespace", + scope: namespace1, + storeName: store1, + expectedCert: cert1, + }, + { + name: "namespaced access to nonexisting store", + scope: "nonexisting", + storeName: "nonexisting/nonexisting", + expectedCert: nil, + }, + { + name: "namespaced access to store under different namespace", + scope: namespace1, + storeName: store2, + expectedCert: nil, + }, + } - if activeCertStores.IsEmpty() { - t.Errorf("Expected activeCertStores to not be empty") + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctx := ctxUtils.SetContextWithNamespace(context.Background(), tc.scope) + certs := activeCertStores.GetCertsFromStore(ctx, tc.storeName) + if len(certs) == 0 { + if tc.expectedCert != nil { + t.Fatalf("Expected to get certificate, but got none") + } + } else { + if certs[0] != tc.expectedCert { + t.Fatalf("Got unexpected certificate") + } + } + }) } +} - if len(activeCertStores.GetCertStores(namespace1)) != 2 { - t.Errorf("Expected activeCertStores to have 2 cert store") +func generateTestCert() *x509.Certificate { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil } - activeCertStores.DeleteStore(namespace1, name1) - activeCertStores.DeleteStore(namespace2, name2) + // Create a certificate template + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Example Org"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), // Valid for 1 year + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } - if !activeCertStores.IsEmpty() { - t.Errorf("Expected activeCertStores to be empty") + // Create the certificate + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) + if err != nil { + return nil } + + // Parse the certificate + cert, err := x509.ParseCertificate(certDER) + if err != nil { + return nil + } + + return cert } diff --git a/pkg/keymanagementprovider/keymanagementprovider.go b/pkg/keymanagementprovider/keymanagementprovider.go index b8e7ffe6e6..47e2f6b04c 100644 --- a/pkg/keymanagementprovider/keymanagementprovider.go +++ b/pkg/keymanagementprovider/keymanagementprovider.go @@ -20,9 +20,13 @@ import ( "crypto" "crypto/x509" "encoding/pem" + "strings" "sync" "github.com/deislabs/ratify/errors" + "github.com/deislabs/ratify/internal/constants" + ctxUtils "github.com/deislabs/ratify/internal/context" + vu "github.com/deislabs/ratify/pkg/verifier/utils" "github.com/sigstore/sigstore/pkg/cryptoutils" ) @@ -147,14 +151,15 @@ func SetKeysInMap(resource string, providerType string, keys map[KMPMapKey]crypt // GetKeysFromMap gets the keys from the map and returns an empty map with false boolean if not found func GetKeysFromMap(ctx context.Context, resource string) (map[KMPMapKey]PublicKey, bool) { + // A cluster-wide operation can cluster-wide provider + // A namespaced operation can only fetch the provider in the same namespace or cluster-wide provider. if !isCompatibleNamespace(ctx, resource) { return map[KMPMapKey]PublicKey{}, false } - keys, ok := keyMap.Load(resource) - if !ok { - return map[KMPMapKey]PublicKey{}, false + if keys, ok := keyMap.Load(resource); ok { + return keys.(map[KMPMapKey]PublicKey), true } - return keys.(map[KMPMapKey]PublicKey), true + return map[KMPMapKey]PublicKey{}, false } // DeleteKeysFromMap deletes the keys from the map @@ -162,9 +167,12 @@ func DeleteKeysFromMap(resource string) { keyMap.Delete(resource) } -// Namespaced verifiers could access both cluster-scoped and namespaced certStores. -// But cluster-wide verifiers could only access cluster-scoped certStores. -// TODO: current implementation always returns true. Check the namespace once we support multi-tenancy. -func isCompatibleNamespace(_ context.Context, _ string) bool { - return true +// Namespaced verifiers could access KMP in the same namespace or cluster-wide KMP. +// Cluster-wide verifiers could only access cluster-scoped certStores. +func isCompatibleNamespace(ctx context.Context, provider string) bool { + namespace := ctxUtils.GetNamespace(ctx) + if namespace == constants.EmptyNamespace { + return !vu.IsNamespacedNamed(provider) + } + return strings.HasPrefix(provider, namespace+constants.NamespaceSeperator) || !vu.IsNamespacedNamed(provider) } diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index e993d118b9..bdbac0e7c6 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -214,11 +214,11 @@ func StartManager(certRotatorReady chan struct{}, probeAddr string) { setupLog.Error(err, "unable to create controller", "controller", "Namespaced Store") os.Exit(1) } - if err = (&controllers.CertificateStoreReconciler{ + if err = (&namespaceresource.CertificateStoreReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Certificate Store") + setupLog.Error(err, "unable to create controller", "controller", "Namespaced Certificate Store") os.Exit(1) } if err = (&namespaceresource.PolicyReconciler{ diff --git a/pkg/verifier/cosign/trustpolicy.go b/pkg/verifier/cosign/trustpolicy.go index 371a215eb6..ada3ac0111 100644 --- a/pkg/verifier/cosign/trustpolicy.go +++ b/pkg/verifier/cosign/trustpolicy.go @@ -23,9 +23,7 @@ import ( "slices" re "github.com/deislabs/ratify/errors" - "github.com/deislabs/ratify/internal/constants" "github.com/deislabs/ratify/pkg/keymanagementprovider" - vu "github.com/deislabs/ratify/pkg/verifier/utils" "github.com/deislabs/ratify/utils" "github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio" "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" @@ -138,7 +136,7 @@ func (tp *trustPolicy) GetName() string { } // GetKeys returns the public keys defined in the trust policy -func (tp *trustPolicy) GetKeys(ctx context.Context, namespace string) (map[PKKey]keymanagementprovider.PublicKey, error) { +func (tp *trustPolicy) GetKeys(ctx context.Context, _ string) (map[PKKey]keymanagementprovider.PublicKey, error) { keyMap := make(map[PKKey]keymanagementprovider.PublicKey) // preload the local keys into the map of keys to be returned for key, pubKey := range tp.localKeys { @@ -150,24 +148,22 @@ func (tp *trustPolicy) GetKeys(ctx context.Context, namespace string) (map[PKKey if keyConfig.File != "" { continue } - // must prepend namespace to key management provider name if not provided since namespace is prepended during key management provider intialization - namespacedKMP := prependNamespaceToKMPName(keyConfig.Provider, namespace) // get the key management provider resource which contains a map of keys - kmpResource, ok := keymanagementprovider.GetKeysFromMap(ctx, namespacedKMP) + kmpResource, ok := keymanagementprovider.GetKeysFromMap(ctx, keyConfig.Provider) if !ok { - return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(tp.verifierName).WithDetail(fmt.Sprintf("trust policy %s failed: key management provider %s not found", tp.config.Name, namespacedKMP)) + return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(tp.verifierName).WithDetail(fmt.Sprintf("trust policy %s failed: key management provider %s not found", tp.config.Name, keyConfig.Provider)) } // get a specific key from the key management provider resource if keyConfig.Name != "" { pubKey, exists := kmpResource[keymanagementprovider.KMPMapKey{Name: keyConfig.Name, Version: keyConfig.Version}] if !exists { - return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(tp.verifierName).WithDetail(fmt.Sprintf("trust policy %s failed: key %s with version %s not found in key management provider %s", tp.config.Name, keyConfig.Name, keyConfig.Version, namespacedKMP)) + return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(tp.verifierName).WithDetail(fmt.Sprintf("trust policy %s failed: key %s with version %s not found in key management provider %s", tp.config.Name, keyConfig.Name, keyConfig.Version, keyConfig.Provider)) } - keyMap[PKKey{Provider: namespacedKMP, Name: keyConfig.Name, Version: keyConfig.Version}] = pubKey + keyMap[PKKey{Provider: keyConfig.Provider, Name: keyConfig.Name, Version: keyConfig.Version}] = pubKey } else { // get all public keys from the key management provider for key, pubKey := range kmpResource { - keyMap[PKKey{Provider: namespacedKMP, Name: key.Name, Version: key.Version}] = pubKey + keyMap[PKKey{Provider: keyConfig.Provider, Name: key.Name, Version: key.Version}] = pubKey } } } @@ -277,13 +273,3 @@ func loadKeyFromPath(filePath string) (crypto.PublicKey, error) { return cryptoutils.UnmarshalPEMToPublicKey(contents) } - -// prependNamespaceToKMPName prepends the namespace to the key management provider name if not already present -// if the namespace is empty, the key management provider name is returned as is -func prependNamespaceToKMPName(kmpName string, namespace string) string { - // namespace will be empty for CLI scenarios. use the KMP name as is - if vu.IsNamespacedNamed(kmpName) || namespace == "" { - return kmpName - } - return fmt.Sprintf("%s%s%s", namespace, constants.NamespaceSeperator, kmpName) -} diff --git a/pkg/verifier/cosign/trustpolicy_test.go b/pkg/verifier/cosign/trustpolicy_test.go index 948c3d2060..c8d5964ab8 100644 --- a/pkg/verifier/cosign/trustpolicy_test.go +++ b/pkg/verifier/cosign/trustpolicy_test.go @@ -21,6 +21,7 @@ import ( "crypto/ecdsa" "testing" + ctxUtils "github.com/deislabs/ratify/internal/context" "github.com/deislabs/ratify/pkg/keymanagementprovider" ) @@ -174,7 +175,7 @@ func TestGetKeys(t *testing.T) { Scopes: []string{"*"}, Keys: []KeyConfig{ { - Provider: "kmp", + Provider: "ns/kmp", Name: "key1", }, }, @@ -189,7 +190,8 @@ func TestGetKeys(t *testing.T) { if err != nil { t.Fatalf("expected no error, got %v", err) } - keys, err := trustPolicy.GetKeys(context.Background(), "ns") + ctx := ctxUtils.SetContextWithNamespace(context.Background(), "ns") + keys, err := trustPolicy.GetKeys(ctx, "") if (err != nil) != tt.wantErr { t.Fatalf("expected %v, got %v", tt.wantErr, err) } @@ -335,41 +337,3 @@ func TestLoadKeyFromPath(t *testing.T) { t.Fatalf("expected ecdsa.PublicKey, got %v", keyType) } } - -// TestPrependNamespaceToKMPName tests the prependNamespaceToKMPName function -func TestPrependNamespaceToKMPName(t *testing.T) { - tc := []struct { - name string - kmpName string - ns string - expected string - }{ - { - name: "empty namespace", - kmpName: "kmp", - ns: "", - expected: "kmp", - }, - { - name: "non-empty namespace", - kmpName: "kmp", - ns: "ns", - expected: "ns/kmp", - }, - { - name: "namespaced kmp", - kmpName: "ns/kmp", - ns: "ns", - expected: "ns/kmp", - }, - } - - for _, tt := range tc { - t.Run(tt.name, func(t *testing.T) { - actual := prependNamespaceToKMPName(tt.kmpName, tt.ns) - if actual != tt.expected { - t.Fatalf("expected %s, got %s", tt.expected, actual) - } - }) - } -} diff --git a/pkg/verifier/notation/notation.go b/pkg/verifier/notation/notation.go index 10997c8482..a1db7a18b3 100644 --- a/pkg/verifier/notation/notation.go +++ b/pkg/verifier/notation/notation.go @@ -24,7 +24,6 @@ import ( ratifyconfig "github.com/deislabs/ratify/config" re "github.com/deislabs/ratify/errors" - "github.com/deislabs/ratify/internal/constants" "github.com/deislabs/ratify/internal/logger" "github.com/deislabs/ratify/pkg/common" "github.com/deislabs/ratify/pkg/homedir" @@ -37,7 +36,6 @@ import ( "github.com/deislabs/ratify/pkg/verifier/types" "github.com/notaryproject/notation-go/log" - vu "github.com/deislabs/ratify/pkg/verifier/utils" _ "github.com/notaryproject/notation-core-go/signature/cose" // register COSE signature _ "github.com/notaryproject/notation-core-go/signature/jws" // register JWS signature "github.com/notaryproject/notation-go" @@ -188,7 +186,7 @@ func (v *notationPluginVerifier) verifySignature(ctx context.Context, subjectRef return (*v.notationVerifier).Verify(ctx, subjectDesc, refBlob, opts) } -func parseVerifierConfig(verifierConfig config.VerifierConfig, namespace string) (*NotationPluginVerifierConfig, error) { +func parseVerifierConfig(verifierConfig config.VerifierConfig, _ string) (*NotationPluginVerifierConfig, error) { verifierName := verifierConfig[types.Name].(string) conf := &NotationPluginVerifierConfig{} @@ -201,15 +199,6 @@ func parseVerifierConfig(verifierConfig config.VerifierConfig, namespace string) return nil, re.ErrorCodeConfigInvalid.NewError(re.Verifier, verifierName, re.EmptyLink, err, fmt.Sprintf("failed to unmarshal to notationPluginVerifierConfig from: %+v.", verifierConfig), re.HideStackTrace) } - // append namespace to uniquely identify the certstore - if len(conf.VerificationCertStores) > 0 { - logger.GetLogger(context.Background(), logOpt).Debugf("VerificationCertStores is not empty, will append namespace %v to certificate store if resource does not already contain a namespace", namespace) - conf.VerificationCertStores, err = prependNamespaceToCertStore(conf.VerificationCertStores, namespace) - if err != nil { - return nil, err - } - } - defaultCertsDir := paths.Join(homedir.Get(), ratifyconfig.ConfigFileDir, defaultCertPath) conf.VerificationCerts = append(conf.VerificationCerts, defaultCertsDir) return conf, nil @@ -219,20 +208,3 @@ func parseVerifierConfig(verifierConfig config.VerifierConfig, namespace string) func (v *notationPluginVerifier) GetNestedReferences() []string { return []string{} } - -// append namespace to certStore so they are uniquely identifiable -func prependNamespaceToCertStore(verificationCertStore map[string][]string, namespace string) (map[string][]string, error) { - // TODO: once we support multi-tenancy, empty namespace would be reserved for cluster scope. - if namespace == "" { - return nil, re.ErrorCodeEnvNotSet.WithComponentType(re.Verifier).WithDetail("failure to parse VerificationCertStores, namespace for VerificationCertStores must be provided") - } - - for _, certStores := range verificationCertStore { - for i, certstore := range certStores { - if !vu.IsNamespacedNamed(certstore) { - certStores[i] = namespace + constants.NamespaceSeperator + certstore - } - } - } - return verificationCertStore, nil -} diff --git a/pkg/verifier/notation/notation_test.go b/pkg/verifier/notation/notation_test.go index e8f155fd22..876e1da74b 100644 --- a/pkg/verifier/notation/notation_test.go +++ b/pkg/verifier/notation/notation_test.go @@ -257,8 +257,8 @@ func TestParseVerifierConfig(t *testing.T) { Name: test, VerificationCerts: []string{testPath, defaultCertDir}, VerificationCertStores: map[string][]string{ - "certstore1": {"defaultns/akv1", "testns/akv2"}, - "certstore2": {"testns/akv3", "testns/akv4"}, + "certstore1": {"defaultns/akv1", "akv2"}, + "certstore2": {"akv3", "akv4"}, }, }, }, diff --git a/pkg/verifier/notation/truststore.go b/pkg/verifier/notation/truststore.go index bdf3ad6e71..c0e3d7b72f 100644 --- a/pkg/verifier/notation/truststore.go +++ b/pkg/verifier/notation/truststore.go @@ -22,7 +22,7 @@ import ( "fmt" "github.com/deislabs/ratify/internal/logger" - cutils "github.com/deislabs/ratify/pkg/controllers/utils" + "github.com/deislabs/ratify/pkg/controllers" "github.com/deislabs/ratify/pkg/keymanagementprovider" "github.com/deislabs/ratify/pkg/utils" "github.com/notaryproject/notation-go/verifier/truststore" @@ -42,14 +42,14 @@ type trustStore struct { // will be loaded for each signature verification. // And this API must follow the Notation Trust Store spec: https://github.com/notaryproject/notaryproject/blob/main/specs/trust-store-trust-policy.md#trust-store func (s trustStore) GetCertificates(ctx context.Context, _ truststore.Type, namedStore string) ([]*x509.Certificate, error) { - certs, err := s.getCertificatesInternal(ctx, namedStore, cutils.GetCertificatesMap(ctx)) + certs, err := s.getCertificatesInternal(ctx, namedStore) if err != nil { return nil, err } return s.filterValidCerts(certs) } -func (s trustStore) getCertificatesInternal(ctx context.Context, namedStore string, certificatesMap map[string][]*x509.Certificate) ([]*x509.Certificate, error) { +func (s trustStore) getCertificatesInternal(ctx context.Context, namedStore string) ([]*x509.Certificate, error) { certs := make([]*x509.Certificate, 0) // certs configured for this namedStore overrides cert path @@ -63,7 +63,7 @@ func (s trustStore) getCertificatesInternal(ctx context.Context, namedStore stri // check certificate store if key management provider does not have certificates. // NOTE: certificate store and key management provider should not be configured together. // User will be warned by the controller/CLI - result = certificatesMap[certStore] + result = controllers.NamespacedCertStores.GetCertsFromStore(ctx, certStore) if len(result) == 0 { logger.GetLogger(ctx, logOpt).Warnf("no certificate fetched for Certificate Store %+v", certStore) } diff --git a/pkg/verifier/notation/truststore_test.go b/pkg/verifier/notation/truststore_test.go index e2a6ce882d..385bacae86 100644 --- a/pkg/verifier/notation/truststore_test.go +++ b/pkg/verifier/notation/truststore_test.go @@ -20,6 +20,8 @@ import ( "os" "reflect" "testing" + + "github.com/deislabs/ratify/pkg/controllers" ) const ( @@ -29,7 +31,23 @@ const ( leafCertStr = "-----BEGIN CERTIFICATE-----\nMIIC7jCCAdagAwIBAgIURNiOON+GKbFS8yFxG6aMRoMg29cwDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzAzMTAwMTEwMjlaFw0yNDAzMDkwMTEwMjlaMBkxFzAVBgNVBAMMDnJhdGlm\neS5kZWZhdWx0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUwGuWJ5\nDwspcL+7K+0XlkQ0g+sbyvfY0j0NdUmzsTPQNxsdUsbgYeidLnp0ruHKuHLq6Y9t\nEHUPF+A4S6lIi5OPhEkVxd/A5kzSX23WocJGmlew+Z/usjQdtiQ4ylYyHoHfPNrf\nrocbY21XQ3x2IM3yIo1QqSHNdCsE0UxsFI3j9XC+saIqrkr+k1SsI2AhhGRjXTke\nPNpOaJ+CRwsGz7PbnsACLbiAdOUJUGRkOlIl/p7hU2IcZUYTTGcKOFXP8DtbUJ+K\nQcBQOsfZyg36jvkpzmw/yAK00Uuc0X+5CaKfDKDw4MXvJFpRvG+Vc0mb5RB1E8py\neA6eXtUrZ5J4hQIDAQABox0wGzAZBgNVHREEEjAQgg5yYXRpZnkuZGVmYXVsdDAN\nBgkqhkiG9w0BAQsFAAOCAQEAHbiuodTJCDpCUu8tNjbww5ebTRznKZGnFmKQs5zU\no8KyCfLhR9/9zetDADwtWCQUvykFuHjx8tj41hALXXXafzkYPeTsfDmEoVWIJMQ1\nHqjbzc6bbxQAY7cC5HqM67fXYjPs1v3Uv3GZhF2EjBMqymKC+lZ/RSfktzN0iADn\nlwG9DrDibD739jBF09b3LHtdV55blN2wyB54DwMl5x0a4+bFYVj7fZzjctG4pH7T\njnBS69oxetPaqcRY7SQljJKaesiqx3CtiwVUpGTBexDtw6OIj9cWiCFT0lS3TfCh\nunfSQvVgezqE7txrFbXDQCgbl1jGagfia2ol7+IbLUR6TQ==\n-----END CERTIFICATE-----\n" ) +type mockCertStores struct { + certMap map[string][]*x509.Certificate +} + +func (m *mockCertStores) GetCertsFromStore(_ context.Context, storeName string) []*x509.Certificate { + if m.certMap == nil { + return nil + } + return m.certMap[storeName] +} + +func (m *mockCertStores) AddStore(_ string, _ []*x509.Certificate) {} + +func (m *mockCertStores) DeleteStore(_ string) {} + func TestGetCertificates_EmptyCertMap(t *testing.T) { + resetCertStore() certStore := map[string][]string{} certStore["store1"] = []string{"kv1"} certStore["store2"] = []string{"kv2"} @@ -37,13 +55,13 @@ func TestGetCertificates_EmptyCertMap(t *testing.T) { certStores: certStore, } - certificatesMap := map[string][]*x509.Certificate{} - if _, err := store.getCertificatesInternal(context.Background(), "store1", certificatesMap); err == nil { + if _, err := store.getCertificatesInternal(context.Background(), "store1"); err == nil { t.Fatalf("error expected if cert map is empty") } } func TestGetCertificates_NamedStore(t *testing.T) { + resetCertStore() certStore := map[string][]string{} certStore["store1"] = []string{"default/kv1"} certStore["store2"] = []string{"projecta/kv2"} @@ -58,9 +76,12 @@ func TestGetCertificates_NamedStore(t *testing.T) { certificatesMap := map[string][]*x509.Certificate{} certificatesMap["default/kv1"] = []*x509.Certificate{kv1Cert} certificatesMap["projecta/kv2"] = []*x509.Certificate{kv2Cert} + controllers.NamespacedCertStores = &mockCertStores{ + certMap: certificatesMap, + } // only the certificate in the specified namedStore should be returned - result, _ := store.getCertificatesInternal(context.Background(), "store1", certificatesMap) + result, _ := store.getCertificatesInternal(context.Background(), "store1") expectedLen := 1 if len(result) != expectedLen { @@ -73,6 +94,7 @@ func TestGetCertificates_NamedStore(t *testing.T) { } func TestGetCertificates_certPath(t *testing.T) { + resetCertStore() // create a temporary certificate file tmpFile, err := os.CreateTemp("", "*.pem") if err != nil { @@ -85,7 +107,7 @@ func TestGetCertificates_certPath(t *testing.T) { trustStore := &trustStore{ certPaths: []string{tmpFile.Name()}, } - certs, err := trustStore.getCertificatesInternal(context.Background(), "", nil) + certs, err := trustStore.getCertificatesInternal(context.Background(), "") if err != nil { t.Fatalf("failed to get certs: %v", err) } @@ -155,3 +177,7 @@ func getCert(certString string) *x509.Certificate { return test } + +func resetCertStore() { + controllers.NamespacedCertStores = &mockCertStores{} +} diff --git a/test/bats/base-test.bats b/test/bats/base-test.bats index 58ddae270c..00e2e7ef23 100644 --- a/test/bats/base-test.bats +++ b/test/bats/base-test.bats @@ -35,7 +35,7 @@ RATIFY_NAMESPACE=gatekeeper-system assert_success sleep 5 # validate key management provider status property shows success - run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'" + run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml | grep 'issuccess: true'" assert_success run kubectl run demo --namespace default --image=registry:5000/notation:signed assert_success @@ -87,7 +87,7 @@ RATIFY_NAMESPACE=gatekeeper-system sleep 5 # validate key management provider status property shows success - run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'" + run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml | grep 'issuccess: true'" assert_success run kubectl run demo --namespace default --image=registry:5000/notation:signed assert_success @@ -97,6 +97,7 @@ RATIFY_NAMESPACE=gatekeeper-system } @test "notation test with certs across namespace" { + skip "cluster-wide verifiers cannot access KMPs in specific namespace, need to add another test for namespaced verifiers accessing namespaced KMPs once we support multi-tenancy" teardown() { echo "cleaning up" wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo --namespace default --force --ignore-not-found=true' @@ -330,7 +331,7 @@ RATIFY_NAMESPACE=gatekeeper-system } # save the existing key management provider inline resource to restore later - run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml > kmprovider_staging.yaml" + run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml > kmprovider_staging.yaml" assert_success # configure the default template/constraint run kubectl apply -f ./library/default/template.yaml @@ -343,7 +344,7 @@ RATIFY_NAMESPACE=gatekeeper-system assert_failure # delete the existing key management provider inline resource since certificate store and key management provider cannot be used together - run kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} + run kubectl delete keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} assert_success # add the alternate certificate as an inline certificate store cat ~/.config/notation/truststore/x509/ca/alternate-cert/alternate-cert.crt | sed 's/^/ /g' >>./test/bats/tests/config/config_v1beta1_certstore_inline.yaml @@ -363,7 +364,7 @@ RATIFY_NAMESPACE=gatekeeper-system @test "validate inline key management provider" { teardown() { - wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace ${RATIFY_NAMESPACE} --ignore-not-found=true' + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete keymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace ${RATIFY_NAMESPACE} --ignore-not-found=true' wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo-alternate --namespace default --force --ignore-not-found=true' # restore the original notation verifier for other tests @@ -414,7 +415,7 @@ RATIFY_NAMESPACE=gatekeeper-system assert_success # validate key management provider status property shows success - run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'" + run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml | grep 'issuccess: true'" assert_success run kubectl run demo --namespace default --image=registry:5000/notation:signed assert_success @@ -459,7 +460,7 @@ RATIFY_NAMESPACE=gatekeeper-system @test "validate image signed by leaf cert" { teardown() { - wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace ${RATIFY_NAMESPACE} --ignore-not-found=true' + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete keymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace ${RATIFY_NAMESPACE} --ignore-not-found=true' wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo-leaf --namespace default --force --ignore-not-found=true' wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo-leaf2 --namespace default --force --ignore-not-found=true' diff --git a/test/bats/high-availability.bats b/test/bats/high-availability.bats index c45e9f0f32..cf6d246ce1 100644 --- a/test/bats/high-availability.bats +++ b/test/bats/high-availability.bats @@ -32,7 +32,7 @@ SLEEP_TIME=1 assert_success sleep 5 # validate key management provider status property shows success - run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n gatekeeper-system -o yaml | grep 'issuccess: true'" + run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml | grep 'issuccess: true'" assert_success run kubectl run demo --namespace default --image=registry:5000/notation:signed assert_success @@ -47,4 +47,4 @@ SLEEP_TIME=1 assert_success run bash -c "kubectl logs -l app.kubernetes.io/name=ratify -c ratify --tail=-1 -n gatekeeper-system | grep 'cache hit for subject registry:5000/notation'" assert_success -} \ No newline at end of file +} diff --git a/test/bats/tests/config/config_v1beta1_keymanagementprovider_inline.yaml b/test/bats/tests/config/config_v1beta1_keymanagementprovider_inline.yaml index bb5bc47cb1..b0984cbe5a 100644 --- a/test/bats/tests/config/config_v1beta1_keymanagementprovider_inline.yaml +++ b/test/bats/tests/config/config_v1beta1_keymanagementprovider_inline.yaml @@ -1,5 +1,5 @@ apiVersion: config.ratify.deislabs.io/v1beta1 -kind: NamespacedKeyManagementProvider +kind: KeyManagementProvider metadata: name: keymanagementprovider-inline spec: