From 44b4cc62e5c840b80b2cf55ee35e9c9df0dda2ad Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Thu, 31 Oct 2024 13:20:24 -0400 Subject: [PATCH 1/4] Add check for ACR registry in ACR credential helper (#1658) Signed-off-by: Cody Soyland Signed-off-by: Senan Zedan (EXT-Nokia) --- pkg/webhook/registryauth/azure/acrhelper.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/webhook/registryauth/azure/acrhelper.go b/pkg/webhook/registryauth/azure/acrhelper.go index 87b996519..c7054c6e0 100644 --- a/pkg/webhook/registryauth/azure/acrhelper.go +++ b/pkg/webhook/registryauth/azure/acrhelper.go @@ -20,6 +20,7 @@ import ( "fmt" "net/url" "os" + "strings" "github.com/Azure/azure-sdk-for-go/profiles/preview/preview/containerregistry/runtime/containerregistry" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" @@ -42,6 +43,10 @@ func (a ACRHelper) Delete(_ string) error { } func (a ACRHelper) Get(registryURL string) (string, string, error) { + if !isACR(registryURL) { + return "", "", fmt.Errorf("not an ACR registry") + } + azCred, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { return "", "", fmt.Errorf("failed to obtain a credential: %w", err) @@ -81,3 +86,7 @@ func (a ACRHelper) Get(registryURL string) (string, string, error) { func (a ACRHelper) List() (map[string]string, error) { return nil, fmt.Errorf("list is unimplemented") } + +func isACR(registryURL string) bool { + return strings.HasSuffix(registryURL, ".azurecr.io") +} From 7895e28ab67c00f83c6594620115c197cd4e08ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:20:44 +0000 Subject: [PATCH 2/4] chore(deps): Bump google-github-actions/auth from 2.1.6 to 2.1.7 (#1683) Bumps [google-github-actions/auth](https://github.com/google-github-actions/auth) from 2.1.6 to 2.1.7. - [Release notes](https://github.com/google-github-actions/auth/releases) - [Changelog](https://github.com/google-github-actions/auth/blob/main/CHANGELOG.md) - [Commits](https://github.com/google-github-actions/auth/compare/8254fb75a33b976a221574d287e93919e6a36f70...6fc4af4b145ae7821d527454aa9bd537d1f2dc5f) --- updated-dependencies: - dependency-name: google-github-actions/auth dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: Senan Zedan (EXT-Nokia) --- .github/workflows/build.yaml | 2 +- .github/workflows/release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 80b3e1642..e174fde2e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -48,7 +48,7 @@ jobs: - uses: chainguard-dev/actions/goimports@dacf41f3472c33979cfd49bca5b503236be57de0 # main - name: Set up Cloud SDK - uses: google-github-actions/auth@8254fb75a33b976a221574d287e93919e6a36f70 # v2.1.6 + uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7 with: workload_identity_provider: 'projects/498091336538/locations/global/workloadIdentityPools/githubactions/providers/sigstore-policy-controller' service_account: 'gha-policy-controller@projectsigstore.iam.gserviceaccount.com' diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d0816022a..f370fb0ee 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -42,7 +42,7 @@ jobs: - uses: ko-build/setup-ko@3aebd0597dc1e9d1a26bcfdb7cbeb19c131d3037 # v0.7 - name: Set up Cloud SDK - uses: google-github-actions/auth@8254fb75a33b976a221574d287e93919e6a36f70 # v2.1.6 + uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7 with: workload_identity_provider: 'projects/498091336538/locations/global/workloadIdentityPools/githubactions/providers/sigstore-policy-controller' service_account: 'gha-policy-controller@projectsigstore.iam.gserviceaccount.com' From 513fa0ccf41043051363bebbb2fc9e1669677024 Mon Sep 17 00:00:00 2001 From: "Senan Zedan (EXT-Nokia)" Date: Sun, 3 Nov 2024 14:46:27 +0200 Subject: [PATCH 3/4] Control the policy controller monitoring resources from chart, enhancment for the current implmentation to hadd all the resources by default, The new change add avail to pass resourcesNames through the chart with list of resources comma sperataed for which resources to be monitored by the policy controller, the default is all resources if the flag wasn't presented in the chart Signed-off-by: Senan Zedan (EXT-Nokia) --- cmd/webhook/main.go | 50 ++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 52f329caa..6e583e2ab 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -21,6 +21,7 @@ import ( "fmt" "log" "os" + "strings" "time" policyduckv1beta1 "github.com/sigstore/policy-controller/pkg/apis/duck/v1beta1" @@ -78,6 +79,13 @@ var ( // https://github.com/sigstore/policy-controller/issues/354 disableTUF = flag.Bool("disable-tuf", false, "Disable TUF support.") + // Validate specific resources. + // https://github.com/sigstore/policy-controller/issues/1388 + resourcesNames = flag.String("resource-name", "replicasets, deployments, pods, cronjobs, jobs, statefulsets, daemonsets", "Comma-separated list of resources") + // Split the input string into a slice of strings + listResources []string + types map[schema.GroupVersionKind]resourcesemantics.GenericCRD + // mutatingCIPWebhookName holds the name of the mutating webhook configuration // resource dispatching admission requests to policy-webhook. // It is also the name of the webhook which is injected by the controller @@ -116,6 +124,8 @@ func main() { flag.IntVar(&opts.Port, "secure-port", opts.Port, "The port on which to serve HTTPS.") flag.Parse() + resourcesNamesList := strings.Split(*resourcesNames, ",") + listResources = append(listResources, resourcesNamesList...) // If TUF has been disabled do not try to set it up. if !*disableTUF { @@ -140,8 +150,7 @@ func main() { // This must match the set of resources we configure in // cmd/webhook/main.go in the "types" map. - common.ValidResourceNames = sets.NewString("replicasets", "deployments", - "pods", "cronjobs", "jobs", "statefulsets", "daemonsets") + common.ValidResourceNames = sets.NewString(resourcesNamesList...) v := version.GetVersionInfo() vJSON, _ := v.JSONString() @@ -198,17 +207,29 @@ func (c *crdEphemeralContainers) SupportedVerbs() []admissionregistrationv1.Oper } } -var types = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{ - corev1.SchemeGroupVersion.WithKind("Pod"): &crdEphemeralContainers{GenericCRD: &duckv1.Pod{}}, - - appsv1.SchemeGroupVersion.WithKind("ReplicaSet"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}}, - appsv1.SchemeGroupVersion.WithKind("Deployment"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}}, - appsv1.SchemeGroupVersion.WithKind("StatefulSet"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}}, - appsv1.SchemeGroupVersion.WithKind("DaemonSet"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.WithPod{}}, - batchv1.SchemeGroupVersion.WithKind("Job"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.WithPod{}}, - - batchv1.SchemeGroupVersion.WithKind("CronJob"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.CronJob{}}, - batchv1beta1.SchemeGroupVersion.WithKind("CronJob"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.CronJob{}}, +func createTypesMap(kindsList []string) map[schema.GroupVersionKind]resourcesemantics.GenericCRD { + types := make(map[schema.GroupVersionKind]resourcesemantics.GenericCRD) + for _, kind := range kindsList { + kind = strings.TrimSpace(kind) + switch kind { + case "pods": + types[corev1.SchemeGroupVersion.WithKind("Pod")] = &crdEphemeralContainers{GenericCRD: &duckv1.Pod{}} + case "replicasets": + types[appsv1.SchemeGroupVersion.WithKind("ReplicaSet")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}} + case "deployments": + types[appsv1.SchemeGroupVersion.WithKind("Deployment")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}} + case "statefulsets": + types[appsv1.SchemeGroupVersion.WithKind("StatefulSet")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}} + case "daemonsets": + types[appsv1.SchemeGroupVersion.WithKind("DaemonSet")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.WithPod{}} + case "jobs": + types[batchv1.SchemeGroupVersion.WithKind("Job")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.WithPod{}} + case "cronjobs": + types[batchv1.SchemeGroupVersion.WithKind("CronJob")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.CronJob{}} + types[batchv1beta1.SchemeGroupVersion.WithKind("CronJob")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.CronJob{}} + } + } + return types } var typesCIP = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{ @@ -221,6 +242,7 @@ var typesCIP = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{ func NewValidatingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl { // Decorate contexts with the current state of the config. + types = createTypesMap(listResources) store := config.NewStore(logging.FromContext(ctx).Named("config-store")) store.WatchConfigs(cmw) policyControllerConfigStore := policycontrollerconfig.NewStore(logging.FromContext(ctx).Named("config-policy-controller")) @@ -278,7 +300,7 @@ func NewMutatingAdmissionController(ctx context.Context, _ configmap.Watcher) *c } ctx = webhook.WithOptions(ctx, *woptions) validator := cwebhook.NewValidator(ctx) - + types = createTypesMap(listResources) return defaulting.NewAdmissionController(ctx, // Name of the resource webhook. *webhookName, From 8bf379c67057bed49123ecc29f235fc9ca5851f5 Mon Sep 17 00:00:00 2001 From: "Senan Zedan (EXT-Nokia)" Date: Thu, 12 Dec 2024 14:36:16 +0200 Subject: [PATCH 4/4] --amend --- .../kind-cluster-custom-resources.yaml | 34 +---------- test/e2e_test_policy_custom_resource.sh | 58 +++++++++++++++++-- .../kustomization.yaml | 5 +- .../test-deployment-with-custom-resource.yaml | 12 ++-- 4 files changed, 60 insertions(+), 49 deletions(-) diff --git a/.github/workflows/kind-cluster-custom-resources.yaml b/.github/workflows/kind-cluster-custom-resources.yaml index bdaa33d15..dcdfd59c6 100644 --- a/.github/workflows/kind-cluster-custom-resources.yaml +++ b/.github/workflows/kind-cluster-custom-resources.yaml @@ -26,7 +26,7 @@ permissions: read-all jobs: cip-test-trustroot-bring-your-own-keys: - name: ClusterImagePolicy e2e tests with TrustRoot - Bring Your Own Keys + name: ClusterImagePolicy e2e tests with custom resources runs-on: ubuntu-latest strategy: @@ -138,38 +138,6 @@ jobs: kubectl rollout status --timeout 5m --namespace cosign-system deployments/webhook echo "TUF_ROOT_FILE=./root.json" >> $GITHUB_ENV - - name: Checkout TSA for testing. - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v3.0.2 - with: - repository: sigstore/timestamp-authority - path: ./src/github.com/sigstore/timestamp-authority - - - name: Build timestamp-cli - working-directory: ./src/github.com/sigstore/timestamp-authority - run: | - go build -o ./timestamp-cli ./cmd/timestamp-cli - - - name: Exercise our local TSA - working-directory: ./src/github.com/sigstore/timestamp-authority - run: | - TSA_URL=$(kubectl -n tsa-system get ksvc tsa -ojsonpath='{.status.url}') - echo "TSA_URL=$TSA_URL" >> $GITHUB_ENV - - curl $TSA_URL/api/v1/timestamp/certchain > ts_chain.pem - echo "myblob" > myblob - if ! ./timestamp-cli --timestamp_server $TSA_URL timestamp --hash sha256 --artifact myblob --out response.tsr ; then - echo "failed to timestamp artifact" - exit -1 - fi - if ! ./timestamp-cli verify --timestamp response.tsr --artifact "myblob" --certificate-chain ts_chain.pem ; then - echo "failed to verify timestamp" - exit -1 - fi - if ! ./timestamp-cli inspect --timestamp response.tsr --format json ; then - echo "failed to inspect the timestamp" - exit -1 - fi - - name: Run Custom resources Tests timeout-minutes: 5 run: | diff --git a/test/e2e_test_policy_custom_resource.sh b/test/e2e_test_policy_custom_resource.sh index 457abb93b..f2d608210 100644 --- a/test/e2e_test_policy_custom_resource.sh +++ b/test/e2e_test_policy_custom_resource.sh @@ -22,7 +22,7 @@ if [[ -z "${KO_DOCKER_REPO}" ]]; then fi # Variables -export CUSTOM_RESOURCE="Pods,ReplicaSets" +export CUSTOM_RESOURCE="DaemonSet,ReplicaSets" export NS=custom-resource-test export TIMESTAMP="TIMESTAMP" @@ -39,30 +39,76 @@ assert_webhook_configuration() { echo "Resource ${resource} found in ${webhook_name}" } +# Helper function to check if an image uses a tag +assert_image_tag() { + local pod_name=$1 + local container_name=$2 + + echo "Checking that ${pod_name}/${container_name} uses a tag, not a digest" + image=$(kubectl get pod "${pod_name}" -n "${NS}" -o=jsonpath="{.spec.containers[?(@.name=='${container_name}')].image}") + if [[ "${image}" =~ @sha256:[a-f0-9]{64} ]]; then + echo "Image ${image} is using a digest, which is not allowed" + exit 1 + else + echo "Image ${image} is correctly using a tag" + fi +} + +# Step 1: Create namespace echo '::group:: Create and label namespace for testing' kubectl create namespace ${NS} kubectl label namespace ${NS} policy.sigstore.dev/include=true echo '::endgroup::' +# Step 2: Deploy Policy Controller with custom-resource flag +echo '::group:: Deploy Policy Controller with custom-resource flag' +KO_DOCKER_REPO=${KO_DOCKER_REPO} ko apply -f ./deploy/manifests.yaml \ + --set-string policyController.customResource=${CUSTOM_RESOURCE} +echo '::endgroup::' + +# Step 3: Validate webhook configurations echo '::group:: Validate webhook configurations' sleep 5 # Allow webhook configurations to propagate -for resource in Pods ReplicaSets; do +for resource in DaemonSet ReplicaSets; do assert_webhook_configuration "MutatingWebhookConfiguration" "${resource}" assert_webhook_configuration "ValidatingWebhookConfiguration" "${resource}" done # Ensure a non-monitored resource is NOT included -if kubectl get MutatingWebhookConfiguration -o yaml | grep -q "resources:.*DaemonSet"; then - echo "DaemonSet should not be included in MutatingWebhookConfiguration" +if kubectl get MutatingWebhookConfiguration -o yaml | grep -q "resources:.*Pods"; then + echo "Pods should not be included in MutatingWebhookConfiguration" exit 1 else - echo "DaemonSet correctly excluded from MutatingWebhookConfiguration" + echo "Pods correctly excluded from MutatingWebhookConfiguration" fi echo '::endgroup::' +# Step 4: Deploy sandbox and check for image tag usage +echo '::group:: Deploy sandbox and check image tags' +cat <