From fb1701be8bb1bfa878b868fd310d0130b3a25d27 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Fri, 8 Sep 2023 19:53:06 +0200 Subject: [PATCH 01/31] refactor: validate container moved to function --- .gitignore | 3 +- .test/cosign.key | 11 +++ .test/cosign.pub | 4 ++ .test/values.yaml | 120 +++++++++++++++++++++++++++++++ cosign.pub | 4 -- cosignwebhook.go | 178 +++++++++++++++++++++++----------------------- 6 files changed, 224 insertions(+), 96 deletions(-) create mode 100644 .test/cosign.key create mode 100644 .test/cosign.pub create mode 100644 .test/values.yaml delete mode 100644 cosign.pub diff --git a/.gitignore b/.gitignore index 7ed9a2b1..77540ed1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ cosignwebhook grumpywebhook -chart/caas-values.yaml -vendor/ +chart/caas-values.yaml \ No newline at end of file diff --git a/.test/cosign.key b/.test/cosign.key new file mode 100644 index 00000000..da52fac6 --- /dev/null +++ b/.test/cosign.key @@ -0,0 +1,11 @@ +-----BEGIN ENCRYPTED SIGSTORE PRIVATE KEY----- +eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 +OCwicCI6MX0sInNhbHQiOiJPMGlvbENZcWYzdWhCUkpEL1FSbFoyL1g3ZVZqUTJ6 +Q1k0Y3dhRHVSOHhVPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 +Iiwibm9uY2UiOiJqMHF0aTBjWkljNElVOXFkQ1ZVSDRRbFBZbHJvY24zUiJ9LCJj +aXBoZXJ0ZXh0IjoiZXJwanBQMEJ2emYvcXZ4UG1YZlF5ajhteTI4c0lEeGYxUmJQ +MEdtSnJ2RW13MVZyaUYzWjBnZFdrRjl1c2lVaFMrWU5CUmRwblZjYWt6YndVakgy +UW5vUVVucWNxMldEbEJWN0FMRWMwZjNKUWVZTjRocndrWHA3WFd5RjlSb3hLeUN0 +a0diYkFlYjVhTG9LTWV6S0FiUkNnZzd3eFF5SVl3YW9BZkRNRGdtVXFRVExMRllZ +M3VKVWpLSUszVmtscEZYZC94YkRadk5vdXc9PSJ9 +-----END ENCRYPTED SIGSTORE PRIVATE KEY----- diff --git a/.test/cosign.pub b/.test/cosign.pub new file mode 100644 index 00000000..eac7f40e --- /dev/null +++ b/.test/cosign.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcev6yWq+sKUSjNx/yN994I9/u0Zv +zh30VL76D/YbtNnHejvQ7gFPdJNE5m8rYh29oPw/4Kq1Z6epGYmiqc8Txg== +-----END PUBLIC KEY----- diff --git a/.test/values.yaml b/.test/values.yaml new file mode 100644 index 00000000..c8b88945 --- /dev/null +++ b/.test/values.yaml @@ -0,0 +1,120 @@ +# Default values for cosignwebhook. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: mtr.devops.telekom.de/bbressi/cosignwebhook + pullPolicy: IfNotPresent + tag: dev + +imagePullSecrets: [] +logLevel: debug + +nameOverride: "" +fullnameOverride: "" + +# configuration admission controller +# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/ +admission: + failurePolicy: Fail # or Ignore to allowed to continue in case of errors + sideEffects: None # for out-of-band changes + # name of the webhook + webhook: + name: webhook.example.com + # list of excluded namespaces, comma-separated + # exclude: default, kube-system, cattle-system + +podAnnotations: {} + +# minimal permissions for pod +podSecurityContext: + fsGroup: 1000 + supplementalGroups: + - 1000 + +# minimal permissions for container +securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + runAsUser: 1000 + runAsGroup: 1000 + +# service for Webhook endpoint and Monitoring +service: + type: ClusterIP + monitorPort: 80 + webhookPort: 443 + # targetPort and metricPort are static in the cosignwebhook app + # configurable options for later + targetPort: 8080 + metricPort: 8081 + +serviceMonitor: + enabled: false + +networkPolicy: + enabled: false + +# install a Grafana Dashboard (optional on a specific namespace) +grafanaDashboard: + enabled: false +# namespace: cattle-dashboards + +# resources for InitContainer +initresources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + +# resources for Container +resources: + limits: + memory: 250Mi + cpu: 500m + requests: + memory: 64Mi + cpu: 300m + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# verify all images before usage +# sccosign: verify the cosign cli image itself +# scwebhook: verify the cosign webhook image +cosign: + image: + repository: ghcr.io/sigstore/cosign/cosign + tag: v2.0.0 + pullPolicy: IfNotPresent + sccosign: + key: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhyQCx0E9wQWSFI9ULGwy3BuRklnt + IqozONbbdbqz11hlRJy9c7SG+hdcFl9jE9uE/dwtuwU2MqU9T/cN0YkWww== + -----END PUBLIC KEY----- + scwebhook: + key: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcev6yWq+sKUSjNx/yN994I9/u0Zv + zh30VL76D/YbtNnHejvQ7gFPdJNE5m8rYh29oPw/4Kq1Z6epGYmiqc8Txg== + -----END PUBLIC KEY----- + diff --git a/cosign.pub b/cosign.pub deleted file mode 100644 index 361c05e9..00000000 --- a/cosign.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENDN3HpXY2weMYRuuJbZnNczrOyns -ZvVnR15G9EILCH8+elXkYy+4U70mR++XIL0iD8NhZ3kxfpFjxyHlnG5Snw== ------END PUBLIC KEY----- diff --git a/cosignwebhook.go b/cosignwebhook.go index 2f3b6e85..b2807880 100644 --- a/cosignwebhook.go +++ b/cosignwebhook.go @@ -5,7 +5,9 @@ import ( "crypto" "crypto/ecdsa" "encoding/json" + "errors" "fmt" + "github.com/google/go-containerregistry/pkg/authn" "io" "k8s.io/apimachinery/pkg/types" "net/http" @@ -43,6 +45,7 @@ const ( // generate-certs.sh --service cosignwebhook --webhook cosignwebhook --namespace cosignwebhook --secret cosignwebhook type CosignServerHandler struct { cs kubernetes.Interface + kc authn.Keychain } func NewCosignServerHandler() *CosignServerHandler { @@ -70,9 +73,10 @@ func restClient() (*kubernetes.Clientset, error) { return k8sclientset, err } -func recordEvent(pod *corev1.Pod, k8sclientset *kubernetes.Clientset) { +// recordEvent creates an image verified event for the pod +func (csh *CosignServerHandler) recordEvent(pod *corev1.Pod) { eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: k8sclientset.CoreV1().Events("")}) + eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: csh.cs.CoreV1().Events("")}) eventRecorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "Cosignwebhook"}) eventRecorder.Eventf(pod, corev1.EventTypeNormal, "Cosignwebhook", "Cosign image verified") eventBroadcaster.Shutdown() @@ -181,111 +185,105 @@ func (csh *CosignServerHandler) serve(w http.ResponseWriter, r *http.Request) { return } - noPubKeyCount := 0 - for i, _ := range pod.Spec.Containers { - log.Debugf("Inspecting container #%d: %s: ", i, pod.Spec.Containers[i].Name) - // Get public key from environment var - pubKey, err := csh.getPubKeyFromEnv(pod, i) - if err != nil { - log.Debugf("Could not get public key from environment variable in %s/%s/%s: %v. Trying to get public key from secret", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) - } - - // If no public key get here, try to load default secret - if len(pubKey) == 0 { - pubKey, err = csh.getSecretValue(pod.Namespace, "cosignwebhook", cosignEnvVar) - if err != nil { - log.Debugf("Could not get public key from secret in %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) - } - } + imagePullSecrets := make([]string, 0, len(pod.Spec.ImagePullSecrets)) + for _, s := range pod.Spec.ImagePullSecrets { + imagePullSecrets = append(imagePullSecrets, s.Name) + } + opt := k8schain.Options{ + Namespace: pod.Namespace, + ServiceAccountName: pod.Spec.ServiceAccountName, + ImagePullSecrets: imagePullSecrets, + } - // Still no public key, we don't care. Otherwise, POD won't start if we return with 403 - if len(pubKey) == 0 { - noPubKeyCount++ - continue - } + ctx := r.Context() + kc, err := k8schain.New(ctx, csh.cs, opt) + if err != nil { + log.Errorf("Error intializing k8schain %s/%s: %v", pod.Namespace, pod.Name, err) + http.Error(w, "Failed initializing k8schain", http.StatusInternalServerError) + return + } + csh.kc = kc - // Lookup image name of current container - image := pod.Spec.Containers[i].Image - refImage, err := name.ParseReference(image) + for i, _ := range pod.Spec.Containers { + err = csh.verifyPodContainer(pod, i) if err != nil { - log.Errorf("Error ParseRef image: %v", err) - deny(w, "Cosign ParseRef image failed", arRequest.Request.UID) + log.Errorf("Error verifyPodContainer %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) + deny(w, err.Error(), arRequest.Request.UID) return } + } - // Lookup imagePullSecrets for reviewed POD - imagePullSecrets := make([]string, 0, len(pod.Spec.ImagePullSecrets)) - for _, s := range pod.Spec.ImagePullSecrets { - imagePullSecrets = append(imagePullSecrets, s.Name) - } - opt := k8schain.Options{ - Namespace: pod.Namespace, - ServiceAccountName: pod.Spec.ServiceAccountName, - ImagePullSecrets: imagePullSecrets, - } + accept(w, "Image signature(s) verified", arRequest.Request.UID) +} - // Encrypt public key - publicKey, err := cryptoutils.UnmarshalPEMToPublicKey([]byte(pubKey)) - if err != nil { - log.Errorf("Error UnmarshalPEMToPublicKey %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) - deny(w, "Public key malformed", arRequest.Request.UID) - return - } +// verifyPodContainer verifies the signature of the nth container of the pod +func (csh *CosignServerHandler) verifyPodContainer(p *corev1.Pod, n int) error { - // Load public key to verify - cosignLoadKey, err := signature.LoadECDSAVerifier(publicKey.(*ecdsa.PublicKey), crypto.SHA256) - if err != nil { - log.Errorf("Error LoadECDSAVerifier %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) - deny(w, "Failed creating key verifier", arRequest.Request.UID) - return - } + log.Debugf("Inspecting container %s/%s/%s", p.Namespace, p.Name, p.Spec.Containers[n].Name) + // Get public key from environment var + pubKey, err := csh.getPubKeyFromEnv(p, n) + if err != nil { + log.Debugf("Could not get public key from environment variable in %s/%s/%s: %v. Trying to get public key from secret", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) + } - // Kubernetes client to operate in cluster - kc, err := k8schain.NewInCluster(context.Background(), opt) + // If no public key get here, try to load default secret + if len(pubKey) == 0 { + pubKey, err = csh.getSecretValue(p.Namespace, "cosignwebhook", cosignEnvVar) if err != nil { - log.Errorf("Error k8schain %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) - deny(w, "Failed initializing in-cluster client", arRequest.Request.UID) - return + log.Debugf("Could not get public key from secret in %s/%s/%s: %v", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) } + } - // Verify signature on remote image with the presented public key - remoteOpts := []ociremote.Option{ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(kc))} - _, _, err = cosign.VerifyImageSignatures( - context.Background(), - refImage, - &cosign.CheckOpts{ - RegistryClientOpts: remoteOpts, - SigVerifier: cosignLoadKey, - IgnoreSCT: true, - IgnoreTlog: true, - }) - - // Verify Image failed, needs to reject pod start - if err != nil { - log.Errorf("Error VerifyImageSignatures %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) - deny(w, "Signature verification failed", arRequest.Request.UID) - return - } + // Still no public key, we don't care. Otherwise, POD won't start if we return with 403 + if len(pubKey) == 0 { + return nil + } - // count successful verifies for prometheus metric - verifiedProcessed.Inc() - log.Infof("Image verified successfully: %s/%s/%s", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name) - // Just another K8S client to record events - clientset, err := restClient() - if err != nil { - log.Errorf("Can't init rest client for event recorder: %v", err) - } else { - recordEvent(pod, clientset) - } + // Lookup image name of current container + image := p.Spec.Containers[n].Image + refImage, err := name.ParseReference(image) + if err != nil { + log.Errorf("Error ParseRef image: %v", err) + return errors.New("cosign ParseRef image failed") } - if noPubKeyCount == len(pod.Spec.Containers) { - log.Debugf("No public key found for %s/%s, skipping verification", pod.Namespace, pod.Name) - accept(w, "No image signature verification possible", arRequest.Request.UID) - return + // Encrypt public key + publicKey, err := cryptoutils.UnmarshalPEMToPublicKey([]byte(pubKey)) + if err != nil { + log.Errorf("Error UnmarshalPEMToPublicKey %s/%s/%s: %v", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) + return errors.New("public key malformed") } - accept(w, "Image signature(s) verified", arRequest.Request.UID) + // Load public key to verify + cosignLoadKey, err := signature.LoadECDSAVerifier(publicKey.(*ecdsa.PublicKey), crypto.SHA256) + if err != nil { + log.Errorf("Error LoadECDSAVerifier %s/%s/%s: %v", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) + return errors.New("failed creating key verifier") + } + + // Verify signature on remote image with the presented public key + remoteOpts := []ociremote.Option{ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(csh.kc))} + _, _, err = cosign.VerifyImageSignatures( + context.Background(), + refImage, + &cosign.CheckOpts{ + RegistryClientOpts: remoteOpts, + SigVerifier: cosignLoadKey, + IgnoreSCT: true, + IgnoreTlog: true, + }) + + // Verify Image failed, needs to reject pod start + if err != nil { + log.Errorf("Error VerifyImageSignatures %s/%s/%s: %v", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) + return errors.New("signature verification failed") + } + + // count successful verifies for prometheus metric + verifiedProcessed.Inc() + log.Infof("Image verified successfully: %s/%s/%s", p.Namespace, p.Name, p.Spec.Containers[n].Name) + csh.recordEvent(p) + return nil } // deny stops the pod from starting From 2850a683ae6473a965fc647bbf9c3d2a2fa7d97f Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Fri, 8 Sep 2023 19:54:45 +0200 Subject: [PATCH 02/31] chore: removed .test folder --- .test/cosign.key | 11 ----- .test/cosign.pub | 4 -- .test/values.yaml | 120 ---------------------------------------------- 3 files changed, 135 deletions(-) delete mode 100644 .test/cosign.key delete mode 100644 .test/cosign.pub delete mode 100644 .test/values.yaml diff --git a/.test/cosign.key b/.test/cosign.key deleted file mode 100644 index da52fac6..00000000 --- a/.test/cosign.key +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN ENCRYPTED SIGSTORE PRIVATE KEY----- -eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 -OCwicCI6MX0sInNhbHQiOiJPMGlvbENZcWYzdWhCUkpEL1FSbFoyL1g3ZVZqUTJ6 -Q1k0Y3dhRHVSOHhVPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 -Iiwibm9uY2UiOiJqMHF0aTBjWkljNElVOXFkQ1ZVSDRRbFBZbHJvY24zUiJ9LCJj -aXBoZXJ0ZXh0IjoiZXJwanBQMEJ2emYvcXZ4UG1YZlF5ajhteTI4c0lEeGYxUmJQ -MEdtSnJ2RW13MVZyaUYzWjBnZFdrRjl1c2lVaFMrWU5CUmRwblZjYWt6YndVakgy -UW5vUVVucWNxMldEbEJWN0FMRWMwZjNKUWVZTjRocndrWHA3WFd5RjlSb3hLeUN0 -a0diYkFlYjVhTG9LTWV6S0FiUkNnZzd3eFF5SVl3YW9BZkRNRGdtVXFRVExMRllZ -M3VKVWpLSUszVmtscEZYZC94YkRadk5vdXc9PSJ9 ------END ENCRYPTED SIGSTORE PRIVATE KEY----- diff --git a/.test/cosign.pub b/.test/cosign.pub deleted file mode 100644 index eac7f40e..00000000 --- a/.test/cosign.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcev6yWq+sKUSjNx/yN994I9/u0Zv -zh30VL76D/YbtNnHejvQ7gFPdJNE5m8rYh29oPw/4Kq1Z6epGYmiqc8Txg== ------END PUBLIC KEY----- diff --git a/.test/values.yaml b/.test/values.yaml deleted file mode 100644 index c8b88945..00000000 --- a/.test/values.yaml +++ /dev/null @@ -1,120 +0,0 @@ -# Default values for cosignwebhook. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: mtr.devops.telekom.de/bbressi/cosignwebhook - pullPolicy: IfNotPresent - tag: dev - -imagePullSecrets: [] -logLevel: debug - -nameOverride: "" -fullnameOverride: "" - -# configuration admission controller -# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/ -admission: - failurePolicy: Fail # or Ignore to allowed to continue in case of errors - sideEffects: None # for out-of-band changes - # name of the webhook - webhook: - name: webhook.example.com - # list of excluded namespaces, comma-separated - # exclude: default, kube-system, cattle-system - -podAnnotations: {} - -# minimal permissions for pod -podSecurityContext: - fsGroup: 1000 - supplementalGroups: - - 1000 - -# minimal permissions for container -securityContext: - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - runAsUser: 1000 - runAsGroup: 1000 - -# service for Webhook endpoint and Monitoring -service: - type: ClusterIP - monitorPort: 80 - webhookPort: 443 - # targetPort and metricPort are static in the cosignwebhook app - # configurable options for later - targetPort: 8080 - metricPort: 8081 - -serviceMonitor: - enabled: false - -networkPolicy: - enabled: false - -# install a Grafana Dashboard (optional on a specific namespace) -grafanaDashboard: - enabled: false -# namespace: cattle-dashboards - -# resources for InitContainer -initresources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - -# resources for Container -resources: - limits: - memory: 250Mi - cpu: 500m - requests: - memory: 64Mi - cpu: 300m - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -# verify all images before usage -# sccosign: verify the cosign cli image itself -# scwebhook: verify the cosign webhook image -cosign: - image: - repository: ghcr.io/sigstore/cosign/cosign - tag: v2.0.0 - pullPolicy: IfNotPresent - sccosign: - key: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhyQCx0E9wQWSFI9ULGwy3BuRklnt - IqozONbbdbqz11hlRJy9c7SG+hdcFl9jE9uE/dwtuwU2MqU9T/cN0YkWww== - -----END PUBLIC KEY----- - scwebhook: - key: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcev6yWq+sKUSjNx/yN994I9/u0Zv - zh30VL76D/YbtNnHejvQ7gFPdJNE5m8rYh29oPw/4Kq1Z6epGYmiqc8Txg== - -----END PUBLIC KEY----- - From e78a52a1a105ad706f3811c379208eb052a3b7e3 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Sun, 10 Sep 2023 20:10:23 +0200 Subject: [PATCH 03/31] chore: simplification of dockerfile --- Dockerfile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 935cec0b..e12cfbe8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,9 @@ FROM golang:1.21 AS build-env WORKDIR /app COPY . /app -RUN useradd -u 10001 webhook -RUN go mod tidy -RUN go mod vendor -RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o cosignwebhook +RUN useradd -u 10001 webhook && \ + go mod tidy && \ + CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o cosignwebhook FROM alpine:latest COPY --from=build-env /app/cosignwebhook /cosignwebhook From 874fff69a2b42916df51b8faac41911508ba7b08 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Sun, 10 Sep 2023 20:11:10 +0200 Subject: [PATCH 04/31] feat: makefile & testcases --- Makefile | 13 +++++++++++++ test/cases/fail-1.yaml | 26 ++++++++++++++++++++++++++ test/cases/fail-2.yaml | 36 ++++++++++++++++++++++++++++++++++++ test/cases/ok-1.yaml | 27 +++++++++++++++++++++++++++ test/cases/ok-2.yaml | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 Makefile create mode 100644 test/cases/fail-1.yaml create mode 100644 test/cases/fail-2.yaml create mode 100644 test/cases/ok-1.yaml create mode 100644 test/cases/ok-2.yaml diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..9be25cf6 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +test-cluster: + @echo "Creating registry..." + @k3d registry create registry.localhost --port 5000 + @echo "Adding registry to cluster..." + @k3d cluster create cosign-tests --registry-use k3d-registry.localhost:5000 + +test-image: + @echo "Building test image..." + @docker build -t k3d-registry.localhost:5000/cosignwebhook:dev . + @echo "Pushing test image..." + @docker push k3d-registry.localhost:5000/cosignwebhook:dev + @echo "Signing test image..." + @cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev \ No newline at end of file diff --git a/test/cases/fail-1.yaml b/test/cases/fail-1.yaml new file mode 100644 index 00000000..f574df1d --- /dev/null +++ b/test/cases/fail-1.yaml @@ -0,0 +1,26 @@ +# one container with malformed public key in environment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-fail-1 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee + command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + badKey666 + -----END PUBLIC KEY----- diff --git a/test/cases/fail-2.yaml b/test/cases/fail-2.yaml new file mode 100644 index 00000000..72c83b59 --- /dev/null +++ b/test/cases/fail-2.yaml @@ -0,0 +1,36 @@ +# two containers, the second one has a malformed pub key +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-fail-2 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee + command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D + PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== + -----END PUBLIC KEY----- + - name: busybox-container2 + image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee + command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + badKeyagain! + -----END PUBLIC KEY----- diff --git a/test/cases/ok-1.yaml b/test/cases/ok-1.yaml new file mode 100644 index 00000000..597cfa4f --- /dev/null +++ b/test/cases/ok-1.yaml @@ -0,0 +1,27 @@ +# one container with its public key in environment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-ok-1 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee + command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D + PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== + -----END PUBLIC KEY----- diff --git a/test/cases/ok-2.yaml b/test/cases/ok-2.yaml new file mode 100644 index 00000000..d45a875d --- /dev/null +++ b/test/cases/ok-2.yaml @@ -0,0 +1,37 @@ +# two containers with their public keys in environment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-ok-2 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee + command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D + PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== + -----END PUBLIC KEY----- + - name: busybox-container2 + image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee + command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D + PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== + -----END PUBLIC KEY----- From 3270fcb97329c7a8765a345154523c810728a9ed Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Sun, 10 Sep 2023 20:11:51 +0200 Subject: [PATCH 05/31] refactor: verification of container image only --- cosignwebhook.go | 86 ++++++++++++++------------- cosignwebhook_test.go | 133 +++++++++++++++++++----------------------- 2 files changed, 103 insertions(+), 116 deletions(-) diff --git a/cosignwebhook.go b/cosignwebhook.go index b2807880..bd3644f3 100644 --- a/cosignwebhook.go +++ b/cosignwebhook.go @@ -73,16 +73,16 @@ func restClient() (*kubernetes.Clientset, error) { return k8sclientset, err } -// recordEvent creates an image verified event for the pod -func (csh *CosignServerHandler) recordEvent(pod *corev1.Pod) { +// recordEvent creates an image verified event for the container +func (csh *CosignServerHandler) recordEvent(p *corev1.Pod) { eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: csh.cs.CoreV1().Events("")}) eventRecorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "Cosignwebhook"}) - eventRecorder.Eventf(pod, corev1.EventTypeNormal, "Cosignwebhook", "Cosign image verified") + eventRecorder.Eventf(p, corev1.EventTypeNormal, "Cosignwebhook", "Cosign image verified") eventBroadcaster.Shutdown() } -// get pod object from admission request +// get container object from admission request func getPod(byte []byte) (*corev1.Pod, *v1.AdmissionReview, error) { arRequest := v1.AdmissionReview{} if err := json.Unmarshal(byte, &arRequest); err != nil { @@ -96,33 +96,32 @@ func getPod(byte []byte) (*corev1.Pod, *v1.AdmissionReview, error) { raw := arRequest.Request.Object.Raw pod := corev1.Pod{} if err := json.Unmarshal(raw, &pod); err != nil { - log.Error("Error deserializing pod") + log.Error("Error deserializing container") return nil, nil, err } return &pod, &arRequest, nil } -// getPubKeyFromEnv procures the public key from the pod's nth container, if present. +// getPubKeyFromEnv procures the public key from the container's nth container, if present. // Else it returns an empty string and an error. -func (csh *CosignServerHandler) getPubKeyFromEnv(pod *corev1.Pod, n int) (string, error) { - for i := 0; i < len(pod.Spec.Containers[n].Env); i++ { - if pod.Spec.Containers[n].Env[i].Name == cosignEnvVar { - - if len(pod.Spec.Containers[n].Env[i].Value) != 0 { - log.Debugf("Found public key in env var %s/%s", pod.Namespace, pod.Name) - return pod.Spec.Containers[n].Env[i].Value, nil +func (csh *CosignServerHandler) getPubKeyFromEnv(c *corev1.Container, ns string) (string, error) { + for _, envVar := range c.Env { + if envVar.Name == cosignEnvVar { + if len(envVar.Value) != 0 { + log.Debugf("Found public key in env var for container %q", c.Name) + return envVar.Value, nil } - if pod.Spec.Containers[n].Env[i].ValueFrom.SecretKeyRef != nil { - log.Debugf("Found public key in secret of %s/%s/%s", pod.Namespace, pod.Name, pod.Spec.Containers[n].Name) - return csh.getSecretValue(pod.Namespace, - pod.Spec.Containers[n].Env[i].ValueFrom.SecretKeyRef.Name, - pod.Spec.Containers[n].Env[i].ValueFrom.SecretKeyRef.Key, + if envVar.ValueFrom.SecretKeyRef != nil { + log.Debugf("Found reference to public key in secret %q for container %q", envVar.ValueFrom.SecretKeyRef.Name, c.Name) + return csh.getSecretValue(ns, + envVar.ValueFrom.SecretKeyRef.Name, + envVar.ValueFrom.SecretKeyRef.Key, ) } } } - return "", fmt.Errorf("no env var found in %s/%s/%s", pod.Namespace, pod.Name, pod.Spec.Containers[n].Name) + return "", fmt.Errorf("no env var found in container %q in namespace %q", c.Name, ns) } // getSecretValue returns the value of passed key for the secret with passed name in passed namespace @@ -204,65 +203,69 @@ func (csh *CosignServerHandler) serve(w http.ResponseWriter, r *http.Request) { } csh.kc = kc - for i, _ := range pod.Spec.Containers { - err = csh.verifyPodContainer(pod, i) + for i, c := range pod.Spec.Containers { + err = csh.verifyContainer(&c, pod.Namespace) if err != nil { - log.Errorf("Error verifyPodContainer %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) + log.Errorf("Error verifyContainer %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) deny(w, err.Error(), arRequest.Request.UID) return } } accept(w, "Image signature(s) verified", arRequest.Request.UID) + csh.recordEvent(pod) } -// verifyPodContainer verifies the signature of the nth container of the pod -func (csh *CosignServerHandler) verifyPodContainer(p *corev1.Pod, n int) error { +// verifyContainer verifies the signature of the container image +func (csh *CosignServerHandler) verifyContainer(c *corev1.Container, ns string) error { - log.Debugf("Inspecting container %s/%s/%s", p.Namespace, p.Name, p.Spec.Containers[n].Name) + log.Debugf("Inspecting container %q in namespace %q", ns, c.Name) // Get public key from environment var - pubKey, err := csh.getPubKeyFromEnv(p, n) + pubKey, err := csh.getPubKeyFromEnv(c, ns) if err != nil { - log.Debugf("Could not get public key from environment variable in %s/%s/%s: %v. Trying to get public key from secret", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) + log.Debugf("Could not find pub key in container's %q environment: %v", c.Name, err) } // If no public key get here, try to load default secret if len(pubKey) == 0 { - pubKey, err = csh.getSecretValue(p.Namespace, "cosignwebhook", cosignEnvVar) + pubKey, err = csh.getSecretValue(ns, "cosignwebhook", cosignEnvVar) if err != nil { - log.Debugf("Could not get public key from secret in %s/%s/%s: %v", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) + log.Debugf("Could not find pub key from default secret: %v", err) } } // Still no public key, we don't care. Otherwise, POD won't start if we return with 403 + // In future versions this should block the start of the container if len(pubKey) == 0 { + log.Debugf("No public key found, returning") return nil } // Lookup image name of current container - image := p.Spec.Containers[n].Image + image := c.Image refImage, err := name.ParseReference(image) if err != nil { - log.Errorf("Error ParseRef image: %v", err) - return errors.New("cosign ParseRef image failed") + log.Errorf("Error parsing image reference: %v", err) + return fmt.Errorf("could parse image reference for image %q", image) } // Encrypt public key publicKey, err := cryptoutils.UnmarshalPEMToPublicKey([]byte(pubKey)) if err != nil { - log.Errorf("Error UnmarshalPEMToPublicKey %s/%s/%s: %v", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) - return errors.New("public key malformed") + log.Errorf("Error unmarshalling public key: %v", err) + return fmt.Errorf("public key for image %q malformed", image) } // Load public key to verify cosignLoadKey, err := signature.LoadECDSAVerifier(publicKey.(*ecdsa.PublicKey), crypto.SHA256) if err != nil { - log.Errorf("Error LoadECDSAVerifier %s/%s/%s: %v", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) + log.Errorf("Error loading ECDSA verifier: %v", err) return errors.New("failed creating key verifier") } // Verify signature on remote image with the presented public key remoteOpts := []ociremote.Option{ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(csh.kc))} + log.Debugf("Verifying image %q with public key %q", image, pubKey) _, _, err = cosign.VerifyImageSignatures( context.Background(), refImage, @@ -273,20 +276,19 @@ func (csh *CosignServerHandler) verifyPodContainer(p *corev1.Pod, n int) error { IgnoreTlog: true, }) - // Verify Image failed, needs to reject pod start + // Verify Image failed, needs to reject container start if err != nil { - log.Errorf("Error VerifyImageSignatures %s/%s/%s: %v", p.Namespace, p.Name, p.Spec.Containers[n].Name, err) - return errors.New("signature verification failed") + log.Errorf("Error verifying signature: %v", err) + return fmt.Errorf("signature for %q couldn't be verified", image) } // count successful verifies for prometheus metric verifiedProcessed.Inc() - log.Infof("Image verified successfully: %s/%s/%s", p.Namespace, p.Name, p.Spec.Containers[n].Name) - csh.recordEvent(p) + log.Infof("Image %q verified successfully", image) return nil } -// deny stops the pod from starting +// deny stops the container from starting func deny(w http.ResponseWriter, msg string, uid types.UID) { resp, err := json.Marshal(admissionReview(403, false, "Failure", msg, uid)) if err != nil { @@ -299,7 +301,7 @@ func deny(w http.ResponseWriter, msg string, uid types.UID) { } } -// accept allows the pod to start +// accept allows the container to start func accept(w http.ResponseWriter, msg string, uid types.UID) { resp, err := json.Marshal(admissionReview(200, true, "Success", msg, uid)) if err != nil { diff --git a/cosignwebhook_test.go b/cosignwebhook_test.go index 4f5701c5..fc4a5e12 100644 --- a/cosignwebhook_test.go +++ b/cosignwebhook_test.go @@ -12,28 +12,18 @@ func Test_getPubKeyFromEnv(t *testing.T) { tests := []struct { name string - pod *corev1.Pod + container *corev1.Container secretPresent bool want string wantErr bool }{ { name: "public key from environment variable", - pod: &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-pod", - Namespace: "test", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Env: []corev1.EnvVar{ - { - Name: cosignEnvVar, - Value: "secret", - }, - }, - }, + container: &corev1.Container{ + Env: []corev1.EnvVar{ + { + Name: cosignEnvVar, + Value: "secret", }, }, }, @@ -43,25 +33,15 @@ func Test_getPubKeyFromEnv(t *testing.T) { }, { name: "public key from referenced secret", - pod: &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-pod", - Namespace: "test", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Env: []corev1.EnvVar{ - { - Name: cosignEnvVar, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - Key: cosignEnvVar, - LocalObjectReference: corev1.LocalObjectReference{ - Name: "cosign-pubkey", - }, - }, - }, + container: &corev1.Container{ + Env: []corev1.EnvVar{ + { + Name: cosignEnvVar, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: cosignEnvVar, + LocalObjectReference: corev1.LocalObjectReference{ + Name: "cosign-pubkey", }, }, }, @@ -74,25 +54,15 @@ func Test_getPubKeyFromEnv(t *testing.T) { }, { name: "public key from referenced secret with wrong key", - pod: &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-pod", - Namespace: "test", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Env: []corev1.EnvVar{ - { - Name: cosignEnvVar, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - Key: "wrong-key", - LocalObjectReference: corev1.LocalObjectReference{ - Name: "cosign-pubkey", - }, - }, - }, + container: &corev1.Container{ + Env: []corev1.EnvVar{ + { + Name: cosignEnvVar, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: "wrong-key", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "cosign-pubkey", }, }, }, @@ -105,25 +75,15 @@ func Test_getPubKeyFromEnv(t *testing.T) { }, { name: "public key from referenced non-existing secret", - pod: &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-pod", - Namespace: "test", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Env: []corev1.EnvVar{ - { - Name: cosignEnvVar, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - Key: cosignEnvVar, - LocalObjectReference: corev1.LocalObjectReference{ - Name: "non-existing-secret", - }, - }, - }, + container: &corev1.Container{ + Env: []corev1.EnvVar{ + { + Name: cosignEnvVar, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: cosignEnvVar, + LocalObjectReference: corev1.LocalObjectReference{ + Name: "non-existing-secret", }, }, }, @@ -157,7 +117,7 @@ func Test_getPubKeyFromEnv(t *testing.T) { cs: c, } - got, err := chs.getPubKeyFromEnv(tt.pod, 0) + got, err := chs.getPubKeyFromEnv(tt.container, "test") if (err != nil) != tt.wantErr { t.Errorf("getPubKeyFromEnv() error = %v, wantErr %v", err, tt.wantErr) return @@ -168,3 +128,28 @@ func Test_getPubKeyFromEnv(t *testing.T) { }) } } + +func TestCosignServerHandler_verifyPodContainer(t *testing.T) { + + //tests := []struct { + // name string + // pod *corev1.Pod + // wantErr bool + //}{ + // { + // name: "1 container, signed image, pub key present", + // }, + //} + // + //for _, tt := range tests { + // t.Run(tt.name, func(t *testing.T) { + // c := fake.NewSimpleClientset() + // csh := &CosignServerHandler{ + // cs: c, + // } + // if err := csh.verifyContainer(tt.); (err != nil) != tt.wantErr { + // t.Errorf("verifyContainer() error = %v, wantErr %v", err, tt.wantErr) + // } + // }) + //} +} From 28b1ff0db0f903ce4313fd428fcf908576376149 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Wed, 13 Sep 2023 22:17:45 +0200 Subject: [PATCH 06/31] feat: added support for init containers --- cosignwebhook.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/cosignwebhook.go b/cosignwebhook.go index bd3644f3..39e851e8 100644 --- a/cosignwebhook.go +++ b/cosignwebhook.go @@ -11,6 +11,7 @@ import ( "io" "k8s.io/apimachinery/pkg/types" "net/http" + "os" "time" log "github.com/gookit/slog" @@ -77,8 +78,8 @@ func restClient() (*kubernetes.Clientset, error) { func (csh *CosignServerHandler) recordEvent(p *corev1.Pod) { eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: csh.cs.CoreV1().Events("")}) - eventRecorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "Cosignwebhook"}) - eventRecorder.Eventf(p, corev1.EventTypeNormal, "Cosignwebhook", "Cosign image verified") + eventRecorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "Cosignwebhook", Host: os.Getenv("HOSTNAME")}) + eventRecorder.Eventf(p, corev1.EventTypeNormal, "PodVerified", "Signature of pod's images(s) verified successfully") eventBroadcaster.Shutdown() } @@ -144,7 +145,11 @@ func (csh *CosignServerHandler) getSecretValue(namespace string, name string, ke func (csh *CosignServerHandler) healthz(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte("ok")) + _, err := w.Write([]byte("ok")) + if err != nil { + log.Errorf("Can't write response: %v", err) + http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError) + } } func (csh *CosignServerHandler) serve(w http.ResponseWriter, r *http.Request) { @@ -203,10 +208,19 @@ func (csh *CosignServerHandler) serve(w http.ResponseWriter, r *http.Request) { } csh.kc = kc + for _, c := range pod.Spec.InitContainers { + err = csh.verifyContainer(&c, pod.Namespace) + if err != nil { + log.Errorf("Error verifying init container %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.InitContainers[0].Name, err) + deny(w, err.Error(), arRequest.Request.UID) + return + } + } + for i, c := range pod.Spec.Containers { err = csh.verifyContainer(&c, pod.Namespace) if err != nil { - log.Errorf("Error verifyContainer %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) + log.Errorf("Error verifying container %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err) deny(w, err.Error(), arRequest.Request.UID) return } From 25780dffcd295c0c8c8dedeeaa58b4656c42cb9d Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Wed, 13 Sep 2023 22:18:08 +0200 Subject: [PATCH 07/31] fix: fixed logging on server shutdown --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 6deb4131..24ae39bf 100644 --- a/main.go +++ b/main.go @@ -91,12 +91,12 @@ func main() { // start webhook server in new rountine go func() { if err := server.ListenAndServeTLS("", ""); err != nil { - log.Errorf("Failed to listen and serve webhook server ", err) + log.Errorf("Failed to listen and serve webhook server %v", err) } }() go func() { if err := mserver.ListenAndServe(); err != nil { - log.Errorf("Failed to listen and serve minitor server ", err) + log.Errorf("Failed to listen and serve monitor server %v", err) } }() From 85acad221abf0370ad0a8c07e60baf3238036770 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Wed, 13 Sep 2023 22:19:04 +0200 Subject: [PATCH 08/31] chore: added test-cases and first draft of e2e tests --- .gitignore | 3 +- go.mod | 71 +++++++++++- go.sum | 244 ++++++++++++++++++++++++++++++++++++++++- test/cases/fail-1.yaml | 4 +- test/cases/fail-2.yaml | 6 +- test/cases/fail-3.yaml | 26 +++++ test/cases/fail-4.yaml | 36 ++++++ test/cases/ok-1.yaml | 8 +- test/cases/ok-2.yaml | 16 +-- test/cases/ok-3.yaml | 35 ++++++ test/cases/ok-4.yaml | 45 ++++++++ test/cases/ok-5.yaml | 45 ++++++++ test/cases/ok-6.yaml | 46 ++++++++ test/e2e_test.go | 45 ++++++++ 14 files changed, 608 insertions(+), 22 deletions(-) create mode 100644 test/cases/fail-3.yaml create mode 100644 test/cases/fail-4.yaml create mode 100644 test/cases/ok-3.yaml create mode 100644 test/cases/ok-4.yaml create mode 100644 test/cases/ok-5.yaml create mode 100644 test/cases/ok-6.yaml create mode 100644 test/e2e_test.go diff --git a/.gitignore b/.gitignore index 77540ed1..e4bc9588 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ cosignwebhook grumpywebhook -chart/caas-values.yaml \ No newline at end of file +chart/caas-values.yaml +test/keys diff --git a/go.mod b/go.mod index 6be4ab69..5ee607c8 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,11 @@ require ( cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.1 // indirect + cuelang.org/go v0.5.0 // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.29 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect @@ -27,6 +31,22 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect + github.com/ThalesIgnite/crypto11 v1.2.5 // indirect + github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect + github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect + github.com/alibabacloud-go/cr-20181201 v1.0.10 // indirect + github.com/alibabacloud-go/darabonba-openapi v0.1.18 // indirect + github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect + github.com/alibabacloud-go/endpoint-util v1.1.1 // indirect + github.com/alibabacloud-go/openapi-util v0.0.11 // indirect + github.com/alibabacloud-go/tea v1.1.18 // indirect + github.com/alibabacloud-go/tea-utils v1.4.4 // indirect + github.com/alibabacloud-go/tea-xml v1.1.2 // indirect + github.com/aliyun/credentials-go v1.2.3 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.37 // indirect @@ -45,9 +65,16 @@ require ( github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230823232655-ce48fc331ac7 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect + github.com/buildkite/agent/v3 v3.49.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect + github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect + github.com/clbanning/mxj/v2 v2.5.6 // indirect + github.com/cloudflare/circl v1.3.3 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect + github.com/coreos/go-oidc/v3 v3.6.0 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20230710064741-aa7fe85c7dbd // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect @@ -58,10 +85,13 @@ require ( github.com/docker/docker v24.0.5+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/proto v1.10.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/ghodss/yaml v1.0.0 // indirect github.com/go-chi/chi v4.1.2+incompatible // indirect + github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.21.4 // indirect @@ -74,11 +104,14 @@ require ( github.com/go-openapi/strfmt v0.21.7 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/go-openapi/validate v0.22.1 // indirect + github.com/go-piv/piv-go v1.11.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.2 // indirect + github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -86,12 +119,19 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230822174451-190ad0e4d556 // indirect + github.com/google/go-github/v50 v50.2.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/s2a-go v0.1.4 // indirect github.com/google/uuid v1.3.1 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect github.com/gookit/color v1.5.4 // indirect github.com/gookit/goutil v0.6.12 // indirect github.com/gookit/gsr v0.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/in-toto/in-toto-golang v0.9.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect @@ -103,46 +143,71 @@ require ( github.com/letsencrypt/boulder v0.0.0-20230828195701-66a4c11fced9 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/manifoldco/promptui v0.9.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect + github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/open-policy-agent/opa v0.52.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc4 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pborman/uuid v1.2.1 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect + github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect + github.com/segmentio/ksuid v1.0.4 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect + github.com/sigstore/fulcio v1.3.1 // indirect github.com/sigstore/rekor v1.2.2 // indirect github.com/sigstore/timestamp-authority v1.1.2 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.16.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.1.6 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tchap/go-patricia/v2 v2.3.1 // indirect + github.com/thales-e-security/pool v0.0.2 // indirect github.com/theupdateframework/go-tuf v0.6.1 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect + github.com/tjfoc/gmsm v1.3.2 // indirect github.com/transparency-dev/merkle v0.0.2 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/vbatts/tar-split v0.11.5 // indirect + github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 // indirect + github.com/xanzy/go-gitlab v0.86.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/yashtewari/glob-intersection v0.1.0 // indirect + github.com/zeebo/errs v1.3.0 // indirect go.mongodb.org/mongo-driver v1.12.1 // indirect + go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.17.0 // indirect go.opentelemetry.io/otel/metric v1.17.0 // indirect go.opentelemetry.io/otel/trace v1.17.0 // indirect + go.step.sm/crypto v0.35.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.25.0 // indirect golang.org/x/crypto v0.12.0 // indirect @@ -155,20 +220,24 @@ require ( golang.org/x/term v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect + google.golang.org/api v0.135.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.57.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230816210353-14e408962443 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/release-utils v0.7.4 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index c619b6dc..6a14e2d3 100644 --- a/go.sum +++ b/go.sum @@ -17,7 +17,7 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= +cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -44,11 +44,15 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cuelang.org/go v0.5.0 h1:D6N0UgTGJCOxFKU8RU+qYvavKNsVc/+ZobmifStVJzU= +cuelang.org/go v0.5.0/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230618160516-e936619f9f18 h1:rd389Q26LMy03gG4anandGFC2LW/xvjga5GezeeaxQk= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230618160516-e936619f9f18/go.mod h1:fgJuSBrJP5qZtKqaMJE0hmhS2tmRH+44IkfZvjtaf1M= +github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 h1:8+4G8JaejP8Xa6W46PzJEwisNgBXMvFcz78N6zG/ARw= +github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0/go.mod h1:GgeIE+1be8Ivm7Sh4RgwI42aTtC9qrcj+Y9Y6CjJhJs= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY= @@ -61,6 +65,8 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15a github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= @@ -88,10 +94,61 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkM github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= +github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= +github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= +github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= +github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.2/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/cr-20160607 v1.0.1 h1:WEnP1iPFKJU74ryUKh/YDPHoxMZawqlPajOymyNAkts= +github.com/alibabacloud-go/cr-20160607 v1.0.1/go.mod h1:QHeKZtZ3F3FOE+/uIXCBAp8POwnUYekpLwr1dtQa5r0= +github.com/alibabacloud-go/cr-20181201 v1.0.10 h1:B60f6S1imsgn2fgC6X6FrVNrONDrbCT0NwYhsJ0C9/c= +github.com/alibabacloud-go/cr-20181201 v1.0.10/go.mod h1:VN9orB/w5G20FjytoSpZROqu9ZqxwycASmGqYUJSoDc= +github.com/alibabacloud-go/darabonba-openapi v0.1.12/go.mod h1:sTAjsFJmVsmcVeklL9d9uDBlFsgl43wZ6jhI6BHqHqU= +github.com/alibabacloud-go/darabonba-openapi v0.1.14/go.mod h1:w4CosR7O/kapCtEEMBm3JsQqWBU/CnZ2o0pHorsTWDI= +github.com/alibabacloud-go/darabonba-openapi v0.1.18 h1:3eUVmAr7WCJp7fgIvmCd9ZUyuwtJYbtUqJIed5eXCmk= +github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc= +github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 h1:NqugFkGxx1TXSh/pBcU00Y6bljgDPaFdh5MUSeJ7e50= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8= +github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.0.9/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.0.10/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.0.11 h1:iYnqOPR5hyEEnNZmebGyRMkkEJRWUEjDiiaOHZ5aNhA= +github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.1.18 h1:+6GJ06eu5Cr/Mkj09vWrf6QAfrPepctY2OxcWNclRC0= +github.com/alibabacloud-go/tea v1.1.18/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils v1.3.9/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= +github.com/alibabacloud-go/tea-utils v1.4.4 h1:lxCDvNCdTo9FaXKKq45+4vGETQUKNOW/qKTcX9Sk53o= +github.com/alibabacloud-go/tea-utils v1.4.4/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= +github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M= +github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= +github.com/aliyun/credentials-go v1.2.3 h1:Vmodnr52Rz1mcbwn0kzMhLRKb6soizewuKXdfZiNemU= +github.com/aliyun/credentials-go v1.2.3/go.mod h1:/KowD1cfGSLrLsH28Jr8W+xwoId0ywIy5lNzDz6O1vw= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= @@ -135,26 +192,54 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buildkite/agent/v3 v3.49.0 h1:FSmRQz8YFhaCXg4MfE7JucPcY7mQ/HWM55ir1j3E9qM= +github.com/buildkite/agent/v3 v3.49.0/go.mod h1:iasSyh3KPjOPCnyvnZB1trkkX7jrdL8PnLBgjdVJxgU= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj/v2 v2.5.6 h1:Jm4VaCI/+Ug5Q57IzEoZbwx4iQFA6wkXv72juUSeK+g= +github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= +github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= +github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= +github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= +github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyberphone/json-canonicalization v0.0.0-20230710064741-aa7fe85c7dbd h1:0av0vtcjA8Hqv5gyWj79CLCFVwOOyBNWPjrfUWceMNg= github.com/cyberphone/json-canonicalization v0.0.0-20230710064741-aa7fe85c7dbd/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= @@ -162,6 +247,14 @@ github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0S github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 h1:foGzavPWwtoyBvjWyKJYDYsyzy+23iBV7NKTwdk+LRY= +github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936/go.mod h1:ttKPnOepYt4LLzD+loXQ1rT6EmpyIYHro7TAJuIIlHo= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= +github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= @@ -177,16 +270,27 @@ github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3sm github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw= +github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +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.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -196,6 +300,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -248,6 +354,8 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg= +github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -256,6 +364,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.15.2 h1:Ra5cll2/eF8X0Ff2+8SMD7euo2nenQ8WEpgqfy4NhHU= github.com/go-playground/validator/v10 v10.15.2/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-rod/rod v0.114.2 h1:Qwt+vZHHnb117zc0q+XjhAJCkB01hchWSxH/raCyLb4= +github.com/go-rod/rod v0.114.2/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= @@ -286,6 +396,8 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -295,6 +407,8 @@ github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -332,6 +446,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.1.6 h1:SW5K3sr7ptST/pIvNkSVWMiJqemRmkjJPPT0jzXdOOY= github.com/google/certificate-transparency-go v1.1.6/go.mod h1:0OJjOsOk+wj6aYQgP7FU0ioQ0AJUmnWPFMqTjQeazPQ= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -342,8 +458,10 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -353,6 +471,10 @@ github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230822174451- github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230822174451-190ad0e4d556/go.mod h1:Ek+8PQrShkA7aHEj3/zSW33wU0V/Bx3zW/gFh7l21xY= github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230822174451-190ad0e4d556 h1:ECWR2uMl+K4Atp4SnE7l2tulf4DwK515p+v1aM9nnwY= github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230822174451-190ad0e4d556/go.mod h1:5sSbf/SbGGvjWIlMlt2bkEqOq+ufOIBYrBevLuxbfSs= +github.com/google/go-github/v50 v50.2.0 h1:j2FyongEHlO9nxXLc+LP3wuBSVU9mVxfpdYUexMpIfk= +github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -377,6 +499,7 @@ github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= @@ -396,10 +519,16 @@ github.com/gookit/gsr v0.1.0 h1:0gadWaYGU4phMs0bma38t+Do5OZowRMEVlHv31p0Zig= github.com/gookit/gsr v0.1.0/go.mod h1:7wv4Y4WCnil8+DlDYHBjidzrEzfHhXEoFjEA0pPPWpI= github.com/gookit/slog v0.5.4 h1:EMctf/kap/SR8cnhkUucL0D3YZwUAJJ+WKQ/DN6kS5s= github.com/gookit/slog v0.5.4/go.mod h1:awroa12zroMvjFpS7tdpTX12AqIzVewUlC10tsj4TYY= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= @@ -423,6 +552,8 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -441,10 +572,12 @@ github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -469,6 +602,8 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/letsencrypt/boulder v0.0.0-20230828195701-66a4c11fced9 h1:2Z+nOQb50oSQjYlwp3gF/PLLX7tYhuY1mlxbEqi2iG0= github.com/letsencrypt/boulder v0.0.0-20230828195701-66a4c11fced9/go.mod h1:QxVawq7cLEno3oWIpkQfeylV8DzySjOMzQqXnWeyans= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -476,22 +611,43 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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.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= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/mozillazg/docker-credential-acr-helper v0.3.0 h1:DVWFZ3/O8BP6Ue3iS/Olw+G07u1hCq1EOVCDZZjCIBI= +github.com/mozillazg/docker-credential-acr-helper v0.3.0/go.mod h1:cZlu3tof523ujmLuiNUb6JsjtHcNA70u1jitrrdnuyA= +github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto= +github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -516,12 +672,16 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/open-policy-agent/opa v0.52.0 h1:Rv3F+VCDqsufaiYy/3S9/Iuk0yfcREK4iZmWbNsKZjA= +github.com/open-policy-agent/opa v0.52.0/go.mod h1:2n99s7WY/BXZUWUOq10JdTgK+G6XM4FYGoe7kQ5Vg0s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= +github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= @@ -543,6 +703,11 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b h1:zd/2RNzIRkoGGMjE+YIsZ85CnDIz672JK2F3Zl4vux4= +github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b/go.mod h1:KjY0wibdYKc4DYkerHSbguaf3JeIPGhNJBp2BNiFH78= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -557,10 +722,14 @@ github.com/sassoftware/relic/v7 v7.5.5 h1:2ZUM6ovo3STCAp0hZnO9nQY9lOB8OyfneeYIi4 github.com/sassoftware/relic/v7 v7.5.5/go.mod h1:NxwtWxWxlUa9as2qZi635Ye6bBT/tGnMALLq7dSfOOU= github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg= github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI= +github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= +github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/sigstore/cosign/v2 v2.1.1 h1:HOI6pWaEie0wLituDWWaqC5U9MaXablKNf6QroVhj6k= github.com/sigstore/cosign/v2 v2.1.1/go.mod h1:S9KGmdQ/Dd29TdgUwGCNeXR7scJWZwREh4A9Za2PRPY= +github.com/sigstore/fulcio v1.3.1 h1:0ntW9VbQbt2JytoSs8BOGB84A65eeyvGSavWteYp29Y= +github.com/sigstore/fulcio v1.3.1/go.mod h1:/XfqazOec45ulJZpyL9sq+OsVQ8g2UOVoNVi7abFgqU= github.com/sigstore/rekor v1.2.2 h1:5JK/zKZvcQpL/jBmHvmFj3YbpDMBQnJQ6ygp8xdF3bY= github.com/sigstore/rekor v1.2.2/go.mod h1:FGnWBGWzeNceJnp0x9eDFd41mI8aQqCjj+Zp0IEs0Qg= github.com/sigstore/sigstore v1.7.2 h1:MY0wSOhKWa8SIWSCO9SzFnUl+b7jbthgXHJpuUg31Qs= @@ -580,6 +749,13 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= @@ -594,8 +770,11 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spiffe/go-spiffe/v2 v2.1.6 h1:4SdizuQieFyL9eNU+SPiCArH4kynzaKOOj0VvM8R7Xo= +github.com/spiffe/go-spiffe/v2 v2.1.6/go.mod h1:eVDqm9xFvyqao6C+eQensb9ZPkyNEeaUbqbBpOhBnNk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -615,17 +794,27 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= +github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= +github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.6.1 h1:6J89fGjQf7s0mLmTG7p7pO/MbKOg+bIXhaLyQdmbKuE= github.com/theupdateframework/go-tuf v0.6.1/go.mod h1:LAFusuQsFNBnEyYoTuA5zZrF7iaQ4TEgBXm8lb6Vj18= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= +github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= +github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 h1:+dBg5k7nuTE38VVdoroRsT0Z88fmvdYrI2EjzJst35I= +github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1/go.mod h1:nmuySobZb4kFgFy6BptpXp/BBw+xFSyvVPP6auoJB4k= +github.com/xanzy/go-gitlab v0.86.0 h1:jR8V9cK9jXRQDb46KOB20NCF3ksY09luaG0IfXE6p7w= +github.com/xanzy/go-gitlab v0.86.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= @@ -633,16 +822,35 @@ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3k github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= +github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= +github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= +github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= +github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= +github.com/ysmood/got v0.34.1 h1:IrV2uWLs45VXNvZqhJ6g2nIhY+pgIG1CUoOcqfXFl1s= +github.com/ysmood/got v0.34.1/go.mod h1:yddyjq/PmAf08RMLSwDjPyCvHvYed+WjHnQxpH851LM= +github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= +github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= +github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak= +github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zalando/go-keyring v0.2.2 h1:f0xmpYiSrHtSNAVgwip93Cg8tuF45HJM6rHq/A5RI/4= github.com/zalando/go-keyring v0.2.2/go.mod h1:sI3evg9Wvpw3+n4SqplGSJUMwtDeROfD4nsFz4z9PG0= +github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs= +github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= @@ -660,10 +868,11 @@ go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= -go.opentelemetry.io/otel/sdk v1.15.0 h1:jZTCkRRd08nxD6w7rIaZeDNGZGGQstH3SfLQ3ZsKICk= -go.opentelemetry.io/otel/sdk v1.15.0/go.mod h1:XDEMrYWzJ4YlC17i6Luih2lwDw2j6G0PkUfr1ZqE+rQ= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.step.sm/crypto v0.35.0 h1:0N6ks5n1sdv4+biJMUTdqHjpTBKKN9zNqqBdOJIyHe4= go.step.sm/crypto v0.35.0/go.mod h1:sBsrpVReoxmiLexbWL+vQRxZd6Gq4YBj/IRSUH+DZe4= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= @@ -677,15 +886,20 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -724,6 +938,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -756,6 +971,7 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -766,6 +982,7 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -792,11 +1009,13 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -824,6 +1043,7 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -840,6 +1060,8 @@ golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -847,11 +1069,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -865,6 +1089,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -878,6 +1103,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -910,6 +1136,7 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -927,6 +1154,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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= golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -986,6 +1214,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -1018,9 +1247,12 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1048,11 +1280,15 @@ gopkg.in/go-jose/go-jose.v2 v2.6.1 h1:qEzJlIDmG9q5VO0M/o8tGS65QMHMS1w01TQJB1VPJ4 gopkg.in/go-jose/go-jose.v2 v2.6.1/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1090,6 +1326,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/release-utils v0.7.4 h1:17LmJrydpUloTCtaoWj95uKlcrUp4h2A9Sa+ZL+lV9w= +sigs.k8s.io/release-utils v0.7.4/go.mod h1:JEt2QPHItd5Pg2UKLAU8PEaSlF4bUjCZimpxFDgymVU= sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/test/cases/fail-1.yaml b/test/cases/fail-1.yaml index f574df1d..5f779003 100644 --- a/test/cases/fail-1.yaml +++ b/test/cases/fail-1.yaml @@ -16,8 +16,8 @@ spec: spec: containers: - name: busybox-container - image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee - command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] env: - name: COSIGNPUBKEY value: | diff --git a/test/cases/fail-2.yaml b/test/cases/fail-2.yaml index 72c83b59..882ad369 100644 --- a/test/cases/fail-2.yaml +++ b/test/cases/fail-2.yaml @@ -16,8 +16,8 @@ spec: spec: containers: - name: busybox-container - image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee - command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] env: - name: COSIGNPUBKEY value: | @@ -26,7 +26,7 @@ spec: PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== -----END PUBLIC KEY----- - name: busybox-container2 - image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee + image: k3d-registry.localhost:5000/busybox:latest command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] env: - name: COSIGNPUBKEY diff --git a/test/cases/fail-3.yaml b/test/cases/fail-3.yaml new file mode 100644 index 00000000..c2481ae0 --- /dev/null +++ b/test/cases/fail-3.yaml @@ -0,0 +1,26 @@ +# one container with a non-existent secret reference to a public key +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-fail-3 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] + env: + - name: COSIGNPUBKEY + valueFrom: + secretKeyRef: + name: non-existent-secret + key: cosign.pub diff --git a/test/cases/fail-4.yaml b/test/cases/fail-4.yaml new file mode 100644 index 00000000..c6f403a9 --- /dev/null +++ b/test/cases/fail-4.yaml @@ -0,0 +1,36 @@ +# two containers, the second one has a non-existent secret reference to a public key +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-fail-4 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D + PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== + -----END PUBLIC KEY----- + - name: busybox-container2 + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] + env: + - name: COSIGNPUBKEY + valueFrom: + secretKeyRef: + name: non-existent-secret + key: non-existent-key diff --git a/test/cases/ok-1.yaml b/test/cases/ok-1.yaml index 597cfa4f..91aa82bc 100644 --- a/test/cases/ok-1.yaml +++ b/test/cases/ok-1.yaml @@ -16,12 +16,12 @@ spec: spec: containers: - name: busybox-container - image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee - command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] env: - name: COSIGNPUBKEY value: | -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D - PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEilKZPJAeodknMNw4krsJhMfPom4S + FziLqM8UHYfhM7LSUwNL98OZh06ALzEJ3nlfTY/Lyvspdmdav4YHQnoMNg== -----END PUBLIC KEY----- diff --git a/test/cases/ok-2.yaml b/test/cases/ok-2.yaml index d45a875d..6b732e1b 100644 --- a/test/cases/ok-2.yaml +++ b/test/cases/ok-2.yaml @@ -16,22 +16,22 @@ spec: spec: containers: - name: busybox-container - image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee - command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] env: - name: COSIGNPUBKEY value: | -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D - PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEilKZPJAeodknMNw4krsJhMfPom4S + FziLqM8UHYfhM7LSUwNL98OZh06ALzEJ3nlfTY/Lyvspdmdav4YHQnoMNg== -----END PUBLIC KEY----- - name: busybox-container2 - image: k3d-registry.localhost:5000/busybox@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee + image: k3d-registry.localhost:5000/busybox:latest command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] env: - name: COSIGNPUBKEY value: | -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D - PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== - -----END PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEilKZPJAeodknMNw4krsJhMfPom4S + FziLqM8UHYfhM7LSUwNL98OZh06ALzEJ3nlfTY/Lyvspdmdav4YHQnoMNg== + -----END PUBLIC KEY----- \ No newline at end of file diff --git a/test/cases/ok-3.yaml b/test/cases/ok-3.yaml new file mode 100644 index 00000000..581714d7 --- /dev/null +++ b/test/cases/ok-3.yaml @@ -0,0 +1,35 @@ +# one container with its public key in secret ref +apiVersion: v1 +kind: Secret +metadata: + name: cosign-pubkey + namespace: test-cases +type: Opaque +data: + pubkey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaWxLWlBKQWVvZGtuTU53NGtyc0poTWZQb200UwpGemlMcU04VUhZZmhNN0xTVXdOTDk4T1poMDZBTHpFSjNubGZUWS9MeXZzcGRtZGF2NFlIUW5vTU5nPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-ok-3 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] + env: + - name: COSIGNPUBKEY + valueFrom: + secretKeyRef: + name: cosign-pubkey + key: pubkey diff --git a/test/cases/ok-4.yaml b/test/cases/ok-4.yaml new file mode 100644 index 00000000..7e98f27e --- /dev/null +++ b/test/cases/ok-4.yaml @@ -0,0 +1,45 @@ +# two containers with its public key in secret ref and environment +apiVersion: v1 +kind: Secret +metadata: + name: cosign-pubkey + namespace: test-cases +type: Opaque +data: + pubkey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaWxLWlBKQWVvZGtuTU53NGtyc0poTWZQb200UwpGemlMcU04VUhZZmhNN0xTVXdOTDk4T1poMDZBTHpFSjNubGZUWS9MeXZzcGRtZGF2NFlIUW5vTU5nPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-ok-4 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] + env: + - name: COSIGNPUBKEY + valueFrom: + secretKeyRef: + name: cosign-pubkey + key: pubkey + - name: busybox-container2 + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEilKZPJAeodknMNw4krsJhMfPom4S + FziLqM8UHYfhM7LSUwNL98OZh06ALzEJ3nlfTY/Lyvspdmdav4YHQnoMNg== + -----END PUBLIC KEY----- \ No newline at end of file diff --git a/test/cases/ok-5.yaml b/test/cases/ok-5.yaml new file mode 100644 index 00000000..4ee4185b --- /dev/null +++ b/test/cases/ok-5.yaml @@ -0,0 +1,45 @@ +# two containers signed with different keys with their public keys in secret ref and env var +apiVersion: v1 +kind: Secret +metadata: + name: cosign-pubkey + namespace: test-cases +type: Opaque +data: + pubkey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaWxLWlBKQWVvZGtuTU53NGtyc0poTWZQb200UwpGemlMcU04VUhZZmhNN0xTVXdOTDk4T1poMDZBTHpFSjNubGZUWS9MeXZzcGRtZGF2NFlIUW5vTU5nPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-ok-5 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] + env: + - name: COSIGNPUBKEY + valueFrom: + secretKeyRef: + name: cosign-pubkey + key: pubkey + - name: busybox-container2 + image: k3d-registry.localhost:5000/busybox:second + command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXpsuwGT77BFMY6oXiivnytKEdu7g + lSSZim8OgipDSU6t2xHrmAPVhBcWeHGqbpjYn+/Av1RunIt81IBItYZIzQ== + -----END PUBLIC KEY----- diff --git a/test/cases/ok-6.yaml b/test/cases/ok-6.yaml new file mode 100644 index 00000000..79ef3bc5 --- /dev/null +++ b/test/cases/ok-6.yaml @@ -0,0 +1,46 @@ +# two containers (one init) signed with different keys with their public keys in secret ref and env var +apiVersion: v1 +kind: Secret +metadata: + name: cosign-pubkey + namespace: test-cases +type: Opaque +data: + pubkey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaWxLWlBKQWVvZGtuTU53NGtyc0poTWZQb200UwpGemlMcU04VUhZZmhNN0xTVXdOTDk4T1poMDZBTHpFSjNubGZUWS9MeXZzcGRtZGF2NFlIUW5vTU5nPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-case-ok-6 + namespace: test-cases +spec: + replicas: 1 + selector: + matchLabels: + app: busybox + template: + metadata: + labels: + app: busybox + spec: + initContainers: + - name: busybox-container + image: k3d-registry.localhost:5000/busybox:latest + command: ["/bin/sh", "-c", "echo 'i am the one that must run first!'; sleep 5"] + env: + - name: COSIGNPUBKEY + valueFrom: + secretKeyRef: + name: cosign-pubkey + key: pubkey + containers: + - name: busybox-container2 + image: k3d-registry.localhost:5000/busybox:second + command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] + env: + - name: COSIGNPUBKEY + value: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXpsuwGT77BFMY6oXiivnytKEdu7g + lSSZim8OgipDSU6t2xHrmAPVhBcWeHGqbpjYn+/Av1RunIt81IBItYZIzQ== + -----END PUBLIC KEY----- diff --git a/test/e2e_test.go b/test/e2e_test.go new file mode 100644 index 00000000..cd34b4ee --- /dev/null +++ b/test/e2e_test.go @@ -0,0 +1,45 @@ +package test + +import ( + "fmt" + "github.com/sigstore/cosign/v2/cmd/cosign/cli" + "os" + "testing" +) + +// createKeys creates a signing keypair for cosing with the provided name +func createKeys(t testing.TB, name string) (string, string) { + args := []string{fmt.Sprintf("--output-key-prefix=%s", name)} + err := os.Setenv("COSIGN_PASSWORD", "") + if err != nil { + t.Fatalf("failed setting COSIGN_PASSWORD: %v", err) + } + cmd := cli.GenerateKeyPair() + cmd.SetArgs(args) + err = cmd.Execute() + if err != nil { + t.Fatalf("failed generating keypair: %v", err) + } + + // read private key and public key from the current directory + privateKey, err := os.ReadFile(fmt.Sprintf("%s.key", name)) + if err != nil { + t.Fatalf("failed reading private key: %v", err) + } + pubKey, err := os.ReadFile(fmt.Sprintf("%s.pub", name)) + if err != nil { + t.Fatalf("failed reading public key: %v", err) + } + + return string(privateKey), string(pubKey) +} + +func TestCreateKeyPair(t *testing.T) { + priv, pub := createKeys(t, "test") + if priv == "" { + t.Fatal("private key is empty") + } + if pub == "" { + t.Fatal("public key is empty") + } +} From fd638faa8bbec2eb8377a585c04c7ab3d9b18c00 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Wed, 13 Sep 2023 22:19:23 +0200 Subject: [PATCH 09/31] chore: added makefile to support local e2e tests --- Makefile | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9be25cf6..88ad2ef0 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,54 @@ -test-cluster: +test-create-cluster: @echo "Creating registry..." @k3d registry create registry.localhost --port 5000 @echo "Adding registry to cluster..." @k3d cluster create cosign-tests --registry-use k3d-registry.localhost:5000 + @echo "Create test namespace..." + @kubectl create namespace test-cases + +test-generate-key: + @echo "Generating key..." + @export COSIGN_PASSWORD="" && \ + cosign generate-key-pair && \ + mv cosign.key test/keys/cosign.key && \ + mv cosign.pub test/keys/cosign.pub + +test-busybox-images: + @echo "Building busybox image..." + @docker pull busybox:latest + @echo "Tagging & pushing busybox images..." + @docker tag busybox:latest k3d-registry.localhost:5000/busybox:latest + @docker tag busybox:latest k3d-registry.localhost:5000/busybox:second + @docker push k3d-registry.localhost:5000/busybox --all-tags + @echo "Signing busybox images..." + @export COSIGN_PASSWORD="" && \ + cosign sign --tlog-upload=false --key test/keys/cosign.key k3d-registry.localhost:5000/busybox:latest && \ + cosign sign --tlog-upload=false --key test/keys/second.key k3d-registry.localhost:5000/busybox:second + test-image: + @echo "Checking for cosign.key..." + @test -f test/keys/cosign.key || (echo "cosign.key not found. Run 'make generate-key' to generate one." && exit 1) @echo "Building test image..." @docker build -t k3d-registry.localhost:5000/cosignwebhook:dev . @echo "Pushing test image..." @docker push k3d-registry.localhost:5000/cosignwebhook:dev @echo "Signing test image..." - @cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev \ No newline at end of file + @export COSIGN_PASSWORD="" && \ + cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev + +test-deploy: + @echo "Deploying test image..." + @SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ + echo "Using image SHA: $${SHA}" && \ + helm upgrade -i cosignwebhook chart -n cosignwebhook --create-namespace \ + --set image.repository=k3d-registry.localhost:5000/cosignwebhook \ + --set image.tag="dev@$${SHA}" \ + --set-file cosign.scwebhook.key=cosign.pub \ + --set logLevel=debug + +test-cleanup: + @echo "Cleaning up..." + @helm uninstall cosignwebhook -n cosignwebhook + @k3d registry delete k3d-registry.localhost + @k3d cluster delete cosign-tests From f6caee9bfb587e37ca4ae1c1bb845e97971128e2 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Sun, 17 Sep 2023 11:26:52 +0200 Subject: [PATCH 10/31] feat: first e2e test draft with signed container & deployment --- main.go | 22 +-- test/e2e_test.go | 130 +++++++++++++++++- cosignwebhook.go => webhook/cosignwebhook.go | 31 +++-- .../cosignwebhook_test.go | 19 ++- 4 files changed, 164 insertions(+), 38 deletions(-) rename cosignwebhook.go => webhook/cosignwebhook.go (94%) rename cosignwebhook_test.go => webhook/cosignwebhook_test.go (93%) diff --git a/main.go b/main.go index 24ae39bf..45273aa0 100644 --- a/main.go +++ b/main.go @@ -10,10 +10,9 @@ import ( "os/signal" "syscall" + "github.com/eumel8/cosignwebhook/webhook" log "github.com/gookit/slog" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -23,17 +22,7 @@ const ( logTemplate = "[{{datetime}}] [{{level}}] {{caller}} {{message}} \n" ) -var ( - tlscert, tlskey string - opsProcessed = promauto.NewCounter(prometheus.CounterOpts{ - Name: "cosign_processed_ops_total", - Help: "The total number of processed events", - }) - verifiedProcessed = promauto.NewCounter(prometheus.CounterOpts{ - Name: "cosign_processed_verified_total", - Help: "The number of verfified events", - }) -) +var tlscert, tlskey string func main() { // parse arguments @@ -63,7 +52,6 @@ func main() { log.GetFormatter().(*log.TextFormatter).SetTemplate(logTemplate) certs, err := tls.LoadX509KeyPair(tlscert, tlskey) - if err != nil { log.Errorf("Failed to load key pair: ", err) } @@ -78,13 +66,13 @@ func main() { } // define http server and server handler - cs := NewCosignServerHandler() + cs := webhook.NewCosignServerHandler() mux := http.NewServeMux() - mux.HandleFunc("/validate", cs.serve) + mux.HandleFunc("/validate", cs.Serve) server.Handler = mux mmux := http.NewServeMux() - mmux.HandleFunc("/healthz", cs.healthz) + mmux.HandleFunc("/healthz", cs.Healthz) mmux.Handle("/metrics", promhttp.Handler()) mserver.Handler = mmux diff --git a/test/e2e_test.go b/test/e2e_test.go index cd34b4ee..38e081b6 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -1,10 +1,18 @@ package test import ( + "context" "fmt" - "github.com/sigstore/cosign/v2/cmd/cosign/cli" "os" "testing" + + "github.com/eumel8/cosignwebhook/webhook" + "github.com/sigstore/cosign/v2/cmd/cosign/cli" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" ) // createKeys creates a signing keypair for cosing with the provided name @@ -34,6 +42,126 @@ func createKeys(t testing.TB, name string) (string, string) { return string(privateKey), string(pubKey) } +// TestOneContainerPubKeyEnvVar tests that deployment with a single signed container, +// with a public key provided via an environment variable, succeeds. +func TestOneContainerPubKeyEnvVar(t *testing.T) { + // create a keypair to sign the container + _, pub := createKeys(t, "test") + os.Setenv("COSIGN_PASSWORD", "") + // sign the container + err := signContainer(t, "test") + if err != nil { + t.Fatalf("failed signing container: %v", err) + } + + // create a deployment with a single signed container and a public key provided via an environment variable + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-1", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "test-case-1"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test-case-1"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test-case-1", + Image: "image_name:tag", + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: pub, + }, + }, + }, + }, + }, + }, + }, + } + + // create clientset + k8sClient, err := createClientSet() + if err != nil { + t.Fatalf("failed creating clientset: %v", err) + } + + // create the deployment + _, err = k8sClient.AppsV1().Deployments("test-cases").Create(context.Background(), &depl, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("failed creating deployment: %v", err) + } + + // wait for the deployment to be ready + err = waitForDeploymentReady(t, k8sClient, "test-cases", "test-case-1") + if err != nil { + t.Fatalf("failed waiting for deployment to be ready: %v", err) + } +} + +// waitForDeploymentReady waits for the deployment to be ready +func waitForDeploymentReady(t *testing.T, k8sClient *kubernetes.Clientset, ns, name string) error { + + // wait until the deployment is ready + w, err := k8sClient.AppsV1().Deployments(ns).Watch(context.Background(), metav1.ListOptions{ + FieldSelector: fmt.Sprintf("metadata.name=%s", name), + }) + + if err != nil { + return err + } + + for event := range w.ResultChan() { + deployment, ok := event.Object.(*appsv1.Deployment) + if !ok { + continue + } + + if deployment.Status.ReadyReplicas == 1 { + return nil + } + } + + return nil +} + +func signContainer(t *testing.T, priv string) error { + args := []string{ + "sign", + "--key", fmt.Sprintf("%s.key", priv), + "image_name:tag", + } + cmd := cli.Sign() + cmd.SetArgs(args) + return cmd.Execute() + +} + +func createClientSet() (k8sClient *kubernetes.Clientset, err error) { + kubeconfig := os.Getenv("KUBECONFIG") + if kubeconfig == "" { + kubeconfig = os.Getenv("HOME") + "/.kube/config" + } + + // create restconfig from kubeconfig + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + return nil, err + } + + cs, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + return cs, nil +} + func TestCreateKeyPair(t *testing.T) { priv, pub := createKeys(t, "test") if priv == "" { diff --git a/cosignwebhook.go b/webhook/cosignwebhook.go similarity index 94% rename from cosignwebhook.go rename to webhook/cosignwebhook.go index 39e851e8..90a88de6 100644 --- a/cosignwebhook.go +++ b/webhook/cosignwebhook.go @@ -1,4 +1,4 @@ -package main +package webhook import ( "context" @@ -7,13 +7,16 @@ import ( "encoding/json" "errors" "fmt" - "github.com/google/go-containerregistry/pkg/authn" "io" - "k8s.io/apimachinery/pkg/types" "net/http" "os" "time" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "k8s.io/apimachinery/pkg/types" + log "github.com/gookit/slog" v1 "k8s.io/api/admission/v1" corev1 "k8s.io/api/core/v1" @@ -38,7 +41,18 @@ import ( const ( admissionApi = "admission.k8s.io/v1" admissionKind = "AdmissionReview" - cosignEnvVar = "COSIGNPUBKEY" + CosignEnvVar = "COSIGNPUBKEY" +) + +var ( + opsProcessed = promauto.NewCounter(prometheus.CounterOpts{ + Name: "cosign_processed_ops_total", + Help: "The total number of processed events", + }) + verifiedProcessed = promauto.NewCounter(prometheus.CounterOpts{ + Name: "cosign_processed_verified_total", + Help: "The number of verfified events", + }) ) // CosignServerHandler listen to admission requests and serve responses @@ -107,7 +121,7 @@ func getPod(byte []byte) (*corev1.Pod, *v1.AdmissionReview, error) { // Else it returns an empty string and an error. func (csh *CosignServerHandler) getPubKeyFromEnv(c *corev1.Container, ns string) (string, error) { for _, envVar := range c.Env { - if envVar.Name == cosignEnvVar { + if envVar.Name == CosignEnvVar { if len(envVar.Value) != 0 { log.Debugf("Found public key in env var for container %q", c.Name) return envVar.Value, nil @@ -143,7 +157,7 @@ func (csh *CosignServerHandler) getSecretValue(namespace string, name string, ke return string(value), nil } -func (csh *CosignServerHandler) healthz(w http.ResponseWriter, r *http.Request) { +func (csh *CosignServerHandler) Healthz(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) _, err := w.Write([]byte("ok")) if err != nil { @@ -152,7 +166,7 @@ func (csh *CosignServerHandler) healthz(w http.ResponseWriter, r *http.Request) } } -func (csh *CosignServerHandler) serve(w http.ResponseWriter, r *http.Request) { +func (csh *CosignServerHandler) Serve(w http.ResponseWriter, r *http.Request) { var body []byte if r.Body != nil { if data, err := io.ReadAll(r.Body); err == nil { @@ -232,7 +246,6 @@ func (csh *CosignServerHandler) serve(w http.ResponseWriter, r *http.Request) { // verifyContainer verifies the signature of the container image func (csh *CosignServerHandler) verifyContainer(c *corev1.Container, ns string) error { - log.Debugf("Inspecting container %q in namespace %q", ns, c.Name) // Get public key from environment var pubKey, err := csh.getPubKeyFromEnv(c, ns) @@ -242,7 +255,7 @@ func (csh *CosignServerHandler) verifyContainer(c *corev1.Container, ns string) // If no public key get here, try to load default secret if len(pubKey) == 0 { - pubKey, err = csh.getSecretValue(ns, "cosignwebhook", cosignEnvVar) + pubKey, err = csh.getSecretValue(ns, "cosignwebhook", CosignEnvVar) if err != nil { log.Debugf("Could not find pub key from default secret: %v", err) } diff --git a/cosignwebhook_test.go b/webhook/cosignwebhook_test.go similarity index 93% rename from cosignwebhook_test.go rename to webhook/cosignwebhook_test.go index fc4a5e12..40d63bb9 100644 --- a/cosignwebhook_test.go +++ b/webhook/cosignwebhook_test.go @@ -1,4 +1,4 @@ -package main +package webhook import ( "testing" @@ -9,7 +9,6 @@ import ( ) func Test_getPubKeyFromEnv(t *testing.T) { - tests := []struct { name string container *corev1.Container @@ -22,7 +21,7 @@ func Test_getPubKeyFromEnv(t *testing.T) { container: &corev1.Container{ Env: []corev1.EnvVar{ { - Name: cosignEnvVar, + Name: CosignEnvVar, Value: "secret", }, }, @@ -36,10 +35,10 @@ func Test_getPubKeyFromEnv(t *testing.T) { container: &corev1.Container{ Env: []corev1.EnvVar{ { - Name: cosignEnvVar, + Name: CosignEnvVar, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ - Key: cosignEnvVar, + Key: CosignEnvVar, LocalObjectReference: corev1.LocalObjectReference{ Name: "cosign-pubkey", }, @@ -57,7 +56,7 @@ func Test_getPubKeyFromEnv(t *testing.T) { container: &corev1.Container{ Env: []corev1.EnvVar{ { - Name: cosignEnvVar, + Name: CosignEnvVar, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ Key: "wrong-key", @@ -78,10 +77,10 @@ func Test_getPubKeyFromEnv(t *testing.T) { container: &corev1.Container{ Env: []corev1.EnvVar{ { - Name: cosignEnvVar, + Name: CosignEnvVar, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ - Key: cosignEnvVar, + Key: CosignEnvVar, LocalObjectReference: corev1.LocalObjectReference{ Name: "non-existing-secret", }, @@ -98,7 +97,6 @@ func Test_getPubKeyFromEnv(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := fake.NewSimpleClientset() if tt.secretPresent { secret := &corev1.Secret{ @@ -107,7 +105,7 @@ func Test_getPubKeyFromEnv(t *testing.T) { Namespace: "test", }, Data: map[string][]byte{ - cosignEnvVar: []byte(tt.want), + CosignEnvVar: []byte(tt.want), }, } c = fake.NewSimpleClientset(secret) @@ -130,7 +128,6 @@ func Test_getPubKeyFromEnv(t *testing.T) { } func TestCosignServerHandler_verifyPodContainer(t *testing.T) { - //tests := []struct { // name string // pod *corev1.Pod From a1a794205f599382ed6e080415bebebf4329fa5b Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Sat, 23 Sep 2023 15:36:11 +0200 Subject: [PATCH 11/31] feat: first working deployment test --- .gitignore | 5 +- Makefile | 8 +- README.md | 2 + generate-certs.sh => hack/generate-certs.sh | 0 test/e2e_test.go | 97 +++++++++++++++++---- 5 files changed, 90 insertions(+), 22 deletions(-) rename generate-certs.sh => hack/generate-certs.sh (100%) diff --git a/.gitignore b/.gitignore index e4bc9588..82021941 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ cosignwebhook grumpywebhook chart/caas-values.yaml -test/keys + +# the keypair used for test-signing of the webhook +*.key +*.pub diff --git a/Makefile b/Makefile index 88ad2ef0..c510d4de 100644 --- a/Makefile +++ b/Makefile @@ -28,14 +28,16 @@ test-busybox-images: test-image: @echo "Checking for cosign.key..." - @test -f test/keys/cosign.key || (echo "cosign.key not found. Run 'make generate-key' to generate one." && exit 1) + @test -f cosign.key || (echo "cosign.key not found. Run 'make generate-key' to generate one." && exit 1) @echo "Building test image..." @docker build -t k3d-registry.localhost:5000/cosignwebhook:dev . @echo "Pushing test image..." @docker push k3d-registry.localhost:5000/cosignwebhook:dev @echo "Signing test image..." - @export COSIGN_PASSWORD="" && \ - cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev + @SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ + echo "Using image SHA: $${SHA}" && \ + export COSIGN_PASSWORD="" && \ + cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev@$${SHA} test-deploy: @echo "Deploying test image..." diff --git a/README.md b/README.md index 66d0f42a..0e3cef17 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ kubectl -n cosignwebhook apply -f manifests/manifest.yaml ## Cert generation +Run the generate-certs script in the `hack` folder to generate the TLS key pair and the CA certificate for the webhook: + ```bash generate-certs.sh --service cosignwebhook --webhook cosignwebhook --namespace cosignwebhook --secret cosignwebhook ``` diff --git a/generate-certs.sh b/hack/generate-certs.sh similarity index 100% rename from generate-certs.sh rename to hack/generate-certs.sh diff --git a/test/e2e_test.go b/test/e2e_test.go index 38e081b6..4a27d687 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -26,6 +26,7 @@ func createKeys(t testing.TB, name string) (string, string) { cmd.SetArgs(args) err = cmd.Execute() if err != nil { + cleanupKeys(t, name) t.Fatalf("failed generating keypair: %v", err) } @@ -42,14 +43,67 @@ func createKeys(t testing.TB, name string) (string, string) { return string(privateKey), string(pubKey) } -// TestOneContainerPubKeyEnvVar tests that deployment with a single signed container, +// cleanupKeys removes all keypair files with the passed name from the testing directory +func cleanupKeys(t testing.TB, name string) { + + t.Logf("cleaning up keypair files for %s", name) + // check if the keypair files exist + _, err := os.Stat(fmt.Sprintf("%s.key", name)) + if err != nil { + t.Fatalf("failed reading private key: %v", err) + } + + _, err = os.Stat(fmt.Sprintf("%s.pub", name)) + if err != nil { + t.Fatalf("failed reading public key: %v", err) + } + + err = os.Remove(fmt.Sprintf("%s.key", name)) + if err != nil { + t.Fatalf("failed removing private key: %v", err) + } + err = os.Remove(fmt.Sprintf("%s.pub", name)) + if err != nil { + t.Fatalf("failed removing public key: %v", err) + } + t.Logf("cleaned up keypair files for %s", name) +} + +// signContainer signs the container with the provided private key +// TODO: find a way to simplify this function - maybe use cosing CLI directly? +func signContainer(t *testing.T, priv, img string) error { + args := []string{ + "sign", + img, + } + t.Setenv("COSIGN_PASSWORD", "") + cmd := cli.New() + _ = cmd.Flags().Set("timeout", "30s") + cmd.SetArgs(args) + + // find the sign subcommand in the commands slice + for _, c := range cmd.Commands() { + if c.Name() == "sign" { + cmd = c + break + } + } + _ = cmd.Flags().Set("key", fmt.Sprintf("%s.key", priv)) + _ = cmd.Flags().Set("tlog-upload", "false") + _ = cmd.Flags().Set("yes", "true") + _ = cmd.Flags().Set("allow-http-registry", "true") + return cmd.Execute() +} + +// TestOneContainerPubKeyEnvVar tests that a deployment with a single signed container, // with a public key provided via an environment variable, succeeds. func TestOneContainerPubKeyEnvVar(t *testing.T) { // create a keypair to sign the container _, pub := createKeys(t, "test") - os.Setenv("COSIGN_PASSWORD", "") - // sign the container - err := signContainer(t, "test") + t.Setenv("COSIGN_PASSWORD", "") + + // sign the container with the ephemeral keypair + err := signContainer(t, "test", "k3d-registry.localhost:5000/busybox:dev@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee") if err != nil { t.Fatalf("failed signing container: %v", err) } @@ -72,7 +126,12 @@ func TestOneContainerPubKeyEnvVar(t *testing.T) { Containers: []corev1.Container{ { Name: "test-case-1", - Image: "image_name:tag", + Image: "k3d-registry.localhost:5000/busybox:dev@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, Env: []corev1.EnvVar{ { Name: webhook.CosignEnvVar, @@ -89,25 +148,39 @@ func TestOneContainerPubKeyEnvVar(t *testing.T) { // create clientset k8sClient, err := createClientSet() if err != nil { + cleanupKeys(t, "test") t.Fatalf("failed creating clientset: %v", err) } // create the deployment _, err = k8sClient.AppsV1().Deployments("test-cases").Create(context.Background(), &depl, metav1.CreateOptions{}) if err != nil { + cleanupKeys(t, "test") t.Fatalf("failed creating deployment: %v", err) } // wait for the deployment to be ready err = waitForDeploymentReady(t, k8sClient, "test-cases", "test-case-1") if err != nil { + cleanupKeys(t, "test") t.Fatalf("failed waiting for deployment to be ready: %v", err) } + + // delete the deployment + err = k8sClient.AppsV1().Deployments("test-cases").Delete(context.Background(), "test-case-1", metav1.DeleteOptions{}) + if err != nil { + cleanupKeys(t, "test") + t.Fatalf("failed deleting deployment: %v", err) + } + + // cleanup the keypair + cleanupKeys(t, "test") } // waitForDeploymentReady waits for the deployment to be ready func waitForDeploymentReady(t *testing.T, k8sClient *kubernetes.Clientset, ns, name string) error { + t.Logf("waiting for deployment %s to be ready", name) // wait until the deployment is ready w, err := k8sClient.AppsV1().Deployments(ns).Watch(context.Background(), metav1.ListOptions{ FieldSelector: fmt.Sprintf("metadata.name=%s", name), @@ -116,7 +189,6 @@ func waitForDeploymentReady(t *testing.T, k8sClient *kubernetes.Clientset, ns, n if err != nil { return err } - for event := range w.ResultChan() { deployment, ok := event.Object.(*appsv1.Deployment) if !ok { @@ -124,6 +196,7 @@ func waitForDeploymentReady(t *testing.T, k8sClient *kubernetes.Clientset, ns, n } if deployment.Status.ReadyReplicas == 1 { + t.Logf("deployment %s is ready", name) return nil } } @@ -131,18 +204,6 @@ func waitForDeploymentReady(t *testing.T, k8sClient *kubernetes.Clientset, ns, n return nil } -func signContainer(t *testing.T, priv string) error { - args := []string{ - "sign", - "--key", fmt.Sprintf("%s.key", priv), - "image_name:tag", - } - cmd := cli.Sign() - cmd.SetArgs(args) - return cmd.Execute() - -} - func createClientSet() (k8sClient *kubernetes.Clientset, err error) { kubeconfig := os.Getenv("KUBECONFIG") if kubeconfig == "" { From baeafb5b4188421207e8797ddfaacc3120989081 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Mon, 25 Sep 2023 20:22:58 +0200 Subject: [PATCH 12/31] feat: end2end.yaml should work now --- .github/workflows/end2end.yaml | 61 +++++++-------------------- .github/workflows/gotest.yaml | 11 +---- Makefile | 18 +++++++- test/main_test.go | 21 +++++++++ test/{e2e_test.go => webhook_test.go} | 14 +----- 5 files changed, 56 insertions(+), 69 deletions(-) create mode 100644 test/main_test.go rename test/{e2e_test.go => webhook_test.go} (95%) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index f1b6b779..9e2fe21a 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -7,11 +7,14 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v3 - - name: Set up K3S - uses: debianmaster/actions-k3s@master - id: k3s + - name: Create Cluster + uses: AbsaOSS/k3d-action@v2 with: - version: 'v1.24.16-k3s1' + cluster-name: cosign-tests + k3d-version: v5.4.6 + - name: Create Local Registry + run: | + k3d registry create registry.localhost --port 5000 - name: Check Cluster Nodes run: | kubectl get nodes @@ -39,53 +42,19 @@ jobs: else echo "Deployment metrics-server OK" fi - - name: Check Cluster Pods - run: | - kubectl get pods -A - name: Setup Helm run: | curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash helm version - - name: Install Cosignwebhook - run: | - helm -n cosignwebhook upgrade -i cosignwebhook \ - --atomic \ - --create-namespace \ - --timeout 300s \ - --set logLevel=debug chart - - name: Check Pods + - name: Create ephemeral keys run: | - kubectl -n cosignwebhook get pods - - name: Check Cosignwebhook Deployment + make test-keys + - name: Build test image run: | - kubectl -n cosignwebhook rollout status deployment/cosignwebhook --timeout=60s - STATUS=$(kubectl -n cosignwebhook get deployment cosignwebhook -o jsonpath={.status.readyReplicas}) - if [[ $STATUS -ne 1 ]] - then - echo "Deployment cosignwebhook not ready" - kubectl -n cosignwebhook get events - exit 1 - else - echo "Deployment cosignwebhook OK" - fi - - name: Deploy Demoapp + make test-image + - name: Install Cosignwebhook run: | - kubectl create namespace demoapp - kubectl -n demoapp apply -f manifests/demoapp.yaml - - name: Check Demoapp Deployment + make test-deploy + - name: Run End2End Tests run: | - kubectl -n demoapp rollout status deployment/demoapp --timeout=60s - STATUS=$(kubectl -n demoapp get deployment demoapp -o jsonpath={.status.readyReplicas}) - if [[ $STATUS -ne 1 ]] - then - echo "Deployment demoapp not ready" - kubectl -n demoapp get events - echo "Get cosignwebhook logs" - kubectl -n cosignwebhook logs -lapp.kubernetes.io/name=cosignwebhook - exit 1 - else - echo "Deployment demoapp OK" - kubectl -n demoapp get events - echo "Get cosignwebhook logs" - kubectl -n cosignwebhook logs -lapp.kubernetes.io/name=cosignwebhook - fi + make test-e2e diff --git a/.github/workflows/gotest.yaml b/.github/workflows/gotest.yaml index 84577234..8015b4b1 100644 --- a/.github/workflows/gotest.yaml +++ b/.github/workflows/gotest.yaml @@ -5,21 +5,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Go uses: actions/setup-go@v3 with: go-version: 1.21 - - name: Mod Tidy run: go mod tidy - - - name: Mod Vendor - run: go mod vendor - - - name: Build - run: go build -v ./... - - name: Test - run: go test -v ./... + run: make test-unit diff --git a/Makefile b/Makefile index c510d4de..81d93ae0 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,9 @@ test-busybox-images: @export COSIGN_PASSWORD="" && \ cosign sign --tlog-upload=false --key test/keys/cosign.key k3d-registry.localhost:5000/busybox:latest && \ cosign sign --tlog-upload=false --key test/keys/second.key k3d-registry.localhost:5000/busybox:second + @echo "Importing to cluster..." + @k3d image import k3d-registry.localhost:5000/busybox:latest --cluster cosign-tests + @k3d image import k3d-registry.localhost:5000/busybox:second --cluster cosign-tests test-image: @@ -38,19 +41,32 @@ test-image: echo "Using image SHA: $${SHA}" && \ export COSIGN_PASSWORD="" && \ cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev@$${SHA} + @echo "Importing test image to cluster..." + @k3d image import k3d-registry.localhost:5000/cosignwebhook:dev --cluster cosign-tests test-deploy: @echo "Deploying test image..." @SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ echo "Using image SHA: $${SHA}" && \ helm upgrade -i cosignwebhook chart -n cosignwebhook --create-namespace \ + --wait \ --set image.repository=k3d-registry.localhost:5000/cosignwebhook \ --set image.tag="dev@$${SHA}" \ --set-file cosign.scwebhook.key=cosign.pub \ --set logLevel=debug +.PHONY: test-e2e +test-e2e: + @echo "Running e2e tests..." + @go test -v -race -count 1 ./test/ + +.PHONY: test-unit +test-unit: + @echo "Running unit tests..." + @go test -v -race -count 1 ./webhook/ + test-cleanup: @echo "Cleaning up..." @helm uninstall cosignwebhook -n cosignwebhook @k3d registry delete k3d-registry.localhost - @k3d cluster delete cosign-tests + @k3d cluster delete cosign-tests \ No newline at end of file diff --git a/test/main_test.go b/test/main_test.go new file mode 100644 index 00000000..d8d386bf --- /dev/null +++ b/test/main_test.go @@ -0,0 +1,21 @@ +package test + +import ( + "os" + "testing" +) + +func TestDeployments(t *testing.T) { + + if os.Getenv("SKIP_TEST_DEPLOYMENTS") != "" { + t.Skip("Skipping TestDeployments") + } + + testFuncs := map[string]func(t *testing.T){ + "OneContainerPubKeyEnvVar": testOneContainerPubKeyEnvVar, + } + + for name, tf := range testFuncs { + t.Run(name, tf) + } +} diff --git a/test/e2e_test.go b/test/webhook_test.go similarity index 95% rename from test/e2e_test.go rename to test/webhook_test.go index 4a27d687..e8847304 100644 --- a/test/e2e_test.go +++ b/test/webhook_test.go @@ -95,9 +95,9 @@ func signContainer(t *testing.T, priv, img string) error { return cmd.Execute() } -// TestOneContainerPubKeyEnvVar tests that a deployment with a single signed container, +// testOneContainerPubKeyEnvVar tests that a deployment with a single signed container, // with a public key provided via an environment variable, succeeds. -func TestOneContainerPubKeyEnvVar(t *testing.T) { +func testOneContainerPubKeyEnvVar(t *testing.T) { // create a keypair to sign the container _, pub := createKeys(t, "test") t.Setenv("COSIGN_PASSWORD", "") @@ -222,13 +222,3 @@ func createClientSet() (k8sClient *kubernetes.Clientset, err error) { } return cs, nil } - -func TestCreateKeyPair(t *testing.T) { - priv, pub := createKeys(t, "test") - if priv == "" { - t.Fatal("private key is empty") - } - if pub == "" { - t.Fatal("public key is empty") - } -} From 6dde95f67c58f3397933f70c8d421dbd5318aa9f Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Mon, 25 Sep 2023 20:25:13 +0200 Subject: [PATCH 13/31] fix: ephemeral key generation will work now --- .github/workflows/end2end.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index 9e2fe21a..336f40af 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -48,7 +48,7 @@ jobs: helm version - name: Create ephemeral keys run: | - make test-keys + make test-generate-keys - name: Build test image run: | make test-image From 1bda9ee84bd87e3ac26a508940072893463a774e Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Mon, 25 Sep 2023 20:27:39 +0200 Subject: [PATCH 14/31] fix: typo in e2e tests --- .github/workflows/end2end.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index 336f40af..4caf0ee8 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -48,7 +48,7 @@ jobs: helm version - name: Create ephemeral keys run: | - make test-generate-keys + make test-generate-key - name: Build test image run: | make test-image From d8406df3a7e7dce52964599ff6ec1e255dfb7122 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Mon, 25 Sep 2023 20:33:14 +0200 Subject: [PATCH 15/31] chore: install cosing for e2e --- .github/workflows/build.yaml | 2 +- .github/workflows/end2end.yaml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f92d6a4b..c15ef0cd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,7 +12,7 @@ jobs: - name: Install Cosign uses: sigstore/cosign-installer@main with: - cosign-release: 'v2.0.0' + cosign-release: 'v2.2.0' - name: Login Build Sign Push run: | echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${GHR} -u ${{ github.actor }} --password-stdin diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index 4caf0ee8..b5820aad 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -46,6 +46,10 @@ jobs: run: | curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash helm version + - name: Install Cosign + uses: sigstore/cosign-installer@main + with: + cosign-release: 'v2.2.0' - name: Create ephemeral keys run: | make test-generate-key From ed61204389884d07a9cd6a260f78f693066ac0b3 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Mon, 25 Sep 2023 21:54:47 +0200 Subject: [PATCH 16/31] fix: changed key location --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 81d93ae0..2734a642 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,8 @@ test-generate-key: @echo "Generating key..." @export COSIGN_PASSWORD="" && \ cosign generate-key-pair && \ - mv cosign.key test/keys/cosign.key && \ - mv cosign.pub test/keys/cosign.pub + mv cosign.key cosign.key && \ + mv cosign.pub cosign.pub test-busybox-images: @echo "Building busybox image..." @@ -22,8 +22,8 @@ test-busybox-images: @docker push k3d-registry.localhost:5000/busybox --all-tags @echo "Signing busybox images..." @export COSIGN_PASSWORD="" && \ - cosign sign --tlog-upload=false --key test/keys/cosign.key k3d-registry.localhost:5000/busybox:latest && \ - cosign sign --tlog-upload=false --key test/keys/second.key k3d-registry.localhost:5000/busybox:second + cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/busybox:latest && \ + cosign sign --tlog-upload=false --key second.key k3d-registry.localhost:5000/busybox:second @echo "Importing to cluster..." @k3d image import k3d-registry.localhost:5000/busybox:latest --cluster cosign-tests @k3d image import k3d-registry.localhost:5000/busybox:second --cluster cosign-tests From 9f947330cb20552272f941f3725e6c75ca7502a4 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Mon, 25 Sep 2023 22:01:38 +0200 Subject: [PATCH 17/31] chore: build action runs on main branch only --- .github/workflows/build.yaml | 4 +++- Makefile | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c15ef0cd..5cc22840 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,7 +1,9 @@ -name: Docker Image CI +name: Build Image & Chart on: push: + branches: + - main jobs: build: diff --git a/Makefile b/Makefile index 2734a642..ae53566a 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,7 @@ test-create-cluster: test-generate-key: @echo "Generating key..." @export COSIGN_PASSWORD="" && \ - cosign generate-key-pair && \ - mv cosign.key cosign.key && \ - mv cosign.pub cosign.pub + cosign generate-key-pair test-busybox-images: @echo "Building busybox image..." From 56a1b547751bab5942c6bead42c4f1d5226cbda1 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Mon, 25 Sep 2023 22:42:42 +0200 Subject: [PATCH 18/31] debug: export SHA --- Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ae53566a..dfb44b2f 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,6 @@ test-busybox-images: @k3d image import k3d-registry.localhost:5000/busybox:latest --cluster cosign-tests @k3d image import k3d-registry.localhost:5000/busybox:second --cluster cosign-tests - test-image: @echo "Checking for cosign.key..." @test -f cosign.key || (echo "cosign.key not found. Run 'make generate-key' to generate one." && exit 1) @@ -35,8 +34,7 @@ test-image: @echo "Pushing test image..." @docker push k3d-registry.localhost:5000/cosignwebhook:dev @echo "Signing test image..." - @SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ - echo "Using image SHA: $${SHA}" && \ + @export SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ export COSIGN_PASSWORD="" && \ cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev@$${SHA} @echo "Importing test image to cluster..." @@ -44,7 +42,7 @@ test-image: test-deploy: @echo "Deploying test image..." - @SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ + @export SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ echo "Using image SHA: $${SHA}" && \ helm upgrade -i cosignwebhook chart -n cosignwebhook --create-namespace \ --wait \ From 67e6ad55c0bbbdfef751443bddf1514be911fa8a Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Mon, 25 Sep 2023 22:49:27 +0200 Subject: [PATCH 19/31] debug: use only tags instead of digests --- Makefile | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index dfb44b2f..498d4ede 100644 --- a/Makefile +++ b/Makefile @@ -34,20 +34,17 @@ test-image: @echo "Pushing test image..." @docker push k3d-registry.localhost:5000/cosignwebhook:dev @echo "Signing test image..." - @export SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ - export COSIGN_PASSWORD="" && \ - cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev@$${SHA} + @export COSIGN_PASSWORD="" && \ + cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev @echo "Importing test image to cluster..." @k3d image import k3d-registry.localhost:5000/cosignwebhook:dev --cluster cosign-tests test-deploy: @echo "Deploying test image..." - @export SHA=$(shell docker inspect --format='{{index .RepoDigests 0}}' k3d-registry.localhost:5000/cosignwebhook:dev | cut -d '@' -f 2) && \ - echo "Using image SHA: $${SHA}" && \ - helm upgrade -i cosignwebhook chart -n cosignwebhook --create-namespace \ + @helm upgrade -i cosignwebhook chart -n cosignwebhook --create-namespace \ --wait \ --set image.repository=k3d-registry.localhost:5000/cosignwebhook \ - --set image.tag="dev@$${SHA}" \ + --set image.tag=dev \ --set-file cosign.scwebhook.key=cosign.pub \ --set logLevel=debug From f609482d373374a03c11e945bea74eecb55e54fd Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Tue, 26 Sep 2023 19:28:18 +0200 Subject: [PATCH 20/31] debug: check why deployment is not working --- .github/workflows/end2end.yaml | 16 ++++++++++++++++ Makefile | 1 - 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index b5820aad..f46f15fd 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -59,6 +59,22 @@ jobs: - name: Install Cosignwebhook run: | make test-deploy + - name: Check Deployment + run: | + kubectl -n cosign rollout status deployment/cosignwebhook --timeout=60s + STATUS=$(kubectl -n cosignwebhook get deployment cosignwebhook -o jsonpath={.status.readyReplicas}) + if [[ $STATUS -ne 1 ]] + then + echo "Deployment cosignwebhook not ready" + kubectl -n cosignwebhook get events + kubectl logs -n cosignwebhook deployment/cosignwebhook cosignwebhook + kubectl logs -n cosignwebhook deployment/cosignwebhook sccosign + kubectl logs -n cosignwebhook deployment/cosignwebhook scwebhook + kubectl describe -n cosignwebhook deployment/cosignwebhook + exit 1 + else + echo "Deployment cosignwebhook OK" + fi - name: Run End2End Tests run: | make test-e2e diff --git a/Makefile b/Makefile index 498d4ede..f8533b51 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,6 @@ test-image: test-deploy: @echo "Deploying test image..." @helm upgrade -i cosignwebhook chart -n cosignwebhook --create-namespace \ - --wait \ --set image.repository=k3d-registry.localhost:5000/cosignwebhook \ --set image.tag=dev \ --set-file cosign.scwebhook.key=cosign.pub \ From 3fc51465af9f9bad81ce33403a20ef2a1cff6acb Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Tue, 26 Sep 2023 19:31:39 +0200 Subject: [PATCH 21/31] debug: fixed namespace --- .github/workflows/end2end.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index f46f15fd..dcec46cb 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -61,7 +61,7 @@ jobs: make test-deploy - name: Check Deployment run: | - kubectl -n cosign rollout status deployment/cosignwebhook --timeout=60s + kubectl -n cosignwebhook rollout status deployment/cosignwebhook --timeout=60s STATUS=$(kubectl -n cosignwebhook get deployment cosignwebhook -o jsonpath={.status.readyReplicas}) if [[ $STATUS -ne 1 ]] then From 880af4af4c16c1ffb7ca1dceaebc91cbf1da660f Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Tue, 26 Sep 2023 19:38:15 +0200 Subject: [PATCH 22/31] debug: only check status --- .github/workflows/end2end.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index dcec46cb..c6e1064f 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -61,7 +61,6 @@ jobs: make test-deploy - name: Check Deployment run: | - kubectl -n cosignwebhook rollout status deployment/cosignwebhook --timeout=60s STATUS=$(kubectl -n cosignwebhook get deployment cosignwebhook -o jsonpath={.status.readyReplicas}) if [[ $STATUS -ne 1 ]] then From c00f8c61740df1c9a84de7502409c3ca6be64b23 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Tue, 26 Sep 2023 21:01:21 +0200 Subject: [PATCH 23/31] debug: switch to own k3d installation --- .github/workflows/end2end.yaml | 37 +++++++++++++--------------------- Makefile | 3 ++- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index c6e1064f..1f9f0cd8 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -7,14 +7,13 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v3 - - name: Create Cluster - uses: AbsaOSS/k3d-action@v2 - with: - cluster-name: cosign-tests - k3d-version: v5.4.6 - - name: Create Local Registry + - name: Install k3d + run: | + curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash + k3d version + - name: Create Cluster & Registry run: | - k3d registry create registry.localhost --port 5000 + make test-create-cluster - name: Check Cluster Nodes run: | kubectl get nodes @@ -61,19 +60,11 @@ jobs: make test-deploy - name: Check Deployment run: | - STATUS=$(kubectl -n cosignwebhook get deployment cosignwebhook -o jsonpath={.status.readyReplicas}) - if [[ $STATUS -ne 1 ]] - then - echo "Deployment cosignwebhook not ready" - kubectl -n cosignwebhook get events - kubectl logs -n cosignwebhook deployment/cosignwebhook cosignwebhook - kubectl logs -n cosignwebhook deployment/cosignwebhook sccosign - kubectl logs -n cosignwebhook deployment/cosignwebhook scwebhook - kubectl describe -n cosignwebhook deployment/cosignwebhook - exit 1 - else - echo "Deployment cosignwebhook OK" - fi - - name: Run End2End Tests - run: | - make test-e2e + echo "Waiting for deployment to be ready" + sleep 60 + echo "Deployment cosignwebhook not ready" + kubectl -n cosignwebhook get events + kubectl describe -n cosignwebhook deployment/cosignwebhook +# - name: Run End2End Tests +# run: | +# make test-e2e diff --git a/Makefile b/Makefile index f8533b51..283746e8 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,8 @@ test-deploy: --set image.repository=k3d-registry.localhost:5000/cosignwebhook \ --set image.tag=dev \ --set-file cosign.scwebhook.key=cosign.pub \ - --set logLevel=debug + --set logLevel=debug \ + --wait --debug .PHONY: test-e2e test-e2e: From 628c04fe9179ee6bd9039110afc63b705aeed10f Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Tue, 26 Sep 2023 21:07:26 +0200 Subject: [PATCH 24/31] debug: activated e2e tests --- .github/workflows/end2end.yaml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index 1f9f0cd8..51a5207e 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -58,13 +58,6 @@ jobs: - name: Install Cosignwebhook run: | make test-deploy - - name: Check Deployment + - name: Run End2End Tests run: | - echo "Waiting for deployment to be ready" - sleep 60 - echo "Deployment cosignwebhook not ready" - kubectl -n cosignwebhook get events - kubectl describe -n cosignwebhook deployment/cosignwebhook -# - name: Run End2End Tests -# run: | -# make test-e2e + make test-e2e From dcc3e84b30ba883cfb3d7661c2d600654503fd01 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Tue, 26 Sep 2023 21:19:24 +0200 Subject: [PATCH 25/31] debug: added busybox images --- .github/workflows/end2end.yaml | 3 ++- Makefile | 12 +++++------- test/webhook_test.go | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index 51a5207e..ee01a585 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -51,7 +51,7 @@ jobs: cosign-release: 'v2.2.0' - name: Create ephemeral keys run: | - make test-generate-key + make test-generate-keys - name: Build test image run: | make test-image @@ -60,4 +60,5 @@ jobs: make test-deploy - name: Run End2End Tests run: | + make test-busybox-images make test-e2e diff --git a/Makefile b/Makefile index 283746e8..3c323c13 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,12 @@ test-create-cluster: @echo "Create test namespace..." @kubectl create namespace test-cases -test-generate-key: - @echo "Generating key..." +test-generate-keys: + @echo "Generating cosign keys..." @export COSIGN_PASSWORD="" && \ - cosign generate-key-pair + cosign generate-key-pair && \ + cosign generate-key-pair --output-key-prefix second + test-busybox-images: @echo "Building busybox image..." @@ -22,10 +24,6 @@ test-busybox-images: @export COSIGN_PASSWORD="" && \ cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/busybox:latest && \ cosign sign --tlog-upload=false --key second.key k3d-registry.localhost:5000/busybox:second - @echo "Importing to cluster..." - @k3d image import k3d-registry.localhost:5000/busybox:latest --cluster cosign-tests - @k3d image import k3d-registry.localhost:5000/busybox:second --cluster cosign-tests - test-image: @echo "Checking for cosign.key..." @test -f cosign.key || (echo "cosign.key not found. Run 'make generate-key' to generate one." && exit 1) diff --git a/test/webhook_test.go b/test/webhook_test.go index e8847304..cff36c67 100644 --- a/test/webhook_test.go +++ b/test/webhook_test.go @@ -126,7 +126,7 @@ func testOneContainerPubKeyEnvVar(t *testing.T) { Containers: []corev1.Container{ { Name: "test-case-1", - Image: "k3d-registry.localhost:5000/busybox:dev@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee", + Image: "k3d-registry.localhost:5000/busybox:latest", Command: []string{ "sh", "-c", From 5f23750aca46be3b6b5352efd62b527efd783cf6 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Fri, 29 Sep 2023 14:28:20 +0200 Subject: [PATCH 26/31] feat: happy path e2e tests --- .github/workflows/end2end.yaml | 9 +- Makefile | 30 +- test/framework/client.go | 243 +++++++++++++++ test/main_test.go | 7 +- test/webhook_test.go | 541 ++++++++++++++++++++++++--------- 5 files changed, 669 insertions(+), 161 deletions(-) create mode 100644 test/framework/client.go diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index ee01a585..546bea66 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -13,7 +13,7 @@ jobs: k3d version - name: Create Cluster & Registry run: | - make test-create-cluster + make e2e-cluster - name: Check Cluster Nodes run: | kubectl get nodes @@ -51,14 +51,13 @@ jobs: cosign-release: 'v2.2.0' - name: Create ephemeral keys run: | - make test-generate-keys + make e2e-keys - name: Build test image run: | - make test-image + make e2e-images - name: Install Cosignwebhook run: | - make test-deploy + make e2e-deploy - name: Run End2End Tests run: | - make test-busybox-images make test-e2e diff --git a/Makefile b/Makefile index 3c323c13..bb94b516 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -test-create-cluster: +e2e-cluster: @echo "Creating registry..." @k3d registry create registry.localhost --port 5000 @echo "Adding registry to cluster..." @@ -6,25 +6,13 @@ test-create-cluster: @echo "Create test namespace..." @kubectl create namespace test-cases -test-generate-keys: +e2e-keys: @echo "Generating cosign keys..." @export COSIGN_PASSWORD="" && \ cosign generate-key-pair && \ cosign generate-key-pair --output-key-prefix second - -test-busybox-images: - @echo "Building busybox image..." - @docker pull busybox:latest - @echo "Tagging & pushing busybox images..." - @docker tag busybox:latest k3d-registry.localhost:5000/busybox:latest - @docker tag busybox:latest k3d-registry.localhost:5000/busybox:second - @docker push k3d-registry.localhost:5000/busybox --all-tags - @echo "Signing busybox images..." - @export COSIGN_PASSWORD="" && \ - cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/busybox:latest && \ - cosign sign --tlog-upload=false --key second.key k3d-registry.localhost:5000/busybox:second -test-image: +e2e-images: @echo "Checking for cosign.key..." @test -f cosign.key || (echo "cosign.key not found. Run 'make generate-key' to generate one." && exit 1) @echo "Building test image..." @@ -36,8 +24,18 @@ test-image: cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/cosignwebhook:dev @echo "Importing test image to cluster..." @k3d image import k3d-registry.localhost:5000/cosignwebhook:dev --cluster cosign-tests + @echo "Building busybox image..." + @docker pull busybox:latest + @echo "Tagging & pushing busybox images..." + @docker tag busybox:latest k3d-registry.localhost:5000/busybox:first + @docker tag busybox:latest k3d-registry.localhost:5000/busybox:second + @docker push k3d-registry.localhost:5000/busybox --all-tags + @echo "Signing busybox images..." + @export COSIGN_PASSWORD="" && \ + cosign sign --tlog-upload=false --key cosign.key k3d-registry.localhost:5000/busybox:first && \ + cosign sign --tlog-upload=false --key second.key k3d-registry.localhost:5000/busybox:second -test-deploy: +e2e-deploy: @echo "Deploying test image..." @helm upgrade -i cosignwebhook chart -n cosignwebhook --create-namespace \ --set image.repository=k3d-registry.localhost:5000/cosignwebhook \ diff --git a/test/framework/client.go b/test/framework/client.go new file mode 100644 index 00000000..446ff4c4 --- /dev/null +++ b/test/framework/client.go @@ -0,0 +1,243 @@ +package framework + +import ( + "context" + "fmt" + "github.com/sigstore/cosign/v2/cmd/cosign/cli" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "os" + "regexp" + "testing" + "time" +) + +// Framework is a helper struct for testing +// the cosignwebhook in a k8s cluster +type Framework struct { + k8s *kubernetes.Clientset +} + +func New() (*Framework, error) { + k8s, err := createClientSet() + if err != nil { + return nil, err + } + + return &Framework{ + k8s: k8s, + }, nil +} + +// CreateDeployment creates a deployment in the testing namespace +func (f *Framework) CreateDeployment(t testing.TB, d appsv1.Deployment) { + _, err := f.k8s.AppsV1().Deployments("test-cases").Create(context.Background(), &d, metav1.CreateOptions{}) + if err != nil { + f.Cleanup(t) + } +} + +// WaitForDeployment waits until the deployment is ready +func (f *Framework) WaitForDeployment(t *testing.T, ns, name string) { + + t.Logf("waiting for deployment %s to be ready", name) + // wait until the deployment is ready + w, err := f.k8s.AppsV1().Deployments(ns).Watch(context.Background(), metav1.ListOptions{ + FieldSelector: fmt.Sprintf("metadata.name=%s", name), + }) + + if err != nil { + t.Fatalf("failed watching deployment: %v", err) + f.Cleanup(t) + } + for event := range w.ResultChan() { + deployment, ok := event.Object.(*appsv1.Deployment) + if !ok { + continue + } + + if deployment.Status.ReadyReplicas == 1 { + t.Logf("deployment %s is ready", name) + return + } + } + + t.Fatalf("deployment %s is not ready", name) +} + +// Cleanup removes all resources created by the framework +// and cleans up the testing directory +func (f *Framework) Cleanup(t testing.TB) { + cleanupKeys(t) + f.cleanupDeployments(t) + f.cleanupSecrets(t) +} + +// cleanupKeys removes all keypair files from the testing directory +func cleanupKeys(t testing.TB) { + + t.Logf("cleaning up keypair files") + files, err := os.ReadDir(".") + if err != nil { + t.Fatalf("failed reading directory: %v", err) + } + for _, f := range files { + if f.IsDir() { + continue + } + reKey := regexp.MustCompile(".*.key") + rePub := regexp.MustCompile(".*.pub") + if reKey.MatchString(f.Name()) || rePub.MatchString(f.Name()) { + err = os.Remove(f.Name()) + if err != nil { + t.Fatalf("failed removing file %s: %v", f.Name(), err) + } + } + } + t.Logf("cleaned up keypair files for") +} + +// CreateKeys creates a signing keypair for cosing with the provided name +func (f *Framework) CreateKeys(t testing.TB, name string) (string, string) { + args := []string{fmt.Sprintf("--output-key-prefix=%s", name)} + err := os.Setenv("COSIGN_PASSWORD", "") + if err != nil { + t.Fatalf("failed setting COSIGN_PASSWORD: %v", err) + } + cmd := cli.GenerateKeyPair() + cmd.SetArgs(args) + err = cmd.Execute() + if err != nil { + f.Cleanup(t) + t.Fatalf("failed creating keypair: %v", err) + } + + // read private key and public key from the current directory + privateKey, err := os.ReadFile(fmt.Sprintf("%s.key", name)) + if err != nil { + f.Cleanup(t) + t.Fatalf("failed reading private key: %v", err) + } + pubKey, err := os.ReadFile(fmt.Sprintf("%s.pub", name)) + if err != nil { + f.Cleanup(t) + t.Fatalf("failed reading public key: %v", err) + } + + return string(privateKey), string(pubKey) +} + +// SignContainer signs the container with the provided private key +func (f *Framework) SignContainer(t *testing.T, priv, img string) { + // TODO: find a way to simplify this function - maybe use cosing CLI directly? + // get SHA of the container image + t.Setenv("COSIGN_PASSWORD", "") + args := []string{ + "sign", + img, + } + t.Setenv("COSIGN_PASSWORD", "") + cmd := cli.New() + _ = cmd.Flags().Set("timeout", "30s") + cmd.SetArgs(args) + + // find the sign subcommand in the commands slice + for _, c := range cmd.Commands() { + if c.Name() == "sign" { + cmd = c + break + } + } + _ = cmd.Flags().Set("key", fmt.Sprintf("%s.key", priv)) + _ = cmd.Flags().Set("tlog-upload", "false") + _ = cmd.Flags().Set("yes", "true") + _ = cmd.Flags().Set("allow-http-registry", "true") + err := cmd.Execute() + if err != nil { + t.Fatalf("failed signing container: %v", err) + } +} + +// cleanupDeployments removes all deployments from the testing namespace, +// if they exist +func (f *Framework) cleanupDeployments(t testing.TB) { + + t.Logf("cleaning up deployments") + deployments, err := f.k8s.AppsV1().Deployments("test-cases").List(context.Background(), metav1.ListOptions{}) + if err != nil { + t.Fatalf("failed listing deployments: %v", err) + } + for _, d := range deployments.Items { + err = f.k8s.AppsV1().Deployments("test-cases").Delete(context.Background(), d.Name, metav1.DeleteOptions{}) + if err != nil { + t.Fatalf("failed deleting deployment %s: %v", d.Name, err) + } + } + + timeout := time.After(30 * time.Second) + for { + select { + case <-timeout: + t.Fatalf("timeout reached while waiting for pods to be deleted") + default: + pods, err := f.k8s.CoreV1().Pods("test-cases").List(context.Background(), metav1.ListOptions{}) + if err != nil { + t.Fatalf("failed listing pods: %v", err) + } + + if len(pods.Items) == 0 { + t.Logf("All pods are deleted") + return + } + time.Sleep(5 * time.Second) + } + } +} + +// CreateSecret creates a secret in the testing namespace +func (f *Framework) CreateSecret(t *testing.T, secret corev1.Secret) { + t.Logf("creating secret %s", secret.Name) + s, err := f.k8s.CoreV1().Secrets("test-cases").Create(context.Background(), &secret, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("failed creating secret: %v", err) + } + t.Logf("created secret %s", s.Name) +} + +// cleanupSecrets removes all secrets from the testing namespace, +func (f *Framework) cleanupSecrets(t testing.TB) { + + t.Logf("cleaning up secrets") + secrets, err := f.k8s.CoreV1().Secrets("test-cases").List(context.Background(), metav1.ListOptions{}) + if err != nil { + t.Fatalf("failed listing secrets: %v", err) + } + for _, s := range secrets.Items { + err = f.k8s.CoreV1().Secrets("test-cases").Delete(context.Background(), s.Name, metav1.DeleteOptions{}) + if err != nil { + t.Fatalf("failed deleting secret %s: %v", s.Name, err) + } + } +} + +func createClientSet() (k8sClient *kubernetes.Clientset, err error) { + kubeconfig := os.Getenv("KUBECONFIG") + if kubeconfig == "" { + kubeconfig = os.Getenv("HOME") + "/.kube/config" + } + + // create restconfig from kubeconfig + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + return nil, err + } + + cs, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + return cs, nil +} diff --git a/test/main_test.go b/test/main_test.go index d8d386bf..9c5b0dbc 100644 --- a/test/main_test.go +++ b/test/main_test.go @@ -12,7 +12,12 @@ func TestDeployments(t *testing.T) { } testFuncs := map[string]func(t *testing.T){ - "OneContainerPubKeyEnvVar": testOneContainerPubKeyEnvVar, + "OneContainerSinglePubKeyEnvRef": testOneContainerSinglePubKeyEnvRef, + "TwoContainersSinglePubKeyEnvRef": testTwoContainersSinglePubKeyEnvRef, + "OneContainerSinglePubKeySecretRef": testOneContainerSinglePubKeySecretRef, + "TwoContainersSinglePubKeyMixedRef": testTwoContainersSinglePubKeyMixedRef, + "TwoContainersMixedPubKeyMixedRef": testTwoContainersMixedPubKeyMixedRef, + "TwoContainersSingleWithInitPubKeyMixedRef": testTwoContainersWithInitSinglePubKeyMixedRef, } for name, tf := range testFuncs { diff --git a/test/webhook_test.go b/test/webhook_test.go index cff36c67..80b2a4ce 100644 --- a/test/webhook_test.go +++ b/test/webhook_test.go @@ -1,131 +1,185 @@ package test import ( - "context" - "fmt" - "os" + "github.com/eumel8/cosignwebhook/test/framework" "testing" "github.com/eumel8/cosignwebhook/webhook" - "github.com/sigstore/cosign/v2/cmd/cosign/cli" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" ) -// createKeys creates a signing keypair for cosing with the provided name -func createKeys(t testing.TB, name string) (string, string) { - args := []string{fmt.Sprintf("--output-key-prefix=%s", name)} - err := os.Setenv("COSIGN_PASSWORD", "") - if err != nil { - t.Fatalf("failed setting COSIGN_PASSWORD: %v", err) - } - cmd := cli.GenerateKeyPair() - cmd.SetArgs(args) - err = cmd.Execute() - if err != nil { - cleanupKeys(t, name) - t.Fatalf("failed generating keypair: %v", err) - } +// terminationGracePeriodSeconds is the termination grace period for the test deployments +var terminationGracePeriodSeconds int64 = 3 - // read private key and public key from the current directory - privateKey, err := os.ReadFile(fmt.Sprintf("%s.key", name)) +// testOneContainerSinglePubKeyEnvRef tests that a deployment with a single signed container, +// with a public key provided via an environment variable, succeeds. +func testOneContainerSinglePubKeyEnvRef(t *testing.T) { + fw, err := framework.New() if err != nil { - t.Fatalf("failed reading private key: %v", err) + t.Fatal(err) } - pubKey, err := os.ReadFile(fmt.Sprintf("%s.pub", name)) - if err != nil { - t.Fatalf("failed reading public key: %v", err) + + _, pub := fw.CreateKeys(t, "test") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:first") + + // create a deployment with a single signed container and a public key provided via an environment variable + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-1", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "test-case-1"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test-case-1"}, + }, + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + Containers: []corev1.Container{ + { + Name: "test-case-1", + Image: "k3d-registry.localhost:5000/busybox:latest", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: pub, + }, + }, + }, + }, + }, + }, + }, } - return string(privateKey), string(pubKey) + fw.CreateDeployment(t, depl) + fw.WaitForDeployment(t, "test-cases", "test-case-1") + fw.Cleanup(t) } -// cleanupKeys removes all keypair files with the passed name from the testing directory -func cleanupKeys(t testing.TB, name string) { +// testTwoContainersSinglePubKeyEnvRef tests that a deployment with two signed containers, +// with a public key provided via an environment variable, succeeds. +func testTwoContainersSinglePubKeyEnvRef(t *testing.T) { - t.Logf("cleaning up keypair files for %s", name) - // check if the keypair files exist - _, err := os.Stat(fmt.Sprintf("%s.key", name)) + fw, err := framework.New() if err != nil { - t.Fatalf("failed reading private key: %v", err) + t.Fatal(err) } - _, err = os.Stat(fmt.Sprintf("%s.pub", name)) - if err != nil { - t.Fatalf("failed reading public key: %v", err) - } + _, pub := fw.CreateKeys(t, "test") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:first") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:second") - err = os.Remove(fmt.Sprintf("%s.key", name)) - if err != nil { - t.Fatalf("failed removing private key: %v", err) - } - err = os.Remove(fmt.Sprintf("%s.pub", name)) - if err != nil { - t.Fatalf("failed removing public key: %v", err) + // create a deployment with two signed containers and a public key provided via an environment variable + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-2", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "test-case-2"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test-case-2"}, + }, + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + Containers: []corev1.Container{ + { + Name: "test-case-2-first", + Image: "k3d-registry.localhost:5000/busybox:first", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: pub, + }, + }, + }, + { + Name: "test-case-2-second", + Image: "k3d-registry.localhost:5000/busybox:second", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: pub, + }, + }, + }, + }, + }, + }, + }, } - t.Logf("cleaned up keypair files for %s", name) -} -// signContainer signs the container with the provided private key -// TODO: find a way to simplify this function - maybe use cosing CLI directly? -func signContainer(t *testing.T, priv, img string) error { - args := []string{ - "sign", - img, - } - t.Setenv("COSIGN_PASSWORD", "") - cmd := cli.New() - _ = cmd.Flags().Set("timeout", "30s") - cmd.SetArgs(args) - - // find the sign subcommand in the commands slice - for _, c := range cmd.Commands() { - if c.Name() == "sign" { - cmd = c - break - } - } - _ = cmd.Flags().Set("key", fmt.Sprintf("%s.key", priv)) - _ = cmd.Flags().Set("tlog-upload", "false") - _ = cmd.Flags().Set("yes", "true") - _ = cmd.Flags().Set("allow-http-registry", "true") - return cmd.Execute() + fw.CreateDeployment(t, depl) + fw.WaitForDeployment(t, "test-cases", "test-case-2") + fw.Cleanup(t) } -// testOneContainerPubKeyEnvVar tests that a deployment with a single signed container, -// with a public key provided via an environment variable, succeeds. -func testOneContainerPubKeyEnvVar(t *testing.T) { - // create a keypair to sign the container - _, pub := createKeys(t, "test") - t.Setenv("COSIGN_PASSWORD", "") +// testOneContainerPubKeySecret tests that a deployment with a single signed container, +// with a public key provided via a secret, succeeds. +func testOneContainerSinglePubKeySecretRef(t *testing.T) { - // sign the container with the ephemeral keypair - err := signContainer(t, "test", "k3d-registry.localhost:5000/busybox:dev@sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee") + fw, err := framework.New() if err != nil { - t.Fatalf("failed signing container: %v", err) + t.Fatal(err) } - // create a deployment with a single signed container and a public key provided via an environment variable + _, pub := fw.CreateKeys(t, "test") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:first") + + // create a secret with the public key + secret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-3", + Namespace: "test-cases", + }, + StringData: map[string]string{ + "cosign.pub": pub, + }, + } + + // create a deployment with a single signed container and a public key provided via a secret depl := appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-case-1", + Name: "test-case-3", Namespace: "test-cases", }, Spec: appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": "test-case-1"}, + MatchLabels: map[string]string{"app": "test-case-3"}, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"app": "test-case-1"}, + Labels: map[string]string{"app": "test-case-3"}, }, Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, Containers: []corev1.Container{ { - Name: "test-case-1", + Name: "test-case-3", Image: "k3d-registry.localhost:5000/busybox:latest", Command: []string{ "sh", @@ -134,8 +188,15 @@ func testOneContainerPubKeyEnvVar(t *testing.T) { }, Env: []corev1.EnvVar{ { - Name: webhook.CosignEnvVar, - Value: pub, + Name: webhook.CosignEnvVar, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: "cosign.pub", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-case-3", + }, + }, + }, }, }, }, @@ -145,80 +206,282 @@ func testOneContainerPubKeyEnvVar(t *testing.T) { }, } - // create clientset - k8sClient, err := createClientSet() - if err != nil { - cleanupKeys(t, "test") - t.Fatalf("failed creating clientset: %v", err) - } + fw.CreateSecret(t, secret) + fw.CreateDeployment(t, depl) + fw.WaitForDeployment(t, "test-cases", "test-case-3") + fw.Cleanup(t) +} - // create the deployment - _, err = k8sClient.AppsV1().Deployments("test-cases").Create(context.Background(), &depl, metav1.CreateOptions{}) +// testTwoContainersMixedPubKeyMixedRef tests that a deployment with two signed containers with two different public keys, +// with the keys provided by a secret and an environment variable, succeeds. +func testTwoContainersMixedPubKeyMixedRef(t *testing.T) { + fw, err := framework.New() if err != nil { - cleanupKeys(t, "test") - t.Fatalf("failed creating deployment: %v", err) + t.Fatal(err) } - // wait for the deployment to be ready - err = waitForDeploymentReady(t, k8sClient, "test-cases", "test-case-1") - if err != nil { - cleanupKeys(t, "test") - t.Fatalf("failed waiting for deployment to be ready: %v", err) + _, pub1 := fw.CreateKeys(t, "test1") + _, pub2 := fw.CreateKeys(t, "test2") + fw.SignContainer(t, "test1", "k3d-registry.localhost:5000/busybox:first") + fw.SignContainer(t, "test2", "k3d-registry.localhost:5000/busybox:second") + + // create a secret with the public key + secret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-4", + Namespace: "test-cases", + }, + StringData: map[string]string{ + "cosign.pub": pub1, + }, } - // delete the deployment - err = k8sClient.AppsV1().Deployments("test-cases").Delete(context.Background(), "test-case-1", metav1.DeleteOptions{}) - if err != nil { - cleanupKeys(t, "test") - t.Fatalf("failed deleting deployment: %v", err) + // create a deployment with two signed containers and a public key provided via a secret + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-4", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "test-case-4"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test-case-4"}, + }, + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + Containers: []corev1.Container{ + { + Name: "test-case-4-first", + Image: "k3d-registry.localhost:5000/busybox:first", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: "cosign.pub", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-case-4", + }, + }, + }, + }, + }, + }, + { + Name: "test-case-4-second", + Image: "k3d-registry.localhost:5000/busybox:second", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: pub2, + }, + }, + }, + }, + }, + }, + }, } - // cleanup the keypair - cleanupKeys(t, "test") -} + fw.CreateSecret(t, secret) + fw.CreateDeployment(t, depl) + fw.WaitForDeployment(t, "test-cases", "test-case-4") + fw.Cleanup(t) -// waitForDeploymentReady waits for the deployment to be ready -func waitForDeploymentReady(t *testing.T, k8sClient *kubernetes.Clientset, ns, name string) error { +} - t.Logf("waiting for deployment %s to be ready", name) - // wait until the deployment is ready - w, err := k8sClient.AppsV1().Deployments(ns).Watch(context.Background(), metav1.ListOptions{ - FieldSelector: fmt.Sprintf("metadata.name=%s", name), - }) +// testTwoContainersSinglePubKeyMixedRef tests that a deployment with two signed containers, +// with a public key provided via a secret and an environment variable, succeeds. +func testTwoContainersSinglePubKeyMixedRef(t *testing.T) { + fw, err := framework.New() if err != nil { - return err + t.Fatal(err) + } + + _, pub := fw.CreateKeys(t, "test") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:first") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:second") + + // create a secret with the public key + secret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-5", + Namespace: "test-cases", + }, + StringData: map[string]string{ + "cosign.pub": pub, + }, } - for event := range w.ResultChan() { - deployment, ok := event.Object.(*appsv1.Deployment) - if !ok { - continue - } - if deployment.Status.ReadyReplicas == 1 { - t.Logf("deployment %s is ready", name) - return nil - } + // create a deployment with two signed containers and a public key provided via a secret + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-5", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "test-case-5"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test-case-5"}, + }, + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + Containers: []corev1.Container{ + { + Name: "test-case-5-first", + Image: "k3d-registry.localhost:5000/busybox:first", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: "cosign.pub", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-case-5", + }, + }, + }, + }, + }, + }, + { + Name: "test-case-5-second", + Image: "k3d-registry.localhost:5000/busybox:second", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: pub, + }, + }, + }, + }, + }, + }, + }, } - return nil + fw.CreateSecret(t, secret) + fw.CreateDeployment(t, depl) + fw.WaitForDeployment(t, "test-cases", "test-case-5") + fw.Cleanup(t) + } -func createClientSet() (k8sClient *kubernetes.Clientset, err error) { - kubeconfig := os.Getenv("KUBECONFIG") - if kubeconfig == "" { - kubeconfig = os.Getenv("HOME") + "/.kube/config" - } +// testTwoContainersSinglePubKeyMixedRef tests that a deployment with two signed containers, +// with a public key provided via a secret and an environment variable, succeeds. +func testTwoContainersWithInitSinglePubKeyMixedRef(t *testing.T) { - // create restconfig from kubeconfig - config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + fw, err := framework.New() if err != nil { - return nil, err + t.Fatal(err) } - cs, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err + _, pub := fw.CreateKeys(t, "test") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:first") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:second") + + // create a secret with the public key + secret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-6", + Namespace: "test-cases", + }, + StringData: map[string]string{ + "cosign.pub": pub, + }, + } + + // create a deployment with two signed containers and a public key provided via a secret + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-case-6", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "test-case-6"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test-case-6"}, + }, + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + InitContainers: []corev1.Container{ + { + Name: "test-case-6-first", + Image: "k3d-registry.localhost:5000/busybox:first", + Command: []string{ + "sh", + "-c", + "echo 'hello world, i am tired and will sleep now, for a bit...';", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: "cosign.pub", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-case-6", + }, + }, + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "test-case-6-second", + Image: "k3d-registry.localhost:5000/busybox:second", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: pub, + }, + }, + }, + }, + }, + }, + }, } - return cs, nil + + fw.CreateSecret(t, secret) + fw.CreateDeployment(t, depl) + fw.WaitForDeployment(t, "test-cases", "test-case-6") + fw.Cleanup(t) } From a12ea9bb7e5e571fdab9d930dc3652377f987994 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Fri, 29 Sep 2023 16:04:23 +0200 Subject: [PATCH 27/31] feat: failing deployments E2E tested --- Makefile | 4 +- test/framework/client.go | 108 +++++++++++++++++----- test/main_test.go | 20 ++-- test/webhook_test.go | 191 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 288 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index bb94b516..d3c38c89 100644 --- a/Makefile +++ b/Makefile @@ -47,12 +47,12 @@ e2e-deploy: .PHONY: test-e2e test-e2e: @echo "Running e2e tests..." - @go test -v -race -count 1 ./test/ + @go test -race -count 1 ./test/ .PHONY: test-unit test-unit: @echo "Running unit tests..." - @go test -v -race -count 1 ./webhook/ + @go test -race -count 1 ./webhook/ test-cleanup: @echo "Cleaning up..." diff --git a/test/framework/client.go b/test/framework/client.go index 446ff4c4..0d4cd5b9 100644 --- a/test/framework/client.go +++ b/test/framework/client.go @@ -36,7 +36,7 @@ func New() (*Framework, error) { func (f *Framework) CreateDeployment(t testing.TB, d appsv1.Deployment) { _, err := f.k8s.AppsV1().Deployments("test-cases").Create(context.Background(), &d, metav1.CreateOptions{}) if err != nil { - f.Cleanup(t) + f.Cleanup(t, err) } } @@ -50,8 +50,7 @@ func (f *Framework) WaitForDeployment(t *testing.T, ns, name string) { }) if err != nil { - t.Fatalf("failed watching deployment: %v", err) - f.Cleanup(t) + f.Cleanup(t, err) } for event := range w.ResultChan() { deployment, ok := event.Object.(*appsv1.Deployment) @@ -70,10 +69,13 @@ func (f *Framework) WaitForDeployment(t *testing.T, ns, name string) { // Cleanup removes all resources created by the framework // and cleans up the testing directory -func (f *Framework) Cleanup(t testing.TB) { +func (f *Framework) Cleanup(t testing.TB, err error) { cleanupKeys(t) f.cleanupDeployments(t) f.cleanupSecrets(t) + if err != nil { + t.Fatalf("test failed: %v", err) + } } // cleanupKeys removes all keypair files from the testing directory @@ -97,7 +99,7 @@ func cleanupKeys(t testing.TB) { } } } - t.Logf("cleaned up keypair files for") + t.Logf("cleaned up keypair files") } // CreateKeys creates a signing keypair for cosing with the provided name @@ -111,20 +113,17 @@ func (f *Framework) CreateKeys(t testing.TB, name string) (string, string) { cmd.SetArgs(args) err = cmd.Execute() if err != nil { - f.Cleanup(t) - t.Fatalf("failed creating keypair: %v", err) + f.Cleanup(t, err) } // read private key and public key from the current directory privateKey, err := os.ReadFile(fmt.Sprintf("%s.key", name)) if err != nil { - f.Cleanup(t) - t.Fatalf("failed reading private key: %v", err) + f.Cleanup(t, err) } pubKey, err := os.ReadFile(fmt.Sprintf("%s.pub", name)) if err != nil { - f.Cleanup(t) - t.Fatalf("failed reading public key: %v", err) + f.Cleanup(t, err) } return string(privateKey), string(pubKey) @@ -157,23 +156,23 @@ func (f *Framework) SignContainer(t *testing.T, priv, img string) { _ = cmd.Flags().Set("allow-http-registry", "true") err := cmd.Execute() if err != nil { - t.Fatalf("failed signing container: %v", err) + f.Cleanup(t, err) } } -// cleanupDeployments removes all deployments from the testing namespace, +// cleanupDeployments removes all deployments from the testing namespace // if they exist func (f *Framework) cleanupDeployments(t testing.TB) { t.Logf("cleaning up deployments") deployments, err := f.k8s.AppsV1().Deployments("test-cases").List(context.Background(), metav1.ListOptions{}) if err != nil { - t.Fatalf("failed listing deployments: %v", err) + f.Cleanup(t, err) } for _, d := range deployments.Items { err = f.k8s.AppsV1().Deployments("test-cases").Delete(context.Background(), d.Name, metav1.DeleteOptions{}) if err != nil { - t.Fatalf("failed deleting deployment %s: %v", d.Name, err) + f.Cleanup(t, err) } } @@ -181,11 +180,11 @@ func (f *Framework) cleanupDeployments(t testing.TB) { for { select { case <-timeout: - t.Fatalf("timeout reached while waiting for pods to be deleted") + f.Cleanup(t, fmt.Errorf("timeout reached while waiting for deployments to be deleted")) default: pods, err := f.k8s.CoreV1().Pods("test-cases").List(context.Background(), metav1.ListOptions{}) if err != nil { - t.Fatalf("failed listing pods: %v", err) + f.Cleanup(t, err) } if len(pods.Items) == 0 { @@ -202,25 +201,92 @@ func (f *Framework) CreateSecret(t *testing.T, secret corev1.Secret) { t.Logf("creating secret %s", secret.Name) s, err := f.k8s.CoreV1().Secrets("test-cases").Create(context.Background(), &secret, metav1.CreateOptions{}) if err != nil { - t.Fatalf("failed creating secret: %v", err) + f.Cleanup(t, err) } t.Logf("created secret %s", s.Name) } -// cleanupSecrets removes all secrets from the testing namespace, +// cleanupSecrets removes all secrets from the testing namespace func (f *Framework) cleanupSecrets(t testing.TB) { t.Logf("cleaning up secrets") secrets, err := f.k8s.CoreV1().Secrets("test-cases").List(context.Background(), metav1.ListOptions{}) if err != nil { - t.Fatalf("failed listing secrets: %v", err) + f.Cleanup(t, err) + } + if len(secrets.Items) == 0 { + return } for _, s := range secrets.Items { err = f.k8s.CoreV1().Secrets("test-cases").Delete(context.Background(), s.Name, metav1.DeleteOptions{}) if err != nil { - t.Fatalf("failed deleting secret %s: %v", s.Name, err) + f.Cleanup(t, err) + } + } +} + +// AssertDeploymentFailed asserts that the deployment cannot start +func (f *Framework) AssertDeploymentFailed(t *testing.T, d appsv1.Deployment) { + + t.Logf("waiting for deployment %s to fail", d.Name) + + // watch for replicasets of the deployment + rsName, err := f.waitForReplicaSetCreation(t, d) + if err != nil { + f.Cleanup(t, err) + } + + // get warning events of deployment's namespace and check if the deployment failed + w, err := f.k8s.CoreV1().Events("test-cases").Watch(context.Background(), metav1.ListOptions{ + FieldSelector: fmt.Sprintf("involvedObject.name=%s", rsName), + }) + if err != nil { + f.Cleanup(t, err) + } + + timeout := time.After(30 * time.Second) + for event := range w.ResultChan() { + select { + case <-timeout: + f.Cleanup(t, fmt.Errorf("timeout reached while waiting for deployment to fail")) + default: + e, ok := event.Object.(*corev1.Event) + if !ok { + time.Sleep(5 * time.Second) + continue + } + if e.Reason == "FailedCreate" { + t.Logf("deployment %s failed: %s", d.Name, e.Message) + return + } + time.Sleep(5 * time.Second) + } + } +} + +func (f *Framework) waitForReplicaSetCreation(t *testing.T, d appsv1.Deployment) (string, error) { + rs, err := f.k8s.AppsV1().ReplicaSets("test-cases").Watch(context.Background(), metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", d.Name), + }) + if err != nil { + f.Cleanup(t, err) + } + + timeout := time.After(30 * time.Second) + for event := range rs.ResultChan() { + select { + case <-timeout: + return "", fmt.Errorf("timeout reached while waiting for replicaset to be created") + default: + rs, ok := event.Object.(*appsv1.ReplicaSet) + if ok { + t.Logf("replicaset %s created", rs.Name) + return rs.Name, nil + } + time.Sleep(5 * time.Second) } } + return "", fmt.Errorf("failed to wait for replicaset creation") } func createClientSet() (k8sClient *kubernetes.Clientset, err error) { diff --git a/test/main_test.go b/test/main_test.go index 9c5b0dbc..9d2bc0ce 100644 --- a/test/main_test.go +++ b/test/main_test.go @@ -1,15 +1,10 @@ package test import ( - "os" "testing" ) -func TestDeployments(t *testing.T) { - - if os.Getenv("SKIP_TEST_DEPLOYMENTS") != "" { - t.Skip("Skipping TestDeployments") - } +func TestPassingDeployments(t *testing.T) { testFuncs := map[string]func(t *testing.T){ "OneContainerSinglePubKeyEnvRef": testOneContainerSinglePubKeyEnvRef, @@ -24,3 +19,16 @@ func TestDeployments(t *testing.T) { t.Run(name, tf) } } + +func TestFailingDeployments(t *testing.T) { + + testFuncs := map[string]func(t *testing.T){ + "OneContainerSinglePubKeyMalformedEnvRef": testOneContainerSinglePubKeyMalformedEnvRef, + "TwoContainersSinglePubKeyMalformedEnvRef": testTwoContainersSinglePubKeyMalformedEnvRef, + "OneContainerSinglePubKeyNoMatchEnvRef": testOneContainerSinglePubKeyNoMatchEnvRef, + } + + for name, tf := range testFuncs { + t.Run(name, tf) + } +} diff --git a/test/webhook_test.go b/test/webhook_test.go index 80b2a4ce..da8e0446 100644 --- a/test/webhook_test.go +++ b/test/webhook_test.go @@ -64,7 +64,7 @@ func testOneContainerSinglePubKeyEnvRef(t *testing.T) { fw.CreateDeployment(t, depl) fw.WaitForDeployment(t, "test-cases", "test-case-1") - fw.Cleanup(t) + fw.Cleanup(t, nil) } // testTwoContainersSinglePubKeyEnvRef tests that a deployment with two signed containers, @@ -135,7 +135,7 @@ func testTwoContainersSinglePubKeyEnvRef(t *testing.T) { fw.CreateDeployment(t, depl) fw.WaitForDeployment(t, "test-cases", "test-case-2") - fw.Cleanup(t) + fw.Cleanup(t, nil) } // testOneContainerPubKeySecret tests that a deployment with a single signed container, @@ -209,7 +209,7 @@ func testOneContainerSinglePubKeySecretRef(t *testing.T) { fw.CreateSecret(t, secret) fw.CreateDeployment(t, depl) fw.WaitForDeployment(t, "test-cases", "test-case-3") - fw.Cleanup(t) + fw.Cleanup(t, nil) } // testTwoContainersMixedPubKeyMixedRef tests that a deployment with two signed containers with two different public keys, @@ -299,7 +299,7 @@ func testTwoContainersMixedPubKeyMixedRef(t *testing.T) { fw.CreateSecret(t, secret) fw.CreateDeployment(t, depl) fw.WaitForDeployment(t, "test-cases", "test-case-4") - fw.Cleanup(t) + fw.Cleanup(t, nil) } @@ -390,7 +390,7 @@ func testTwoContainersSinglePubKeyMixedRef(t *testing.T) { fw.CreateSecret(t, secret) fw.CreateDeployment(t, depl) fw.WaitForDeployment(t, "test-cases", "test-case-5") - fw.Cleanup(t) + fw.Cleanup(t, nil) } @@ -483,5 +483,184 @@ func testTwoContainersWithInitSinglePubKeyMixedRef(t *testing.T) { fw.CreateSecret(t, secret) fw.CreateDeployment(t, depl) fw.WaitForDeployment(t, "test-cases", "test-case-6") - fw.Cleanup(t) + fw.Cleanup(t, nil) +} + +// testOneContainerSinglePubKeyNoMatchEnvRef tests that a deployment with a single signed container, +// with a public key provided via an environment variable, fails if the public key does not match the signature. +func testOneContainerSinglePubKeyNoMatchEnvRef(t *testing.T) { + + fw, err := framework.New() + if err != nil { + t.Fatal(err) + } + + _, _ = fw.CreateKeys(t, "test") + _, other := fw.CreateKeys(t, "other") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:first") + + // create a deployment with a single signed container and a public key provided via an environment variable + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fail-case-1", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "fail-case-1"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "fail-case-1"}, + }, + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + Containers: []corev1.Container{ + { + Name: "fail-case-1", + Image: "k3d-registry.localhost:5000/busybox:first", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: other, + }, + }, + }, + }, + }, + }, + }, + } + + fw.CreateDeployment(t, depl) + fw.AssertDeploymentFailed(t, depl) + fw.Cleanup(t, nil) + +} + +// testTwoContainersSinglePubKeyNoMatchEnvRef tests that a deployment with two signed containers, +// with a public key provided via an environment variable, fails if one of the container's pub key is malformed. +func testTwoContainersSinglePubKeyMalformedEnvRef(t *testing.T) { + + fw, err := framework.New() + if err != nil { + t.Fatal(err) + } + + _, pub := fw.CreateKeys(t, "test") + fw.SignContainer(t, "test", "k3d-registry.localhost:5000/busybox:first") + + // create a deployment with two signed containers and a public key provided via an environment variable + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fail-case-2", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "fail-case-2"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "fail-case-2"}, + }, + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + Containers: []corev1.Container{ + { + Name: "fail-case-2-first", + Image: "k3d-registry.localhost:5000/busybox:first", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: pub, + }, + }, + }, + { + Name: "fail-case-2-second", + Image: "k3d-registry.localhost:5000/busybox:second", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: "not-a-public-key", + }, + }, + }, + }, + }, + }, + }, + } + + fw.CreateDeployment(t, depl) + fw.AssertDeploymentFailed(t, depl) + fw.Cleanup(t, nil) + +} + +// testOneContainerSinglePubKeyMalformedEnvRef tests that a deployment with a single signed container, +// // with a public key provided via an environment variable, fails if the public key has an incorrect format. +func testOneContainerSinglePubKeyMalformedEnvRef(t *testing.T) { + fw, err := framework.New() + if err != nil { + t.Fatal(err) + } + + depl := appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fail-case-3", + Namespace: "test-cases", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "fail-case-3"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "fail-case-3"}, + }, + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + Containers: []corev1.Container{ + { + Name: "fail-case-3", + Image: "k3d-registry.localhost:5000/busybox:latest", + Command: []string{ + "sh", + "-c", + "while true; do echo 'hello world, i am tired and will sleep now'; sleep 10; done", + }, + Env: []corev1.EnvVar{ + { + Name: webhook.CosignEnvVar, + Value: "not-a-public-key", + }, + }, + }, + }, + }, + }, + }, + } + + fw.CreateDeployment(t, depl) + fw.AssertDeploymentFailed(t, depl) + fw.Cleanup(t, nil) + } From a4a274aef2221bf792af21124197de72560ea36e Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Fri, 29 Sep 2023 16:17:40 +0200 Subject: [PATCH 28/31] chore: docs & verbose tests to find failure --- .github/workflows/end2end.yaml | 1 + Makefile | 4 ++-- README.md | 19 ++++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index 546bea66..a172d7f9 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -60,4 +60,5 @@ jobs: make e2e-deploy - name: Run End2End Tests run: | + go mod download make test-e2e diff --git a/Makefile b/Makefile index d3c38c89..bb94b516 100644 --- a/Makefile +++ b/Makefile @@ -47,12 +47,12 @@ e2e-deploy: .PHONY: test-e2e test-e2e: @echo "Running e2e tests..." - @go test -race -count 1 ./test/ + @go test -v -race -count 1 ./test/ .PHONY: test-unit test-unit: @echo "Running unit tests..." - @go test -race -count 1 ./webhook/ + @go test -v -race -count 1 ./webhook/ test-cleanup: @echo "Cleaning up..." diff --git a/README.md b/README.md index 0e3cef17..530b0bf4 100644 --- a/README.md +++ b/README.md @@ -103,17 +103,29 @@ public key used to sign the image you're deploying. # Test -Based on the signed image and the corresponding key, the demo app should appear or denied (check event log) +To test the webhook, you may run the following command(s): ```bash -kubectl create namespace cosignwebhook -kubectl -n cosignwebhook apply -f manifests/demoapp.yaml +# unit tests +make test-unit + +# E2E tests +make e2e-prep +make test-e2e ``` +## E2E tests + +The E2E tests require a running kubernetes cluster. Currently, the namespace and webhook are deployed via helper make targets. To run the tests the following is required: + +- docker +- cosign (v2) + # TODO * [x] Support private images * [x] Support multiple container/keys +* [ ] Support COSING_REPOSITORY # Local build @@ -124,6 +136,7 @@ CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o cosignw ## Credits Frank Kloeker f.kloeker@telekom.de +Bruno Bressi, bruno.bressi@telekom.de Life is for sharing. If you have an issue with the code or want to improve it, feel free to open an issue or an pull request. From 0833d601c8ddd08e5cd8c61c827243a6282ec265 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Fri, 29 Sep 2023 16:23:57 +0200 Subject: [PATCH 29/31] refactor: WaitForDeployment works with deployment --- test/framework/client.go | 205 +++++++++++++-------------------------- test/framework/cosign.go | 91 +++++++++++++++++ test/webhook_test.go | 12 +-- 3 files changed, 162 insertions(+), 146 deletions(-) create mode 100644 test/framework/cosign.go diff --git a/test/framework/client.go b/test/framework/client.go index 0d4cd5b9..1ed236ee 100644 --- a/test/framework/client.go +++ b/test/framework/client.go @@ -3,14 +3,12 @@ package framework import ( "context" "fmt" - "github.com/sigstore/cosign/v2/cmd/cosign/cli" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" "os" - "regexp" "testing" "time" ) @@ -32,39 +30,23 @@ func New() (*Framework, error) { }, nil } -// CreateDeployment creates a deployment in the testing namespace -func (f *Framework) CreateDeployment(t testing.TB, d appsv1.Deployment) { - _, err := f.k8s.AppsV1().Deployments("test-cases").Create(context.Background(), &d, metav1.CreateOptions{}) - if err != nil { - f.Cleanup(t, err) +func createClientSet() (k8sClient *kubernetes.Clientset, err error) { + kubeconfig := os.Getenv("KUBECONFIG") + if kubeconfig == "" { + kubeconfig = os.Getenv("HOME") + "/.kube/config" } -} - -// WaitForDeployment waits until the deployment is ready -func (f *Framework) WaitForDeployment(t *testing.T, ns, name string) { - - t.Logf("waiting for deployment %s to be ready", name) - // wait until the deployment is ready - w, err := f.k8s.AppsV1().Deployments(ns).Watch(context.Background(), metav1.ListOptions{ - FieldSelector: fmt.Sprintf("metadata.name=%s", name), - }) + // create restconfig from kubeconfig + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) if err != nil { - f.Cleanup(t, err) + return nil, err } - for event := range w.ResultChan() { - deployment, ok := event.Object.(*appsv1.Deployment) - if !ok { - continue - } - if deployment.Status.ReadyReplicas == 1 { - t.Logf("deployment %s is ready", name) - return - } + cs, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err } - - t.Fatalf("deployment %s is not ready", name) + return cs, nil } // Cleanup removes all resources created by the framework @@ -78,88 +60,6 @@ func (f *Framework) Cleanup(t testing.TB, err error) { } } -// cleanupKeys removes all keypair files from the testing directory -func cleanupKeys(t testing.TB) { - - t.Logf("cleaning up keypair files") - files, err := os.ReadDir(".") - if err != nil { - t.Fatalf("failed reading directory: %v", err) - } - for _, f := range files { - if f.IsDir() { - continue - } - reKey := regexp.MustCompile(".*.key") - rePub := regexp.MustCompile(".*.pub") - if reKey.MatchString(f.Name()) || rePub.MatchString(f.Name()) { - err = os.Remove(f.Name()) - if err != nil { - t.Fatalf("failed removing file %s: %v", f.Name(), err) - } - } - } - t.Logf("cleaned up keypair files") -} - -// CreateKeys creates a signing keypair for cosing with the provided name -func (f *Framework) CreateKeys(t testing.TB, name string) (string, string) { - args := []string{fmt.Sprintf("--output-key-prefix=%s", name)} - err := os.Setenv("COSIGN_PASSWORD", "") - if err != nil { - t.Fatalf("failed setting COSIGN_PASSWORD: %v", err) - } - cmd := cli.GenerateKeyPair() - cmd.SetArgs(args) - err = cmd.Execute() - if err != nil { - f.Cleanup(t, err) - } - - // read private key and public key from the current directory - privateKey, err := os.ReadFile(fmt.Sprintf("%s.key", name)) - if err != nil { - f.Cleanup(t, err) - } - pubKey, err := os.ReadFile(fmt.Sprintf("%s.pub", name)) - if err != nil { - f.Cleanup(t, err) - } - - return string(privateKey), string(pubKey) -} - -// SignContainer signs the container with the provided private key -func (f *Framework) SignContainer(t *testing.T, priv, img string) { - // TODO: find a way to simplify this function - maybe use cosing CLI directly? - // get SHA of the container image - t.Setenv("COSIGN_PASSWORD", "") - args := []string{ - "sign", - img, - } - t.Setenv("COSIGN_PASSWORD", "") - cmd := cli.New() - _ = cmd.Flags().Set("timeout", "30s") - cmd.SetArgs(args) - - // find the sign subcommand in the commands slice - for _, c := range cmd.Commands() { - if c.Name() == "sign" { - cmd = c - break - } - } - _ = cmd.Flags().Set("key", fmt.Sprintf("%s.key", priv)) - _ = cmd.Flags().Set("tlog-upload", "false") - _ = cmd.Flags().Set("yes", "true") - _ = cmd.Flags().Set("allow-http-registry", "true") - err := cmd.Execute() - if err != nil { - f.Cleanup(t, err) - } -} - // cleanupDeployments removes all deployments from the testing namespace // if they exist func (f *Framework) cleanupDeployments(t testing.TB) { @@ -196,16 +96,6 @@ func (f *Framework) cleanupDeployments(t testing.TB) { } } -// CreateSecret creates a secret in the testing namespace -func (f *Framework) CreateSecret(t *testing.T, secret corev1.Secret) { - t.Logf("creating secret %s", secret.Name) - s, err := f.k8s.CoreV1().Secrets("test-cases").Create(context.Background(), &secret, metav1.CreateOptions{}) - if err != nil { - f.Cleanup(t, err) - } - t.Logf("created secret %s", s.Name) -} - // cleanupSecrets removes all secrets from the testing namespace func (f *Framework) cleanupSecrets(t testing.TB) { @@ -225,6 +115,60 @@ func (f *Framework) cleanupSecrets(t testing.TB) { } } +// CreateDeployment creates a deployment in the testing namespace +func (f *Framework) CreateDeployment(t testing.TB, d appsv1.Deployment) { + _, err := f.k8s.AppsV1().Deployments("test-cases").Create(context.Background(), &d, metav1.CreateOptions{}) + if err != nil { + f.Cleanup(t, err) + } +} + +// WaitForDeployment waits until the deployment is ready +func (f *Framework) WaitForDeployment(t *testing.T, d appsv1.Deployment) { + + t.Logf("waiting for deployment %s to be ready", d.Name) + // wait until the deployment is ready + w, err := f.k8s.AppsV1().Deployments(d.Namespace).Watch(context.Background(), metav1.ListOptions{ + FieldSelector: fmt.Sprintf("metadata.name=%s", d.Name), + }) + + if err != nil { + f.Cleanup(t, err) + } + + timeout := time.After(30 * time.Second) + for event := range w.ResultChan() { + select { + case <-timeout: + f.Cleanup(t, fmt.Errorf("timeout reached while waiting for deployment to be ready")) + default: + deployment, ok := event.Object.(*appsv1.Deployment) + if !ok { + time.Sleep(5 * time.Second) + continue + } + + if deployment.Status.ReadyReplicas == 1 { + t.Logf("deployment %s is ready", d.Name) + return + } + time.Sleep(5 * time.Second) + } + } + + f.Cleanup(t, fmt.Errorf("failed to wait for deployment to be ready")) +} + +// CreateSecret creates a secret in the testing namespace +func (f *Framework) CreateSecret(t *testing.T, secret corev1.Secret) { + t.Logf("creating secret %s", secret.Name) + s, err := f.k8s.CoreV1().Secrets("test-cases").Create(context.Background(), &secret, metav1.CreateOptions{}) + if err != nil { + f.Cleanup(t, err) + } + t.Logf("created secret %s", s.Name) +} + // AssertDeploymentFailed asserts that the deployment cannot start func (f *Framework) AssertDeploymentFailed(t *testing.T, d appsv1.Deployment) { @@ -288,22 +232,3 @@ func (f *Framework) waitForReplicaSetCreation(t *testing.T, d appsv1.Deployment) } return "", fmt.Errorf("failed to wait for replicaset creation") } - -func createClientSet() (k8sClient *kubernetes.Clientset, err error) { - kubeconfig := os.Getenv("KUBECONFIG") - if kubeconfig == "" { - kubeconfig = os.Getenv("HOME") + "/.kube/config" - } - - // create restconfig from kubeconfig - config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) - if err != nil { - return nil, err - } - - cs, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - return cs, nil -} diff --git a/test/framework/cosign.go b/test/framework/cosign.go new file mode 100644 index 00000000..4ec71542 --- /dev/null +++ b/test/framework/cosign.go @@ -0,0 +1,91 @@ +package framework + +import ( + "fmt" + "github.com/sigstore/cosign/v2/cmd/cosign/cli" + "os" + "regexp" + "testing" +) + +// cleanupKeys removes all keypair files from the testing directory +func cleanupKeys(t testing.TB) { + + t.Logf("cleaning up keypair files") + files, err := os.ReadDir(".") + if err != nil { + t.Fatalf("failed reading directory: %v", err) + } + for _, f := range files { + if f.IsDir() { + continue + } + reKey := regexp.MustCompile(".*.key") + rePub := regexp.MustCompile(".*.pub") + if reKey.MatchString(f.Name()) || rePub.MatchString(f.Name()) { + err = os.Remove(f.Name()) + if err != nil { + t.Fatalf("failed removing file %s: %v", f.Name(), err) + } + } + } + t.Logf("cleaned up keypair files") +} + +// CreateKeys creates a signing keypair for cosing with the provided name +func (f *Framework) CreateKeys(t testing.TB, name string) (string, string) { + args := []string{fmt.Sprintf("--output-key-prefix=%s", name)} + err := os.Setenv("COSIGN_PASSWORD", "") + if err != nil { + t.Fatalf("failed setting COSIGN_PASSWORD: %v", err) + } + cmd := cli.GenerateKeyPair() + cmd.SetArgs(args) + err = cmd.Execute() + if err != nil { + f.Cleanup(t, err) + } + + // read private key and public key from the current directory + privateKey, err := os.ReadFile(fmt.Sprintf("%s.key", name)) + if err != nil { + f.Cleanup(t, err) + } + pubKey, err := os.ReadFile(fmt.Sprintf("%s.pub", name)) + if err != nil { + f.Cleanup(t, err) + } + + return string(privateKey), string(pubKey) +} + +// SignContainer signs the container with the provided private key +func (f *Framework) SignContainer(t *testing.T, priv, img string) { + // TODO: find a way to simplify this function - maybe use cosing CLI directly? + // get SHA of the container image + t.Setenv("COSIGN_PASSWORD", "") + args := []string{ + "sign", + img, + } + t.Setenv("COSIGN_PASSWORD", "") + cmd := cli.New() + _ = cmd.Flags().Set("timeout", "30s") + cmd.SetArgs(args) + + // find the sign subcommand in the commands slice + for _, c := range cmd.Commands() { + if c.Name() == "sign" { + cmd = c + break + } + } + _ = cmd.Flags().Set("key", fmt.Sprintf("%s.key", priv)) + _ = cmd.Flags().Set("tlog-upload", "false") + _ = cmd.Flags().Set("yes", "true") + _ = cmd.Flags().Set("allow-http-registry", "true") + err := cmd.Execute() + if err != nil { + f.Cleanup(t, err) + } +} diff --git a/test/webhook_test.go b/test/webhook_test.go index da8e0446..262b80b6 100644 --- a/test/webhook_test.go +++ b/test/webhook_test.go @@ -63,7 +63,7 @@ func testOneContainerSinglePubKeyEnvRef(t *testing.T) { } fw.CreateDeployment(t, depl) - fw.WaitForDeployment(t, "test-cases", "test-case-1") + fw.WaitForDeployment(t, depl) fw.Cleanup(t, nil) } @@ -134,7 +134,7 @@ func testTwoContainersSinglePubKeyEnvRef(t *testing.T) { } fw.CreateDeployment(t, depl) - fw.WaitForDeployment(t, "test-cases", "test-case-2") + fw.WaitForDeployment(t, depl) fw.Cleanup(t, nil) } @@ -208,7 +208,7 @@ func testOneContainerSinglePubKeySecretRef(t *testing.T) { fw.CreateSecret(t, secret) fw.CreateDeployment(t, depl) - fw.WaitForDeployment(t, "test-cases", "test-case-3") + fw.WaitForDeployment(t, depl) fw.Cleanup(t, nil) } @@ -298,7 +298,7 @@ func testTwoContainersMixedPubKeyMixedRef(t *testing.T) { fw.CreateSecret(t, secret) fw.CreateDeployment(t, depl) - fw.WaitForDeployment(t, "test-cases", "test-case-4") + fw.WaitForDeployment(t, depl) fw.Cleanup(t, nil) } @@ -389,7 +389,7 @@ func testTwoContainersSinglePubKeyMixedRef(t *testing.T) { fw.CreateSecret(t, secret) fw.CreateDeployment(t, depl) - fw.WaitForDeployment(t, "test-cases", "test-case-5") + fw.WaitForDeployment(t, depl) fw.Cleanup(t, nil) } @@ -482,7 +482,7 @@ func testTwoContainersWithInitSinglePubKeyMixedRef(t *testing.T) { fw.CreateSecret(t, secret) fw.CreateDeployment(t, depl) - fw.WaitForDeployment(t, "test-cases", "test-case-6") + fw.WaitForDeployment(t, depl) fw.Cleanup(t, nil) } From 8c38ae9a0a6c322aac41ce295f7d8e14737ad0eb Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Fri, 29 Sep 2023 16:26:24 +0200 Subject: [PATCH 30/31] fix: replaced latest with first to be more explicit --- test/webhook_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/webhook_test.go b/test/webhook_test.go index 262b80b6..5a1f502d 100644 --- a/test/webhook_test.go +++ b/test/webhook_test.go @@ -43,7 +43,7 @@ func testOneContainerSinglePubKeyEnvRef(t *testing.T) { Containers: []corev1.Container{ { Name: "test-case-1", - Image: "k3d-registry.localhost:5000/busybox:latest", + Image: "k3d-registry.localhost:5000/busybox:first", Command: []string{ "sh", "-c", @@ -180,7 +180,7 @@ func testOneContainerSinglePubKeySecretRef(t *testing.T) { Containers: []corev1.Container{ { Name: "test-case-3", - Image: "k3d-registry.localhost:5000/busybox:latest", + Image: "k3d-registry.localhost:5000/busybox:first", Command: []string{ "sh", "-c", @@ -640,7 +640,7 @@ func testOneContainerSinglePubKeyMalformedEnvRef(t *testing.T) { Containers: []corev1.Container{ { Name: "fail-case-3", - Image: "k3d-registry.localhost:5000/busybox:latest", + Image: "k3d-registry.localhost:5000/busybox:first", Command: []string{ "sh", "-c", From 7a564b659383898ef5b7dad48cf14d12a3191af8 Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Fri, 29 Sep 2023 16:42:21 +0200 Subject: [PATCH 31/31] chore: removed old yaml test cases --- Makefile | 39 +++++++++++++++++++++-------------- test/cases/fail-1.yaml | 26 ------------------------ test/cases/fail-2.yaml | 36 --------------------------------- test/cases/fail-3.yaml | 26 ------------------------ test/cases/fail-4.yaml | 36 --------------------------------- test/cases/ok-1.yaml | 27 ------------------------- test/cases/ok-2.yaml | 37 --------------------------------- test/cases/ok-3.yaml | 35 -------------------------------- test/cases/ok-4.yaml | 45 ----------------------------------------- test/cases/ok-5.yaml | 45 ----------------------------------------- test/cases/ok-6.yaml | 46 ------------------------------------------ 11 files changed, 24 insertions(+), 374 deletions(-) delete mode 100644 test/cases/fail-1.yaml delete mode 100644 test/cases/fail-2.yaml delete mode 100644 test/cases/fail-3.yaml delete mode 100644 test/cases/fail-4.yaml delete mode 100644 test/cases/ok-1.yaml delete mode 100644 test/cases/ok-2.yaml delete mode 100644 test/cases/ok-3.yaml delete mode 100644 test/cases/ok-4.yaml delete mode 100644 test/cases/ok-5.yaml delete mode 100644 test/cases/ok-6.yaml diff --git a/Makefile b/Makefile index bb94b516..1762df54 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,26 @@ +############# +### TESTS ### +############# +.PHONY: test-e2e +test-e2e: + @echo "Running e2e tests..." + @go test -v -race -count 1 ./test/ + +test-cleanup: + @echo "Cleaning up..." + @helm uninstall cosignwebhook -n cosignwebhook + @k3d registry delete k3d-registry.localhost + @k3d cluster delete cosign-tests + +.PHONY: test-unit +test-unit: + @echo "Running unit tests..." + @go test -v -race -count 1 ./webhook/ + +########### +### E2E ### +########### + e2e-cluster: @echo "Creating registry..." @k3d registry create registry.localhost --port 5000 @@ -44,18 +67,4 @@ e2e-deploy: --set logLevel=debug \ --wait --debug -.PHONY: test-e2e -test-e2e: - @echo "Running e2e tests..." - @go test -v -race -count 1 ./test/ - -.PHONY: test-unit -test-unit: - @echo "Running unit tests..." - @go test -v -race -count 1 ./webhook/ - -test-cleanup: - @echo "Cleaning up..." - @helm uninstall cosignwebhook -n cosignwebhook - @k3d registry delete k3d-registry.localhost - @k3d cluster delete cosign-tests \ No newline at end of file +e2e-prep: e2e-cluster e2e-keys e2e-images e2e-deploy diff --git a/test/cases/fail-1.yaml b/test/cases/fail-1.yaml deleted file mode 100644 index 5f779003..00000000 --- a/test/cases/fail-1.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# one container with malformed public key in environment -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-fail-1 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - badKey666 - -----END PUBLIC KEY----- diff --git a/test/cases/fail-2.yaml b/test/cases/fail-2.yaml deleted file mode 100644 index 882ad369..00000000 --- a/test/cases/fail-2.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# two containers, the second one has a malformed pub key -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-fail-2 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D - PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== - -----END PUBLIC KEY----- - - name: busybox-container2 - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - badKeyagain! - -----END PUBLIC KEY----- diff --git a/test/cases/fail-3.yaml b/test/cases/fail-3.yaml deleted file mode 100644 index c2481ae0..00000000 --- a/test/cases/fail-3.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# one container with a non-existent secret reference to a public key -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-fail-3 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - valueFrom: - secretKeyRef: - name: non-existent-secret - key: cosign.pub diff --git a/test/cases/fail-4.yaml b/test/cases/fail-4.yaml deleted file mode 100644 index c6f403a9..00000000 --- a/test/cases/fail-4.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# two containers, the second one has a non-existent secret reference to a public key -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-fail-4 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmRU6zHit0hoOmHYNjNpnXyP2IF9D - PvgIuqD33cDiiHY7xS8dkmMFaCpXjkfMOcdKM4riq6XiZvTkEDpjCgHelw== - -----END PUBLIC KEY----- - - name: busybox-container2 - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] - env: - - name: COSIGNPUBKEY - valueFrom: - secretKeyRef: - name: non-existent-secret - key: non-existent-key diff --git a/test/cases/ok-1.yaml b/test/cases/ok-1.yaml deleted file mode 100644 index 91aa82bc..00000000 --- a/test/cases/ok-1.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# one container with its public key in environment -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-ok-1 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEilKZPJAeodknMNw4krsJhMfPom4S - FziLqM8UHYfhM7LSUwNL98OZh06ALzEJ3nlfTY/Lyvspdmdav4YHQnoMNg== - -----END PUBLIC KEY----- diff --git a/test/cases/ok-2.yaml b/test/cases/ok-2.yaml deleted file mode 100644 index 6b732e1b..00000000 --- a/test/cases/ok-2.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# two containers with their public keys in environment -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-ok-2 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEilKZPJAeodknMNw4krsJhMfPom4S - FziLqM8UHYfhM7LSUwNL98OZh06ALzEJ3nlfTY/Lyvspdmdav4YHQnoMNg== - -----END PUBLIC KEY----- - - name: busybox-container2 - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEilKZPJAeodknMNw4krsJhMfPom4S - FziLqM8UHYfhM7LSUwNL98OZh06ALzEJ3nlfTY/Lyvspdmdav4YHQnoMNg== - -----END PUBLIC KEY----- \ No newline at end of file diff --git a/test/cases/ok-3.yaml b/test/cases/ok-3.yaml deleted file mode 100644 index 581714d7..00000000 --- a/test/cases/ok-3.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# one container with its public key in secret ref -apiVersion: v1 -kind: Secret -metadata: - name: cosign-pubkey - namespace: test-cases -type: Opaque -data: - pubkey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaWxLWlBKQWVvZGtuTU53NGtyc0poTWZQb200UwpGemlMcU04VUhZZmhNN0xTVXdOTDk4T1poMDZBTHpFSjNubGZUWS9MeXZzcGRtZGF2NFlIUW5vTU5nPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-ok-3 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - valueFrom: - secretKeyRef: - name: cosign-pubkey - key: pubkey diff --git a/test/cases/ok-4.yaml b/test/cases/ok-4.yaml deleted file mode 100644 index 7e98f27e..00000000 --- a/test/cases/ok-4.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# two containers with its public key in secret ref and environment -apiVersion: v1 -kind: Secret -metadata: - name: cosign-pubkey - namespace: test-cases -type: Opaque -data: - pubkey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaWxLWlBKQWVvZGtuTU53NGtyc0poTWZQb200UwpGemlMcU04VUhZZmhNN0xTVXdOTDk4T1poMDZBTHpFSjNubGZUWS9MeXZzcGRtZGF2NFlIUW5vTU5nPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-ok-4 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - valueFrom: - secretKeyRef: - name: cosign-pubkey - key: pubkey - - name: busybox-container2 - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEilKZPJAeodknMNw4krsJhMfPom4S - FziLqM8UHYfhM7LSUwNL98OZh06ALzEJ3nlfTY/Lyvspdmdav4YHQnoMNg== - -----END PUBLIC KEY----- \ No newline at end of file diff --git a/test/cases/ok-5.yaml b/test/cases/ok-5.yaml deleted file mode 100644 index 4ee4185b..00000000 --- a/test/cases/ok-5.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# two containers signed with different keys with their public keys in secret ref and env var -apiVersion: v1 -kind: Secret -metadata: - name: cosign-pubkey - namespace: test-cases -type: Opaque -data: - pubkey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaWxLWlBKQWVvZGtuTU53NGtyc0poTWZQb200UwpGemlMcU04VUhZZmhNN0xTVXdOTDk4T1poMDZBTHpFSjNubGZUWS9MeXZzcGRtZGF2NFlIUW5vTU5nPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-ok-5 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - containers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "while true; do echo 'i am the one'; sleep 3600; done"] - env: - - name: COSIGNPUBKEY - valueFrom: - secretKeyRef: - name: cosign-pubkey - key: pubkey - - name: busybox-container2 - image: k3d-registry.localhost:5000/busybox:second - command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXpsuwGT77BFMY6oXiivnytKEdu7g - lSSZim8OgipDSU6t2xHrmAPVhBcWeHGqbpjYn+/Av1RunIt81IBItYZIzQ== - -----END PUBLIC KEY----- diff --git a/test/cases/ok-6.yaml b/test/cases/ok-6.yaml deleted file mode 100644 index 79ef3bc5..00000000 --- a/test/cases/ok-6.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# two containers (one init) signed with different keys with their public keys in secret ref and env var -apiVersion: v1 -kind: Secret -metadata: - name: cosign-pubkey - namespace: test-cases -type: Opaque -data: - pubkey: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaWxLWlBKQWVvZGtuTU53NGtyc0poTWZQb200UwpGemlMcU04VUhZZmhNN0xTVXdOTDk4T1poMDZBTHpFSjNubGZUWS9MeXZzcGRtZGF2NFlIUW5vTU5nPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg== ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-case-ok-6 - namespace: test-cases -spec: - replicas: 1 - selector: - matchLabels: - app: busybox - template: - metadata: - labels: - app: busybox - spec: - initContainers: - - name: busybox-container - image: k3d-registry.localhost:5000/busybox:latest - command: ["/bin/sh", "-c", "echo 'i am the one that must run first!'; sleep 5"] - env: - - name: COSIGNPUBKEY - valueFrom: - secretKeyRef: - name: cosign-pubkey - key: pubkey - containers: - - name: busybox-container2 - image: k3d-registry.localhost:5000/busybox:second - command: ["/bin/sh", "-c", "while true; do echo 'i am the second one'; sleep 30; done"] - env: - - name: COSIGNPUBKEY - value: | - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXpsuwGT77BFMY6oXiivnytKEdu7g - lSSZim8OgipDSU6t2xHrmAPVhBcWeHGqbpjYn+/Av1RunIt81IBItYZIzQ== - -----END PUBLIC KEY-----