From 67fccbd583cf508890f1b4117f4ee6ff5fd19853 Mon Sep 17 00:00:00 2001 From: Philipp Belitz Date: Thu, 22 Feb 2024 14:23:20 +0100 Subject: [PATCH] feat: golang switch Switches the project from Python to Golang. This commit includes the following changes: - validation mode - redis caching - resource validation mode - notary: support for all TUF keys - unified "*" trustRoot option - update of cosign to 2.2.3 --- .dockerignore | 24 +- .../.kube-linter}/config.yaml | 0 .github/PULL_REQUEST_TEMPLATE.md | 1 - .github/actions/build/action.yml | 19 +- .github/actions/context/action.yaml | 16 +- .github/actions/k3s-cluster/action.yaml | 1 - .../actions/k8s-version-config/action.yaml | 4 +- .github/actions/safety/action.yaml | 19 - .github/actions/setup-notary/action.yaml | 6 +- .github/actions/trivy-config/action.yaml | 25 +- .github/dependabot.yml | 8 +- .github/workflows/.reusable-build.yml | 25 +- .github/workflows/.reusable-ci.yml | 177 +++ .github/workflows/.reusable-compliance.yml | 19 +- .github/workflows/.reusable-docs.yaml | 11 +- .../workflows/.reusable-integration-test.yml | 186 +-- .github/workflows/.reusable-sast.yml | 221 ++-- .github/workflows/.reusable-sca.yml | 27 +- .github/workflows/.reusable-unit-test.yml | 28 +- .github/workflows/cicd.yaml | 96 -- .github/workflows/nightly-build.yaml | 63 +- .github/workflows/nightly.yaml | 50 +- .github/workflows/pr.yml | 41 + .github/workflows/push.yml | 41 + .github/workflows/release.yaml | 12 +- .gitignore | 26 +- .pylintrc | 501 -------- Makefile | 50 +- build/Dockerfile | 26 + {helm => charts/connaisseur}/.helmignore | 1 + {helm => charts/connaisseur}/Chart.yaml | 12 +- .../alert_payload_templates/ecs-1-12-0.json | 0 .../alert_payload_templates/keybase.json | 0 .../alert_payload_templates/msteams.json | 0 .../alert_payload_templates/opsgenie.json | 0 .../alert_payload_templates/slack.json | 0 charts/connaisseur/templates/_helpers.tpl | 372 ++++++ charts/connaisseur/templates/configmaps.yaml | 46 + charts/connaisseur/templates/deployment.yaml | 100 ++ charts/connaisseur/templates/env.yaml | 30 + charts/connaisseur/templates/redis.yaml | 135 +++ charts/connaisseur/templates/role.yaml | 31 + charts/connaisseur/templates/rolebinding.yaml | 33 + charts/connaisseur/templates/secrets.yaml | 16 + .../connaisseur}/templates/service.yaml | 9 +- .../connaisseur/templates/serviceaccount.yaml | 11 + .../templates/webhook+certificate.yaml | 90 ++ charts/connaisseur/values.yaml | 186 +++ cmd/connaisseur/main.go | 116 ++ connaisseur/__main__.py | 48 - connaisseur/admission_request.py | 42 - connaisseur/alert.py | 229 ---- connaisseur/config.py | 215 ---- connaisseur/constants.py | 7 - connaisseur/exceptions.py | 143 --- connaisseur/flask_application.py | 260 ----- connaisseur/image.py | 96 -- connaisseur/kube_api.py | 33 - connaisseur/logging.py | 74 -- connaisseur/res/ad_request_schema.json | 108 -- connaisseur/res/alertconfig_schema.json | 92 -- connaisseur/res/config_schema.json | 269 ----- connaisseur/res/root_schema.json | 194 ---- connaisseur/res/snapshot_schema.json | 87 -- connaisseur/res/targets_schema.json | 167 --- connaisseur/res/timestamp_schema.json | 89 -- connaisseur/res/trust_schema.json | 59 - connaisseur/trust_root.py | 96 -- connaisseur/util.py | 141 --- .../validators/cosign/cosign_validator.py | 449 ------- connaisseur/validators/interface.py | 21 - connaisseur/validators/notaryv1/key_store.py | 75 -- connaisseur/validators/notaryv1/notary.py | 302 ----- .../validators/notaryv1/notaryv1_validator.py | 318 ----- connaisseur/validators/notaryv1/trust_data.py | 201 ---- connaisseur/validators/notaryv1/tuf_role.py | 27 - .../validators/notaryv2/notaryv2_validator.py | 7 - .../validators/static/static_validator.py | 21 - connaisseur/validators/validator.py | 22 - connaisseur/workload_object.py | 143 --- docker/Dockerfile | 49 - docker/Dockerfile.getRoot | 16 - docker/harden.sh | 27 - docker/release-cosign.pub | 4 - docs/CONTRIBUTING.md | 35 +- docs/README.md | 2 +- docs/adr/ADR-3_multi_notary_config.md | 2 +- docs/adr/ADR-8_go-transition.md | 60 + docs/adr/ADR-9_multi-pod.md | 77 ++ docs/assets/connaisseur_demo.mp4 | Bin 0 -> 6980676 bytes docs/basics.md | 55 +- docs/features/README.md | 2 + docs/features/alerting.md | 7 +- docs/features/automatic_child_approval.md | 20 +- docs/features/automatic_unchanged_approval.md | 2 +- docs/features/caching.md | 3 + docs/features/detection_mode.md | 10 +- docs/features/namespaced_validation.md | 6 +- docs/features/resource_validation_mode.md | 14 + docs/getting_started.md | 12 +- docs/validators/authentication.md | 7 + docs/validators/notaryv1.md | 31 +- docs/validators/sigstore_cosign.md | 36 +- go.mod | 274 +++++ go.sum | 1033 +++++++++++++++++ helm | 1 + helm/README.md | 52 - helm/templates/_helpers.tpl | 227 ---- helm/templates/alertconfig.yaml | 29 - helm/templates/certificate_webhook-conf.yaml | 135 --- helm/templates/config-secrets.yaml | 27 - helm/templates/config.yaml | 25 - helm/templates/deployment.yaml | 146 --- helm/templates/env.yaml | 36 - helm/templates/podsecuritypolicy.yaml | 36 - helm/templates/role.yaml | 23 - helm/templates/rolebinding.yaml | 16 - helm/templates/serviceaccount.yaml | 12 - helm/values.yaml | 247 ---- internal/alert/alerting.go | 104 ++ internal/alert/alerting_test.go | 121 ++ internal/alert/channel.go | 208 ++++ internal/alert/channel_test.go | 342 ++++++ internal/alert/config.go | 91 ++ internal/alert/config_test.go | 173 +++ internal/alert/template.go | 50 + internal/alert/template_test.go | 115 ++ internal/caching/caching.go | 94 ++ internal/caching/caching_test.go | 82 ++ internal/config/config.go | 114 ++ internal/config/config_test.go | 488 ++++++++ internal/constants/constants.go | 82 ++ internal/handler/admission_metrics.go | 42 + internal/handler/admission_metrics_test.go | 22 + internal/handler/health.go | 16 + internal/handler/health_test.go | 29 + internal/handler/mutate.go | 270 +++++ internal/handler/mutate_test.go | 659 +++++++++++ internal/handler/start.go | 25 + internal/handler/start_test.go | 42 + internal/handler/validation/skip.go | 128 ++ internal/handler/validation/skip_test.go | 343 ++++++ internal/handler/validation/validation.go | 256 ++++ .../handler/validation/validation_metrics.go | 56 + .../validation/validation_metrics_test.go | 40 + .../handler/validation/validation_test.go | 466 ++++++++ internal/image/image.go | 110 ++ internal/image/image_test.go | 278 +++++ internal/image/registry.go | 65 ++ internal/image/registry_test.go | 68 ++ internal/kubernetes/admission_objects.go | 54 + internal/kubernetes/admission_objects_test.go | 136 +++ internal/kubernetes/deserializer.go | 19 + internal/kubernetes/kube_api.go | 36 + internal/kubernetes/kube_api_test.go | 126 ++ internal/kubernetes/workload_object.go | 325 ++++++ internal/kubernetes/workload_object_test.go | 875 ++++++++++++++ internal/policy/match.go | 74 ++ internal/policy/match_test.go | 86 ++ internal/policy/rule.go | 29 + internal/utils/feature.go | 41 + internal/utils/feature_test.go | 70 ++ internal/utils/files.go | 45 + internal/utils/files_test.go | 111 ++ internal/utils/int.go | 8 + internal/utils/int_test.go | 31 + internal/utils/list.go | 48 + internal/utils/list_test.go | 100 ++ internal/utils/logging.go | 83 ++ internal/utils/logging_test.go | 203 ++++ internal/utils/strings.go | 93 ++ internal/utils/strings_test.go | 207 ++++ internal/utils/validation.go | 55 + internal/utils/validation_test.go | 59 + internal/validator/auth/auth.go | 160 +++ internal/validator/auth/auth_test.go | 173 +++ internal/validator/auth/trust_root.go | 53 + internal/validator/auth/trust_root_test.go | 105 ++ .../cosignvalidator/cosign_validator.go | 389 +++++++ .../cosignvalidator/cosign_validator_test.go | 419 +++++++ .../validator/notaryv1/notaryserver/client.go | 263 +++++ .../notaryv1/notaryserver/client_test.go | 319 +++++ .../validator/notaryv1/notaryserver/repo.go | 379 ++++++ .../notaryv1/notaryserver/repo_test.go | 429 +++++++ .../notaryserver/simple_cred_store.go | 24 + .../notaryserver/simple_cred_store_test.go | 17 + .../validator/notaryv1/notaryv1_validator.go | 282 +++++ .../notaryv1/notaryv1_validator_test.go | 550 +++++++++ .../validator/notaryv2/notaryv2_validator.go | 162 +++ internal/validator/notaryv2/trust_store.go | 46 + .../staticvalidator/static_validator.go | 30 + .../staticvalidator/static_validator_test.go | 59 + internal/validator/validator.go | 90 ++ internal/validator/validator_test.go | 114 ++ mkdocs.yml | 5 +- requirements.txt | 16 - requirements_dev.txt | 11 - scripts/get_root_key.py | 59 - setup.py | 3 - test/integration/Dockerfile.populate_notary | 16 + {tests => test}/integration/README.md | 6 +- .../integration/alerting/Dockerfile | 0 .../integration/alerting/app/alert_checker.py | 0 .../alerting/app/keybase_payload.json | 0 .../alerting/app/opsgenie_payload.json | 0 .../alerting/app/slack_payload.json | 0 {tests => test}/integration/cases.yaml | 106 +- test/integration/cause_load.sh | 9 + {tests => test}/integration/cleanup.sh | 6 +- test/integration/cosign.pub | 4 + .../integration/deployments/complexity.yaml | 0 .../deployments/deployment_i1.yaml | 0 .../deployments/deployment_i1n1.yaml | 0 .../deployments/deployment_i1u1.yaml | 0 .../deployments/deployment_i2.yaml | 0 .../deployments/deployment_i2i.yaml | 0 .../deployments/deployment_i2u1-1.yaml | 0 .../deployments/deployment_i2u1-2.yaml | 0 .../deployments/deployment_i2u2.yaml | 0 .../deployments/deployment_i2ui.yaml | 0 ...ner_with_unsigned_init_container_image.yml | 2 - .../deployments/valid_init_container.yaml | 2 - {tests => test}/integration/ghcr-values.yaml | 0 .../integration/integration-test.sh | 107 +- .../integration/loadtest.yaml.template | 0 {tests => test}/integration/notary_addhash.sh | 2 +- {tests => test}/integration/notary_init.sh | 2 +- .../config/client_config.json | 0 .../notary_service_container/server/ca.crt | 0 .../server/config.json | 0 .../server/notary-server.crt | 0 .../server/notary-server.key | 0 .../notary_service_container/signer/ca.crt | 0 .../signer/config.json | 0 .../signer/notary-signer.crt | 0 .../signer/notary-signer.key | 0 test/integration/redis-cert/install.yaml | 16 + test/integration/redis-cert/redis_cert.sh | 10 + test/integration/redis-cert/update.yaml | 55 + .../integration/run_integration_tests.sh | 20 +- {tests => test}/integration/tls.cert | 0 {tests => test}/integration/tls.key | 0 test/integration/update-alerting.yaml | 32 + {tests => test}/integration/update-cert.yaml | 0 .../integration/update-for-workloads.yaml | 0 .../update-self-hosted-notary.yaml | 8 +- test/integration/update.yaml | 142 +++ .../update_deployment_notary_test.yaml | 0 {tests => test}/integration/var-img.yaml | 0 .../integration/workload-objects/CronJob.yaml | 0 .../workload-objects/DaemonSet.yaml | 0 .../workload-objects/Deployment.yaml | 0 .../integration/workload-objects/Job.yaml | 0 .../integration/workload-objects/Pod.yaml | 0 .../workload-objects/ReplicaSet.yaml | 0 .../ReplicationController.yaml | 0 .../workload-objects/StatefulSet.yaml | 0 .../admission_requests/01_deployment.json | 2 - .../testdata/admission_requests/02_pod.json | 3 +- .../admission_requests/03_replicaset.json | 2 - .../admission_requests/04_cronjob.json | 8 +- .../05_replication_controller.json | 101 ++ .../admission_requests/06_daemonset.json | 142 +++ .../admission_requests/07_statefulset.json | 142 +++ test/testdata/admission_requests/08_job.json | 93 ++ .../09_dply_multi_image.json | 2 - .../admission_requests/10_update.json | 2 +- test/testdata/admission_requests/11_role.json | 70 ++ .../admission_requests/12_err_pod.json | 33 + .../admission_requests/13_unknown_parent.json | 4 +- .../admission_requests/14_deny_me_job.json | 93 ++ .../admission_requests/15_allow_me_job.json | 93 ++ .../admission_requests/16_mismatch_uid.json | 7 +- .../admission_requests/17_deny_me_pod.json | 111 ++ .../admission_requests/18_allow_me_pod.json | 111 ++ .../19_pod_initcontainer.json | 134 +++ .../20_pod_ephemeralcontainer.json | 121 ++ .../21_broken_admission_request.json | 7 + .../22_allow_me_with_index_docker_io.json | 5 +- .../23_alice_digest_dpl.json | 4 +- .../24_alice_tag_digest_dpl.json | 98 ++ .../admission_requests/25_null_request.json | 1 + test/testdata/alerts/00_template.tpl | 7 + .../testdata/alerts/01_empty.tpl | 0 test/testdata/alerts/02_faulty_template.tpl | 6 + test/testdata/alerts/03_fail_template.tpl | 1 + test/testdata/alerts/04_allow_no_header.tpl | 1 + test/testdata/alerts/05_render_err.tpl | 1 + test/testdata/alerts/06_template.json | 4 + test/testdata/alerts/07_opsgenie.json | 18 + test/testdata/alerts/08_render_err.json | 3 + .../testdata/alerts/09_empty.json | 0 test/testdata/alerts/10_allow_no_header.json | 3 + test/testdata/alerts/11_fail_template.json | 1 + test/testdata/alerts/12_send_notif_test.json | 4 + test/testdata/alerts/13_priority.json | 3 + test/testdata/alerts/14_triple_bracket.json | 3 + test/testdata/alerts/15_mismatch_bracket.json | 3 + test/testdata/alerts/16_single_bracket.json | 3 + .../testdata/alerts/alerting/00_alerting.yaml | 28 + .../alerts/alerting/01_unmarshal_err.yaml | 1 + .../02_requestsender_unmarshal_err.yaml | 5 + .../alerts/alerting/03_send_notif_test.yaml | 11 + .../alerting/04_send_notif_no_cluster.yaml | 10 + .../05_send_notif_tmpl_not_found.yaml | 8 + test/testdata/alerts/alerting/06_empty.yaml | 1 + .../07_send_notif_success_and_fail.yaml | 8 + .../alerting/08_send_notif_multiple_ch.yaml | 13 + test/testdata/alerts/channel/00_channel.yaml | 14 + test/testdata/auth/01_auth.yaml | 1 + test/testdata/auth/01_auth/secret.yaml | 3 + test/testdata/auth/02_keychain.yaml | 1 + .../testdata/auth/03_empty.yaml | 0 test/testdata/auth/04_unable_to_find.yaml | 1 + test/testdata/auth/05_err_reading_secret.yaml | 1 + .../auth/05_err_reading_secret/secret.yaml | 2 + test/testdata/auth/06_unmarshal_err.yaml | 1 + test/testdata/auth/07_no_secret.yaml | 1 + test/testdata/auth/08_docker_auth.yaml | 1 + .../auth/08_docker_auth/.dockerconfigjson | 13 + test/testdata/auth/09_auth_and_keychain.yaml | 2 + test/testdata/auth/10_multi_auth_files.yaml | 1 + .../10_multi_auth_files/.dockerconfigjson | 10 + .../auth/10_multi_auth_files/secret.yaml | 3 + test/testdata/auth/11_no_auth_files.yaml | 1 + .../testdata/auth/12_invalid_secret_file.yaml | 1 + .../auth/12_invalid_secret_file/terces.yaml | 2 + test/testdata/auth/13_no_registry_auth.yaml | 1 + .../auth/13_no_registry_auth/secret.yaml | 2 + .../auth/14_invalid_registry_docker_auth.yaml | 1 + .../.dockerconfigjson | 7 + .../testdata/auth/15_default_auth/secret.yaml | 3 + test/testdata/caching/00_cert/tls.crt | 23 + test/testdata/caching/01_err/tls.crt | 1 + test/testdata/config/00_sample.yaml | 63 + test/testdata/config/01_val_no_name.yaml | 6 + test/testdata/config/02_val_no_type.yaml | 6 + test/testdata/config/03_unsupported_type.yaml | 7 + .../config/04_unmarshal_cosign_error.yaml | 6 + test/testdata/config/05_invalid_cosign.yaml | 6 + .../config/06_unmarshal_static_error.yaml | 4 + .../testdata/config/07_invalid_validator.yaml | 2 + test/testdata/config/08_unknown_field.yaml | 5 + .../config/09_missing_rule_validator.yaml | 11 + test/testdata/config/10_mutate_test.yaml | 16 + .../testdata/config/11_mutate_test_error.yaml | 24 + .../config/12_mutate_test_cached.yaml | 7 + test/testdata/config/13_validation_modes.yaml | 36 + .../config/14_validation_metrics.yaml | 20 + test/testdata/cosign/01_cosign.yaml | 15 + test/testdata/cosign/02_rekor.yaml | 11 + test/testdata/cosign/03_unmarshal_err.yaml | 1 + test/testdata/cosign/04_rekor_err.yaml | 11 + test/testdata/cosign/05_no_trust_roots.yaml | 2 + test/testdata/cosign/06_auth.yaml | 11 + test/testdata/cosign/07_keychain.yaml | 11 + test/testdata/cosign/08_cert.yaml | 34 + test/testdata/cosign/09_full_config.yaml | 50 + test/testdata/cosign/10_invalid_cert.yaml | 35 + test/testdata/cosign/alice.key | 4 + test/testdata/cosign/bob.key | 4 + test/testdata/cosign/charlie.key | 4 + test/testdata/cosign/default.key | 4 + .../testdata/dockerfiles}/Dockerfile | 0 .../testdata/dockerfiles}/Dockerfile.unsigned | 0 .../dockerfiles}/double_sig/Dockerfile | 0 .../testdata/dockerfiles}/double_sig/main.c | 0 .../testdata/dockerfiles}/signed/Dockerfile | 0 .../testdata/dockerfiles}/signed/main.c | 0 .../dockerfiles}/special_sig/Dockerfile | 0 .../testdata/dockerfiles}/special_sig/main.c | 0 .../testdata/dockerfiles}/unsigned/Dockerfile | 0 .../testdata/dockerfiles}/unsigned/main.c | 0 .../dockerfiles}/wrong_signer/Dockerfile | 0 .../testdata/dockerfiles}/wrong_signer/main.c | 0 .../testdata/filesystem/a/b/c | 0 test/testdata/filesystem/a/x | 1 + .../testdata/filesystem/ax | 0 .../testdata/filesystem/d/e | 0 .../err1.yaml => test/testdata/filesystem/d/f | 0 .../testdata/filesystem/home/test | 0 test/testdata/notaryv1/01_notaryv1.yaml | 16 + test/testdata/notaryv1/02_no_trust_root.yaml | 2 + test/testdata/notaryv1/03_invalid_host.yaml | 7 + test/testdata/notaryv1/04_default_host.yaml | 15 + test/testdata/notaryv1/05_unmarshal_err.yaml | 1 + test/testdata/notaryv1/06_rsa_key.yaml | 14 + .../testdata/notaryv1/07_invalid_pub_key.yaml | 13 + .../notaryv1/08_never_expire_notary.yaml | 10 + test/testdata/notaryv1/09_extra_chars.yaml | 17 + test/testdata/notaryv1/10_unknown_keys.yaml | 54 + .../notaryv1/11_multiple_entries.yaml | 16 + test/testdata/notaryv1/12_auth.yaml | 12 + test/testdata/notaryv1/13_invalid_cert.yaml | 14 + .../notaryv1/14_auth_no_registry.yaml | 12 + .../notaryv1/trust_data/01_expired_root.json | 71 ++ .../02_invalid_signature_targets.json | 32 + .../trust_data/03_expired_snapshot.json | 30 + .../04_invalid_signature_delegation.json | 26 + .../trust_data/05_expired_delegation.json | 26 + .../trust_data/06_no_signature_root.json | 65 ++ .../07_root_multiple_signatures.json | 76 ++ .../trust_data/alice-image/root.json | 0 .../trust_data/alice-image/snapshot.json | 0 .../trust_data/alice-image/targets.json | 0 .../alice-image/targets/chamsen.json | 0 .../alice-image/targets/phbelitz.json | 0 .../alice-image/targets/releases.json | 0 .../trust_data/alice-image/timestamp.json | 0 .../trust_data/edge-case-err-image/root.json | 71 ++ .../edge-case-err-image/snapshot.json | 51 + .../edge-case-err-image/targets.json | 79 ++ .../edge-case-err-image/timestamp.json | 23 + .../trust_data/edge-case-image/root.json | 71 ++ .../trust_data/edge-case-image/snapshot.json | 30 + .../trust_data/edge-case-image/targets.json | 79 ++ .../trust_data/edge-case-image/timestamp.json | 23 + .../trust_data/err-image/invalid.json | 1 + .../notaryv1/trust_data/err-image/root.json | 51 + .../trust_data/err-image/snapshot.json | 6 +- .../trust_data/err-image/targets.json | 23 + .../trust_data/err-image/timestamp.json | 71 ++ .../notaryv1/trust_data/keys/del1.key | 5 + .../notaryv1/trust_data/keys/del1.pub | 4 + .../notaryv1/trust_data/keys/del1_id.txt | 1 + .../notaryv1/trust_data/keys/del2.key | 5 + .../notaryv1/trust_data/keys/del2.pub | 4 + .../notaryv1/trust_data/keys/del2_id.txt | 1 + .../notaryv1/trust_data/keys/root.crt | 12 + .../notaryv1/trust_data/keys/root.key | 5 + .../notaryv1/trust_data/keys/root.pub | 4 + .../notaryv1/trust_data/keys/root_id.txt | 1 + .../notaryv1/trust_data/keys/snapshot.key | 5 + .../notaryv1/trust_data/keys/snapshot.pub | 4 + .../notaryv1/trust_data/keys/snapshot_id.txt | 1 + .../notaryv1/trust_data/keys/targets.key | 5 + .../notaryv1/trust_data/keys/targets.pub | 4 + .../notaryv1/trust_data/keys/targets_id.txt | 1 + .../notaryv1/trust_data/keys/timestamp.key | 5 + .../notaryv1/trust_data/keys/timestamp.pub | 4 + .../notaryv1/trust_data/keys/timestamp_id.txt | 1 + .../trust_data/never-expire-image/root.json | 71 ++ .../never-expire-image/snapshot.json | 30 + .../never-expire-image/targets.json | 32 + .../never-expire-image/timestamp.json | 23 + .../root.json | 71 ++ .../snapshot.json | 51 + .../targets.json | 31 +- .../targets/del1.json | 26 + .../targets/del2.json | 26 + .../targets/releases.json | 26 + .../timestamp.json | 23 + .../root.json | 71 ++ .../snapshot.json | 51 + .../targets.json | 31 +- .../targets/del1.json | 26 + .../targets/del2.json | 26 + .../targets/releases.json | 26 + .../timestamp.json | 23 + .../trust_data/sample-image/root.json | 0 .../trust_data/sample-image/snapshot.json | 0 .../trust_data/sample-image/targets.json | 0 .../trust_data/sample-image/timestamp.json | 0 test/testdata/validators/01_static.yaml | 3 + test/testdata/validators/02_cosign.yaml | 9 + test/testdata/validators/03_no_name.yaml | 2 + test/testdata/validators/04_no_type.yaml | 2 + .../validators/05_unsupported_type.yaml | 3 + .../validators/06_unmarshal_error.yaml | 3 + .../testdata/validators/07_static_deny.yaml | 0 test/testdata/validators/08_nv1.yaml | 15 + test/testdata/validators/09_nv2.yaml | 25 + .../testdata/validators/10_err_unmarshal.yaml | 1 + test/testhelper/loader.go | 92 ++ test/testhelper/mock_cache.go | 91 ++ test/testhelper/mock_reader.go | 22 + test/testhelper/mock_server.go | 190 +++ test/testhelper/mock_validator.go | 62 + tests/__init__.py | 1 - tests/conftest.py | 424 ------- tests/data/alerting/alertconfig.json | 76 -- tests/data/alerting/alertconfig_schema.json | 89 -- .../alertconfig.json | 38 - .../alertconfig.json | 41 - .../alerting/invalid_config/alertconfig.json | 12 - .../misconfigured_config/alertconfig.json | 12 - tests/data/alerting/templates/custom.json | 14 - tests/data/alerting/templates/keybase.json | 3 - tests/data/alerting/templates/opsgenie.json | 16 - tests/data/alerting/templates/slack.json | 15 - tests/data/config/err2.yaml | 76 -- tests/data/config/err3.yaml | 76 -- tests/data/config/err4.yaml | 76 -- tests/data/config/err5.yaml | 77 -- tests/data/config/err6.yaml | 7 - tests/data/config/ext/auth.yaml | 2 - tests/data/config/sample_config.yaml | 109 -- tests/data/config/sample_secrets.yaml | 4 - tests/data/cosign/does_not_exist.txt | 2 - tests/data/cosign/no_data.txt | 3 - tests/data/cosign/notfound_in_tl.txt | 2 - tests/data/cosign/wrong_key.txt | 1 - tests/data/notary/hanswurstnotary.yaml | 32 - tests/data/notary/harbor.cert | 42 - tests/data/notary/notary1.yaml | 15 - tests/data/notary/notary2.yaml | 33 - tests/data/notary/unhealthy_notary.yaml | 9 - .../ad_request_allowlisted.json | 161 --- .../ad_request_ephemeral_container.json | 159 --- ...request_ephemeral_container_unchanged.json | 152 --- .../ad_request_err.json | 3 - .../ad_request_invalid.json | 5 - .../ad_request_replica_from_non_zero.json | 182 --- .../ad_request_replicaset_from_zero.json | 182 --- .../ad_request_replicaset_to_zero.json | 182 --- .../sample_kube_resources/deployments.json | 93 -- tests/data/sample_kube_resources/pods.json | 140 --- .../sample_kube_resources/replicasets.json | 80 -- .../sample_sentinel_err.json | 5 - .../sample_sentinel_fin.json | 5 - .../sample_sentinel_run.json | 5 - .../sample_kube_resources/sample_webhook.json | 51 - tests/data/trust_data/bob-image/root.json | 71 -- tests/data/trust_data/bob-image/snapshot.json | 30 - tests/data/trust_data/bob-image/targets.json | 26 - .../data/trust_data/bob-image/timestamp.json | 1 - tests/data/trust_data/charlie-image/root.json | 71 -- .../trust_data/charlie-image/snapshot.json | 44 - .../trust_data/charlie-image/targets.json | 55 - .../charlie-image/targets/del1.json | 26 - .../charlie-image/targets/releases.json | 26 - .../trust_data/charlie-image/timestamp.json | 23 - tests/data/trust_data/dave-image/root.json | 71 -- .../data/trust_data/dave-image/snapshot.json | 1 - .../trust_data/dave-image/targets/del1.json | 26 - .../trust_data/dave-image/targets/del2.json | 26 - .../dave-image/targets/releases.json | 26 - .../data/trust_data/dave-image/timestamp.json | 1 - tests/data/trust_data/eve-image/root.json | 71 -- tests/data/trust_data/eve-image/snapshot.json | 1 - .../data/trust_data/eve-image/timestamp.json | 1 - tests/data/trust_data/missing_keys_root.json | 41 - tests/data/trust_data/missing_path.json | 1 - tests/data/trust_data/redos_targets.json | 45 - tests/data/trust_data/sample2_root.json | 71 -- tests/data/trust_data/sample2_snapshot.json | 30 - tests/data/trust_data/sample2_targets.json | 26 - tests/data/trust_data/sample2_timestamp.json | 23 - tests/data/trust_data/sample3_targets.json | 38 - tests/data/trust_data/sample4_targets.json | 26 - tests/data/trust_data/sample5_root.json | 71 -- tests/data/trust_data/sample7_snapshot.json | 1 - tests/data/trust_data/sample7_targets.json | 1 - tests/data/trust_data/sample_releases.json | 37 - tests/data/trust_data/sample_root.json | 71 -- tests/data/trust_data/sample_snapshot.json | 51 - tests/data/trust_data/sample_timestamp.json | 23 - tests/data/trust_data/wrong_key_format.json | 66 -- tests/data/trust_data/wrong_signature.json | 71 -- .../data/trust_data/wrong_time_timestamp.json | 23 - tests/integration/Dockerfile.populate_notary | 16 - tests/integration/cause_load.sh | 8 - tests/integration/update-alerting.yaml | 33 - tests/integration/update.yaml | 142 --- tests/test_admission_request.py | 84 -- tests/test_alert.py | 319 ----- tests/test_config.py | 210 ---- tests/test_exceptions.py | 62 - tests/test_flask_application.py | 388 ------- tests/test_image.py | 190 --- tests/test_kube_api.py | 24 - tests/test_logging.py | 59 - tests/test_trust_root.py | 102 -- tests/test_util.py | 176 --- tests/test_workload_object.py | 238 ---- .../cosign/test_cosign_validator.py | 785 ------------- tests/validators/notaryv1/__init__.py | 0 tests/validators/notaryv1/test_keystore.py | 169 --- tests/validators/notaryv1/test_notary.py | 377 ------ .../notaryv1/test_notaryv1_validator.py | 469 -------- tests/validators/notaryv1/test_trust_data.py | 532 --------- tests/validators/notaryv1/test_tuf_role.py | 28 - tests/validators/notaryv2/__init__.py | 0 .../notaryv2/test_notaryv2_validator.py | 15 - tests/validators/static/__init__.py | 0 .../static/test_static_validator.py | 25 - tests/validators/test_interface.py | 22 - tests/validators/test_validators.py | 46 - tools/nv1_testdata_creator/main.go | 213 ++++ 589 files changed, 20450 insertions(+), 15057 deletions(-) rename {.kube-linter => .github/.kube-linter}/config.yaml (100%) delete mode 100644 .github/actions/safety/action.yaml create mode 100644 .github/workflows/.reusable-ci.yml delete mode 100644 .github/workflows/cicd.yaml create mode 100644 .github/workflows/pr.yml create mode 100644 .github/workflows/push.yml delete mode 100644 .pylintrc create mode 100644 build/Dockerfile rename {helm => charts/connaisseur}/.helmignore (97%) rename {helm => charts/connaisseur}/Chart.yaml (72%) rename {helm => charts/connaisseur}/alert_payload_templates/ecs-1-12-0.json (100%) rename {helm => charts/connaisseur}/alert_payload_templates/keybase.json (100%) rename {helm => charts/connaisseur}/alert_payload_templates/msteams.json (100%) rename {helm => charts/connaisseur}/alert_payload_templates/opsgenie.json (100%) rename {helm => charts/connaisseur}/alert_payload_templates/slack.json (100%) create mode 100644 charts/connaisseur/templates/_helpers.tpl create mode 100644 charts/connaisseur/templates/configmaps.yaml create mode 100644 charts/connaisseur/templates/deployment.yaml create mode 100644 charts/connaisseur/templates/env.yaml create mode 100644 charts/connaisseur/templates/redis.yaml create mode 100644 charts/connaisseur/templates/role.yaml create mode 100644 charts/connaisseur/templates/rolebinding.yaml create mode 100644 charts/connaisseur/templates/secrets.yaml rename {helm => charts/connaisseur}/templates/service.yaml (51%) create mode 100644 charts/connaisseur/templates/serviceaccount.yaml create mode 100644 charts/connaisseur/templates/webhook+certificate.yaml create mode 100644 charts/connaisseur/values.yaml create mode 100644 cmd/connaisseur/main.go delete mode 100644 connaisseur/__main__.py delete mode 100644 connaisseur/admission_request.py delete mode 100644 connaisseur/alert.py delete mode 100644 connaisseur/config.py delete mode 100644 connaisseur/constants.py delete mode 100644 connaisseur/exceptions.py delete mode 100644 connaisseur/flask_application.py delete mode 100644 connaisseur/image.py delete mode 100644 connaisseur/kube_api.py delete mode 100644 connaisseur/logging.py delete mode 100644 connaisseur/res/ad_request_schema.json delete mode 100644 connaisseur/res/alertconfig_schema.json delete mode 100644 connaisseur/res/config_schema.json delete mode 100644 connaisseur/res/root_schema.json delete mode 100644 connaisseur/res/snapshot_schema.json delete mode 100644 connaisseur/res/targets_schema.json delete mode 100644 connaisseur/res/timestamp_schema.json delete mode 100644 connaisseur/res/trust_schema.json delete mode 100644 connaisseur/trust_root.py delete mode 100644 connaisseur/util.py delete mode 100644 connaisseur/validators/cosign/cosign_validator.py delete mode 100644 connaisseur/validators/interface.py delete mode 100644 connaisseur/validators/notaryv1/key_store.py delete mode 100644 connaisseur/validators/notaryv1/notary.py delete mode 100644 connaisseur/validators/notaryv1/notaryv1_validator.py delete mode 100644 connaisseur/validators/notaryv1/trust_data.py delete mode 100644 connaisseur/validators/notaryv1/tuf_role.py delete mode 100644 connaisseur/validators/notaryv2/notaryv2_validator.py delete mode 100644 connaisseur/validators/static/static_validator.py delete mode 100644 connaisseur/validators/validator.py delete mode 100644 connaisseur/workload_object.py delete mode 100644 docker/Dockerfile delete mode 100644 docker/Dockerfile.getRoot delete mode 100644 docker/harden.sh delete mode 100644 docker/release-cosign.pub create mode 100644 docs/adr/ADR-8_go-transition.md create mode 100644 docs/adr/ADR-9_multi-pod.md create mode 100644 docs/assets/connaisseur_demo.mp4 create mode 100644 docs/features/caching.md create mode 100644 docs/features/resource_validation_mode.md create mode 100644 docs/validators/authentication.md create mode 100644 go.mod create mode 100644 go.sum create mode 120000 helm delete mode 100644 helm/README.md delete mode 100644 helm/templates/_helpers.tpl delete mode 100644 helm/templates/alertconfig.yaml delete mode 100644 helm/templates/certificate_webhook-conf.yaml delete mode 100644 helm/templates/config-secrets.yaml delete mode 100644 helm/templates/config.yaml delete mode 100644 helm/templates/deployment.yaml delete mode 100644 helm/templates/env.yaml delete mode 100644 helm/templates/podsecuritypolicy.yaml delete mode 100644 helm/templates/role.yaml delete mode 100644 helm/templates/rolebinding.yaml delete mode 100644 helm/templates/serviceaccount.yaml delete mode 100644 helm/values.yaml create mode 100644 internal/alert/alerting.go create mode 100644 internal/alert/alerting_test.go create mode 100644 internal/alert/channel.go create mode 100644 internal/alert/channel_test.go create mode 100644 internal/alert/config.go create mode 100644 internal/alert/config_test.go create mode 100644 internal/alert/template.go create mode 100644 internal/alert/template_test.go create mode 100644 internal/caching/caching.go create mode 100644 internal/caching/caching_test.go create mode 100644 internal/config/config.go create mode 100644 internal/config/config_test.go create mode 100644 internal/constants/constants.go create mode 100644 internal/handler/admission_metrics.go create mode 100644 internal/handler/admission_metrics_test.go create mode 100644 internal/handler/health.go create mode 100644 internal/handler/health_test.go create mode 100644 internal/handler/mutate.go create mode 100644 internal/handler/mutate_test.go create mode 100644 internal/handler/start.go create mode 100644 internal/handler/start_test.go create mode 100644 internal/handler/validation/skip.go create mode 100644 internal/handler/validation/skip_test.go create mode 100644 internal/handler/validation/validation.go create mode 100644 internal/handler/validation/validation_metrics.go create mode 100644 internal/handler/validation/validation_metrics_test.go create mode 100644 internal/handler/validation/validation_test.go create mode 100644 internal/image/image.go create mode 100644 internal/image/image_test.go create mode 100644 internal/image/registry.go create mode 100644 internal/image/registry_test.go create mode 100644 internal/kubernetes/admission_objects.go create mode 100644 internal/kubernetes/admission_objects_test.go create mode 100644 internal/kubernetes/deserializer.go create mode 100644 internal/kubernetes/kube_api.go create mode 100644 internal/kubernetes/kube_api_test.go create mode 100644 internal/kubernetes/workload_object.go create mode 100644 internal/kubernetes/workload_object_test.go create mode 100644 internal/policy/match.go create mode 100644 internal/policy/match_test.go create mode 100644 internal/policy/rule.go create mode 100644 internal/utils/feature.go create mode 100644 internal/utils/feature_test.go create mode 100644 internal/utils/files.go create mode 100644 internal/utils/files_test.go create mode 100644 internal/utils/int.go create mode 100644 internal/utils/int_test.go create mode 100644 internal/utils/list.go create mode 100644 internal/utils/list_test.go create mode 100644 internal/utils/logging.go create mode 100644 internal/utils/logging_test.go create mode 100644 internal/utils/strings.go create mode 100644 internal/utils/strings_test.go create mode 100644 internal/utils/validation.go create mode 100644 internal/utils/validation_test.go create mode 100644 internal/validator/auth/auth.go create mode 100644 internal/validator/auth/auth_test.go create mode 100644 internal/validator/auth/trust_root.go create mode 100644 internal/validator/auth/trust_root_test.go create mode 100644 internal/validator/cosignvalidator/cosign_validator.go create mode 100644 internal/validator/cosignvalidator/cosign_validator_test.go create mode 100644 internal/validator/notaryv1/notaryserver/client.go create mode 100644 internal/validator/notaryv1/notaryserver/client_test.go create mode 100644 internal/validator/notaryv1/notaryserver/repo.go create mode 100644 internal/validator/notaryv1/notaryserver/repo_test.go create mode 100644 internal/validator/notaryv1/notaryserver/simple_cred_store.go create mode 100644 internal/validator/notaryv1/notaryserver/simple_cred_store_test.go create mode 100644 internal/validator/notaryv1/notaryv1_validator.go create mode 100644 internal/validator/notaryv1/notaryv1_validator_test.go create mode 100644 internal/validator/notaryv2/notaryv2_validator.go create mode 100644 internal/validator/notaryv2/trust_store.go create mode 100644 internal/validator/staticvalidator/static_validator.go create mode 100644 internal/validator/staticvalidator/static_validator_test.go create mode 100644 internal/validator/validator.go create mode 100644 internal/validator/validator_test.go delete mode 100644 requirements.txt delete mode 100644 requirements_dev.txt delete mode 100644 scripts/get_root_key.py delete mode 100644 setup.py create mode 100644 test/integration/Dockerfile.populate_notary rename {tests => test}/integration/README.md (77%) rename {tests => test}/integration/alerting/Dockerfile (100%) rename {tests => test}/integration/alerting/app/alert_checker.py (100%) rename {tests => test}/integration/alerting/app/keybase_payload.json (100%) rename {tests => test}/integration/alerting/app/opsgenie_payload.json (100%) rename {tests => test}/integration/alerting/app/slack_payload.json (100%) rename {tests => test}/integration/cases.yaml (73%) create mode 100755 test/integration/cause_load.sh rename {tests => test}/integration/cleanup.sh (87%) create mode 100644 test/integration/cosign.pub rename {tests => test}/integration/deployments/complexity.yaml (100%) rename {tests => test}/integration/deployments/deployment_i1.yaml (100%) rename {tests => test}/integration/deployments/deployment_i1n1.yaml (100%) rename {tests => test}/integration/deployments/deployment_i1u1.yaml (100%) rename {tests => test}/integration/deployments/deployment_i2.yaml (100%) rename {tests => test}/integration/deployments/deployment_i2i.yaml (100%) rename {tests => test}/integration/deployments/deployment_i2u1-1.yaml (100%) rename {tests => test}/integration/deployments/deployment_i2u1-2.yaml (100%) rename {tests => test}/integration/deployments/deployment_i2u2.yaml (100%) rename {tests => test}/integration/deployments/deployment_i2ui.yaml (100%) rename {tests => test}/integration/deployments/valid_container_with_unsigned_init_container_image.yml (78%) rename {tests => test}/integration/deployments/valid_init_container.yaml (78%) rename {tests => test}/integration/ghcr-values.yaml (100%) rename {tests => test}/integration/integration-test.sh (83%) rename {tests => test}/integration/loadtest.yaml.template (100%) rename {tests => test}/integration/notary_addhash.sh (73%) rename {tests => test}/integration/notary_init.sh (83%) rename {tests/data => test/integration}/notary_service_container/config/client_config.json (100%) rename {tests/data => test/integration}/notary_service_container/server/ca.crt (100%) rename {tests/data => test/integration}/notary_service_container/server/config.json (100%) rename {tests/data => test/integration}/notary_service_container/server/notary-server.crt (100%) rename {tests/data => test/integration}/notary_service_container/server/notary-server.key (100%) rename {tests/data => test/integration}/notary_service_container/signer/ca.crt (100%) rename {tests/data => test/integration}/notary_service_container/signer/config.json (100%) rename {tests/data => test/integration}/notary_service_container/signer/notary-signer.crt (100%) rename {tests/data => test/integration}/notary_service_container/signer/notary-signer.key (100%) create mode 100644 test/integration/redis-cert/install.yaml create mode 100755 test/integration/redis-cert/redis_cert.sh create mode 100644 test/integration/redis-cert/update.yaml rename {tests => test}/integration/run_integration_tests.sh (89%) rename {tests => test}/integration/tls.cert (100%) rename {tests => test}/integration/tls.key (100%) create mode 100644 test/integration/update-alerting.yaml rename {tests => test}/integration/update-cert.yaml (100%) rename {tests => test}/integration/update-for-workloads.yaml (100%) rename {tests => test}/integration/update-self-hosted-notary.yaml (91%) create mode 100644 test/integration/update.yaml rename {tests => test}/integration/update_deployment_notary_test.yaml (100%) rename {tests => test}/integration/var-img.yaml (100%) rename {tests => test}/integration/workload-objects/CronJob.yaml (100%) rename {tests => test}/integration/workload-objects/DaemonSet.yaml (100%) rename {tests => test}/integration/workload-objects/Deployment.yaml (100%) rename {tests => test}/integration/workload-objects/Job.yaml (100%) rename {tests => test}/integration/workload-objects/Pod.yaml (100%) rename {tests => test}/integration/workload-objects/ReplicaSet.yaml (100%) rename {tests => test}/integration/workload-objects/ReplicationController.yaml (100%) rename {tests => test}/integration/workload-objects/StatefulSet.yaml (100%) rename tests/data/sample_admission_requests/ad_request_deployments.json => test/testdata/admission_requests/01_deployment.json (97%) rename tests/data/sample_admission_requests/ad_request_pods.json => test/testdata/admission_requests/02_pod.json (97%) rename tests/data/sample_admission_requests/ad_request_replicasets.json => test/testdata/admission_requests/03_replicaset.json (97%) rename tests/data/sample_admission_requests/ad_request_cronjob.json => test/testdata/admission_requests/04_cronjob.json (90%) create mode 100644 test/testdata/admission_requests/05_replication_controller.json create mode 100644 test/testdata/admission_requests/06_daemonset.json create mode 100644 test/testdata/admission_requests/07_statefulset.json create mode 100644 test/testdata/admission_requests/08_job.json rename tests/data/sample_admission_requests/ad_request_deployments_multi_image.json => test/testdata/admission_requests/09_dply_multi_image.json (97%) rename tests/data/sample_admission_requests/ad_request_auto_update_approval.json => test/testdata/admission_requests/10_update.json (99%) create mode 100644 test/testdata/admission_requests/11_role.json create mode 100644 test/testdata/admission_requests/12_err_pod.json rename tests/data/sample_admission_requests/ad_request_pods_unknownparent.json => test/testdata/admission_requests/13_unknown_parent.json (97%) create mode 100644 test/testdata/admission_requests/14_deny_me_job.json create mode 100644 test/testdata/admission_requests/15_allow_me_job.json rename tests/data/sample_admission_requests/ad_request_wrong_version.json => test/testdata/admission_requests/16_mismatch_uid.json (95%) create mode 100644 test/testdata/admission_requests/17_deny_me_pod.json create mode 100644 test/testdata/admission_requests/18_allow_me_pod.json create mode 100644 test/testdata/admission_requests/19_pod_initcontainer.json create mode 100644 test/testdata/admission_requests/20_pod_ephemeralcontainer.json create mode 100644 test/testdata/admission_requests/21_broken_admission_request.json rename tests/data/sample_admission_requests/ad_request_invalid_image.json => test/testdata/admission_requests/22_allow_me_with_index_docker_io.json (96%) rename tests/data/sample_admission_requests/ad_request_auto_approval.json => test/testdata/admission_requests/23_alice_digest_dpl.json (94%) create mode 100644 test/testdata/admission_requests/24_alice_tag_digest_dpl.json create mode 100644 test/testdata/admission_requests/25_null_request.json create mode 100644 test/testdata/alerts/00_template.tpl rename connaisseur/__init__.py => test/testdata/alerts/01_empty.tpl (100%) create mode 100644 test/testdata/alerts/02_faulty_template.tpl create mode 100644 test/testdata/alerts/03_fail_template.tpl create mode 100644 test/testdata/alerts/04_allow_no_header.tpl create mode 100644 test/testdata/alerts/05_render_err.tpl create mode 100644 test/testdata/alerts/06_template.json create mode 100644 test/testdata/alerts/07_opsgenie.json create mode 100644 test/testdata/alerts/08_render_err.json rename connaisseur/validators/__init__.py => test/testdata/alerts/09_empty.json (100%) create mode 100644 test/testdata/alerts/10_allow_no_header.json create mode 100644 test/testdata/alerts/11_fail_template.json create mode 100644 test/testdata/alerts/12_send_notif_test.json create mode 100644 test/testdata/alerts/13_priority.json create mode 100644 test/testdata/alerts/14_triple_bracket.json create mode 100644 test/testdata/alerts/15_mismatch_bracket.json create mode 100644 test/testdata/alerts/16_single_bracket.json create mode 100644 test/testdata/alerts/alerting/00_alerting.yaml create mode 100644 test/testdata/alerts/alerting/01_unmarshal_err.yaml create mode 100644 test/testdata/alerts/alerting/02_requestsender_unmarshal_err.yaml create mode 100644 test/testdata/alerts/alerting/03_send_notif_test.yaml create mode 100644 test/testdata/alerts/alerting/04_send_notif_no_cluster.yaml create mode 100644 test/testdata/alerts/alerting/05_send_notif_tmpl_not_found.yaml create mode 100644 test/testdata/alerts/alerting/06_empty.yaml create mode 100644 test/testdata/alerts/alerting/07_send_notif_success_and_fail.yaml create mode 100644 test/testdata/alerts/alerting/08_send_notif_multiple_ch.yaml create mode 100644 test/testdata/alerts/channel/00_channel.yaml create mode 100644 test/testdata/auth/01_auth.yaml create mode 100644 test/testdata/auth/01_auth/secret.yaml create mode 100644 test/testdata/auth/02_keychain.yaml rename connaisseur/validators/cosign/__init__.py => test/testdata/auth/03_empty.yaml (100%) create mode 100644 test/testdata/auth/04_unable_to_find.yaml create mode 100644 test/testdata/auth/05_err_reading_secret.yaml create mode 100644 test/testdata/auth/05_err_reading_secret/secret.yaml create mode 100644 test/testdata/auth/06_unmarshal_err.yaml create mode 100644 test/testdata/auth/07_no_secret.yaml create mode 100644 test/testdata/auth/08_docker_auth.yaml create mode 100644 test/testdata/auth/08_docker_auth/.dockerconfigjson create mode 100644 test/testdata/auth/09_auth_and_keychain.yaml create mode 100644 test/testdata/auth/10_multi_auth_files.yaml create mode 100644 test/testdata/auth/10_multi_auth_files/.dockerconfigjson create mode 100644 test/testdata/auth/10_multi_auth_files/secret.yaml create mode 100644 test/testdata/auth/11_no_auth_files.yaml create mode 100644 test/testdata/auth/12_invalid_secret_file.yaml create mode 100644 test/testdata/auth/12_invalid_secret_file/terces.yaml create mode 100644 test/testdata/auth/13_no_registry_auth.yaml create mode 100644 test/testdata/auth/13_no_registry_auth/secret.yaml create mode 100644 test/testdata/auth/14_invalid_registry_docker_auth.yaml create mode 100644 test/testdata/auth/14_invalid_registry_docker_auth/.dockerconfigjson create mode 100644 test/testdata/auth/15_default_auth/secret.yaml create mode 100644 test/testdata/caching/00_cert/tls.crt create mode 100644 test/testdata/caching/01_err/tls.crt create mode 100644 test/testdata/config/00_sample.yaml create mode 100644 test/testdata/config/01_val_no_name.yaml create mode 100644 test/testdata/config/02_val_no_type.yaml create mode 100644 test/testdata/config/03_unsupported_type.yaml create mode 100644 test/testdata/config/04_unmarshal_cosign_error.yaml create mode 100644 test/testdata/config/05_invalid_cosign.yaml create mode 100644 test/testdata/config/06_unmarshal_static_error.yaml create mode 100644 test/testdata/config/07_invalid_validator.yaml create mode 100644 test/testdata/config/08_unknown_field.yaml create mode 100644 test/testdata/config/09_missing_rule_validator.yaml create mode 100644 test/testdata/config/10_mutate_test.yaml create mode 100644 test/testdata/config/11_mutate_test_error.yaml create mode 100644 test/testdata/config/12_mutate_test_cached.yaml create mode 100644 test/testdata/config/13_validation_modes.yaml create mode 100644 test/testdata/config/14_validation_metrics.yaml create mode 100644 test/testdata/cosign/01_cosign.yaml create mode 100644 test/testdata/cosign/02_rekor.yaml create mode 100644 test/testdata/cosign/03_unmarshal_err.yaml create mode 100644 test/testdata/cosign/04_rekor_err.yaml create mode 100644 test/testdata/cosign/05_no_trust_roots.yaml create mode 100644 test/testdata/cosign/06_auth.yaml create mode 100644 test/testdata/cosign/07_keychain.yaml create mode 100644 test/testdata/cosign/08_cert.yaml create mode 100644 test/testdata/cosign/09_full_config.yaml create mode 100644 test/testdata/cosign/10_invalid_cert.yaml create mode 100644 test/testdata/cosign/alice.key create mode 100644 test/testdata/cosign/bob.key create mode 100644 test/testdata/cosign/charlie.key create mode 100644 test/testdata/cosign/default.key rename {tests => test/testdata/dockerfiles}/Dockerfile (100%) rename {tests => test/testdata/dockerfiles}/Dockerfile.unsigned (100%) rename {tests/testimages => test/testdata/dockerfiles}/double_sig/Dockerfile (100%) rename {tests/testimages => test/testdata/dockerfiles}/double_sig/main.c (100%) rename {tests/testimages => test/testdata/dockerfiles}/signed/Dockerfile (100%) rename {tests/testimages => test/testdata/dockerfiles}/signed/main.c (100%) rename {tests/testimages => test/testdata/dockerfiles}/special_sig/Dockerfile (100%) rename {tests/testimages => test/testdata/dockerfiles}/special_sig/main.c (100%) rename {tests/testimages => test/testdata/dockerfiles}/unsigned/Dockerfile (100%) rename {tests/testimages => test/testdata/dockerfiles}/unsigned/main.c (100%) rename {tests/testimages => test/testdata/dockerfiles}/wrong_signer/Dockerfile (100%) rename {tests/testimages => test/testdata/dockerfiles}/wrong_signer/main.c (100%) rename connaisseur/validators/notaryv1/__init__.py => test/testdata/filesystem/a/b/c (100%) create mode 120000 test/testdata/filesystem/a/x rename connaisseur/validators/notaryv2/__init__.py => test/testdata/filesystem/ax (100%) rename connaisseur/validators/static/__init__.py => test/testdata/filesystem/d/e (100%) rename tests/data/config/err1.yaml => test/testdata/filesystem/d/f (100%) rename tests/validators/__init__.py => test/testdata/filesystem/home/test (100%) create mode 100644 test/testdata/notaryv1/01_notaryv1.yaml create mode 100644 test/testdata/notaryv1/02_no_trust_root.yaml create mode 100644 test/testdata/notaryv1/03_invalid_host.yaml create mode 100644 test/testdata/notaryv1/04_default_host.yaml create mode 100644 test/testdata/notaryv1/05_unmarshal_err.yaml create mode 100644 test/testdata/notaryv1/06_rsa_key.yaml create mode 100644 test/testdata/notaryv1/07_invalid_pub_key.yaml create mode 100644 test/testdata/notaryv1/08_never_expire_notary.yaml create mode 100644 test/testdata/notaryv1/09_extra_chars.yaml create mode 100644 test/testdata/notaryv1/10_unknown_keys.yaml create mode 100644 test/testdata/notaryv1/11_multiple_entries.yaml create mode 100644 test/testdata/notaryv1/12_auth.yaml create mode 100644 test/testdata/notaryv1/13_invalid_cert.yaml create mode 100644 test/testdata/notaryv1/14_auth_no_registry.yaml create mode 100644 test/testdata/notaryv1/trust_data/01_expired_root.json create mode 100644 test/testdata/notaryv1/trust_data/02_invalid_signature_targets.json create mode 100644 test/testdata/notaryv1/trust_data/03_expired_snapshot.json create mode 100644 test/testdata/notaryv1/trust_data/04_invalid_signature_delegation.json create mode 100644 test/testdata/notaryv1/trust_data/05_expired_delegation.json create mode 100644 test/testdata/notaryv1/trust_data/06_no_signature_root.json create mode 100644 test/testdata/notaryv1/trust_data/07_root_multiple_signatures.json rename {tests/data => test/testdata/notaryv1}/trust_data/alice-image/root.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/alice-image/snapshot.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/alice-image/targets.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/alice-image/targets/chamsen.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/alice-image/targets/phbelitz.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/alice-image/targets/releases.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/alice-image/timestamp.json (100%) create mode 100644 test/testdata/notaryv1/trust_data/edge-case-err-image/root.json create mode 100644 test/testdata/notaryv1/trust_data/edge-case-err-image/snapshot.json create mode 100644 test/testdata/notaryv1/trust_data/edge-case-err-image/targets.json create mode 100644 test/testdata/notaryv1/trust_data/edge-case-err-image/timestamp.json create mode 100644 test/testdata/notaryv1/trust_data/edge-case-image/root.json create mode 100644 test/testdata/notaryv1/trust_data/edge-case-image/snapshot.json create mode 100644 test/testdata/notaryv1/trust_data/edge-case-image/targets.json create mode 100644 test/testdata/notaryv1/trust_data/edge-case-image/timestamp.json create mode 100644 test/testdata/notaryv1/trust_data/err-image/invalid.json create mode 100644 test/testdata/notaryv1/trust_data/err-image/root.json rename tests/data/trust_data/sample_targets.json => test/testdata/notaryv1/trust_data/err-image/snapshot.json (89%) create mode 100644 test/testdata/notaryv1/trust_data/err-image/targets.json create mode 100644 test/testdata/notaryv1/trust_data/err-image/timestamp.json create mode 100644 test/testdata/notaryv1/trust_data/keys/del1.key create mode 100644 test/testdata/notaryv1/trust_data/keys/del1.pub create mode 100644 test/testdata/notaryv1/trust_data/keys/del1_id.txt create mode 100644 test/testdata/notaryv1/trust_data/keys/del2.key create mode 100644 test/testdata/notaryv1/trust_data/keys/del2.pub create mode 100644 test/testdata/notaryv1/trust_data/keys/del2_id.txt create mode 100644 test/testdata/notaryv1/trust_data/keys/root.crt create mode 100644 test/testdata/notaryv1/trust_data/keys/root.key create mode 100644 test/testdata/notaryv1/trust_data/keys/root.pub create mode 100644 test/testdata/notaryv1/trust_data/keys/root_id.txt create mode 100644 test/testdata/notaryv1/trust_data/keys/snapshot.key create mode 100644 test/testdata/notaryv1/trust_data/keys/snapshot.pub create mode 100644 test/testdata/notaryv1/trust_data/keys/snapshot_id.txt create mode 100644 test/testdata/notaryv1/trust_data/keys/targets.key create mode 100644 test/testdata/notaryv1/trust_data/keys/targets.pub create mode 100644 test/testdata/notaryv1/trust_data/keys/targets_id.txt create mode 100644 test/testdata/notaryv1/trust_data/keys/timestamp.key create mode 100644 test/testdata/notaryv1/trust_data/keys/timestamp.pub create mode 100644 test/testdata/notaryv1/trust_data/keys/timestamp_id.txt create mode 100644 test/testdata/notaryv1/trust_data/never-expire-image/root.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-image/snapshot.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-image/targets.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-image/timestamp.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-conflicting-delegations-image/root.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-conflicting-delegations-image/snapshot.json rename {tests/data/trust_data/eve-image => test/testdata/notaryv1/trust_data/never-expire-with-conflicting-delegations-image}/targets.json (54%) create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-conflicting-delegations-image/targets/del1.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-conflicting-delegations-image/targets/del2.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-conflicting-delegations-image/targets/releases.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-conflicting-delegations-image/timestamp.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-delegations-image/root.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-delegations-image/snapshot.json rename {tests/data/trust_data/dave-image => test/testdata/notaryv1/trust_data/never-expire-with-delegations-image}/targets.json (54%) create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-delegations-image/targets/del1.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-delegations-image/targets/del2.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-delegations-image/targets/releases.json create mode 100644 test/testdata/notaryv1/trust_data/never-expire-with-delegations-image/timestamp.json rename {tests/data => test/testdata/notaryv1}/trust_data/sample-image/root.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/sample-image/snapshot.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/sample-image/targets.json (100%) rename {tests/data => test/testdata/notaryv1}/trust_data/sample-image/timestamp.json (100%) create mode 100644 test/testdata/validators/01_static.yaml create mode 100644 test/testdata/validators/02_cosign.yaml create mode 100644 test/testdata/validators/03_no_name.yaml create mode 100644 test/testdata/validators/04_no_type.yaml create mode 100644 test/testdata/validators/05_unsupported_type.yaml create mode 100644 test/testdata/validators/06_unmarshal_error.yaml rename tests/validators/cosign/__init__.py => test/testdata/validators/07_static_deny.yaml (100%) create mode 100644 test/testdata/validators/08_nv1.yaml create mode 100644 test/testdata/validators/09_nv2.yaml create mode 100644 test/testdata/validators/10_err_unmarshal.yaml create mode 100644 test/testhelper/loader.go create mode 100644 test/testhelper/mock_cache.go create mode 100644 test/testhelper/mock_reader.go create mode 100644 test/testhelper/mock_server.go create mode 100644 test/testhelper/mock_validator.go delete mode 100644 tests/__init__.py delete mode 100644 tests/conftest.py delete mode 100644 tests/data/alerting/alertconfig.json delete mode 100644 tests/data/alerting/alertconfig_schema.json delete mode 100644 tests/data/alerting/config_only_send_on_admit/alertconfig.json delete mode 100644 tests/data/alerting/config_only_send_on_reject/alertconfig.json delete mode 100644 tests/data/alerting/invalid_config/alertconfig.json delete mode 100644 tests/data/alerting/misconfigured_config/alertconfig.json delete mode 100644 tests/data/alerting/templates/custom.json delete mode 100644 tests/data/alerting/templates/keybase.json delete mode 100644 tests/data/alerting/templates/opsgenie.json delete mode 100644 tests/data/alerting/templates/slack.json delete mode 100644 tests/data/config/err2.yaml delete mode 100644 tests/data/config/err3.yaml delete mode 100644 tests/data/config/err4.yaml delete mode 100644 tests/data/config/err5.yaml delete mode 100644 tests/data/config/err6.yaml delete mode 100644 tests/data/config/ext/auth.yaml delete mode 100644 tests/data/config/sample_config.yaml delete mode 100644 tests/data/config/sample_secrets.yaml delete mode 100644 tests/data/cosign/does_not_exist.txt delete mode 100644 tests/data/cosign/no_data.txt delete mode 100644 tests/data/cosign/notfound_in_tl.txt delete mode 100644 tests/data/cosign/wrong_key.txt delete mode 100644 tests/data/notary/hanswurstnotary.yaml delete mode 100644 tests/data/notary/harbor.cert delete mode 100644 tests/data/notary/notary1.yaml delete mode 100644 tests/data/notary/notary2.yaml delete mode 100644 tests/data/notary/unhealthy_notary.yaml delete mode 100644 tests/data/sample_admission_requests/ad_request_allowlisted.json delete mode 100644 tests/data/sample_admission_requests/ad_request_ephemeral_container.json delete mode 100644 tests/data/sample_admission_requests/ad_request_ephemeral_container_unchanged.json delete mode 100644 tests/data/sample_admission_requests/ad_request_err.json delete mode 100644 tests/data/sample_admission_requests/ad_request_invalid.json delete mode 100644 tests/data/sample_admission_requests/ad_request_replica_from_non_zero.json delete mode 100644 tests/data/sample_admission_requests/ad_request_replicaset_from_zero.json delete mode 100644 tests/data/sample_admission_requests/ad_request_replicaset_to_zero.json delete mode 100644 tests/data/sample_kube_resources/deployments.json delete mode 100644 tests/data/sample_kube_resources/pods.json delete mode 100644 tests/data/sample_kube_resources/replicasets.json delete mode 100644 tests/data/sample_kube_resources/sample_sentinel_err.json delete mode 100644 tests/data/sample_kube_resources/sample_sentinel_fin.json delete mode 100644 tests/data/sample_kube_resources/sample_sentinel_run.json delete mode 100644 tests/data/sample_kube_resources/sample_webhook.json delete mode 100644 tests/data/trust_data/bob-image/root.json delete mode 100644 tests/data/trust_data/bob-image/snapshot.json delete mode 100644 tests/data/trust_data/bob-image/targets.json delete mode 100644 tests/data/trust_data/bob-image/timestamp.json delete mode 100644 tests/data/trust_data/charlie-image/root.json delete mode 100644 tests/data/trust_data/charlie-image/snapshot.json delete mode 100644 tests/data/trust_data/charlie-image/targets.json delete mode 100644 tests/data/trust_data/charlie-image/targets/del1.json delete mode 100644 tests/data/trust_data/charlie-image/targets/releases.json delete mode 100644 tests/data/trust_data/charlie-image/timestamp.json delete mode 100644 tests/data/trust_data/dave-image/root.json delete mode 100644 tests/data/trust_data/dave-image/snapshot.json delete mode 100644 tests/data/trust_data/dave-image/targets/del1.json delete mode 100644 tests/data/trust_data/dave-image/targets/del2.json delete mode 100644 tests/data/trust_data/dave-image/targets/releases.json delete mode 100644 tests/data/trust_data/dave-image/timestamp.json delete mode 100644 tests/data/trust_data/eve-image/root.json delete mode 100644 tests/data/trust_data/eve-image/snapshot.json delete mode 100644 tests/data/trust_data/eve-image/timestamp.json delete mode 100644 tests/data/trust_data/missing_keys_root.json delete mode 100644 tests/data/trust_data/missing_path.json delete mode 100644 tests/data/trust_data/redos_targets.json delete mode 100644 tests/data/trust_data/sample2_root.json delete mode 100644 tests/data/trust_data/sample2_snapshot.json delete mode 100644 tests/data/trust_data/sample2_targets.json delete mode 100644 tests/data/trust_data/sample2_timestamp.json delete mode 100644 tests/data/trust_data/sample3_targets.json delete mode 100644 tests/data/trust_data/sample4_targets.json delete mode 100644 tests/data/trust_data/sample5_root.json delete mode 100644 tests/data/trust_data/sample7_snapshot.json delete mode 100644 tests/data/trust_data/sample7_targets.json delete mode 100644 tests/data/trust_data/sample_releases.json delete mode 100644 tests/data/trust_data/sample_root.json delete mode 100644 tests/data/trust_data/sample_snapshot.json delete mode 100644 tests/data/trust_data/sample_timestamp.json delete mode 100644 tests/data/trust_data/wrong_key_format.json delete mode 100644 tests/data/trust_data/wrong_signature.json delete mode 100644 tests/data/trust_data/wrong_time_timestamp.json delete mode 100644 tests/integration/Dockerfile.populate_notary delete mode 100755 tests/integration/cause_load.sh delete mode 100644 tests/integration/update-alerting.yaml delete mode 100644 tests/integration/update.yaml delete mode 100644 tests/test_admission_request.py delete mode 100644 tests/test_alert.py delete mode 100644 tests/test_config.py delete mode 100644 tests/test_exceptions.py delete mode 100644 tests/test_flask_application.py delete mode 100644 tests/test_image.py delete mode 100644 tests/test_kube_api.py delete mode 100644 tests/test_logging.py delete mode 100644 tests/test_trust_root.py delete mode 100644 tests/test_util.py delete mode 100644 tests/test_workload_object.py delete mode 100644 tests/validators/cosign/test_cosign_validator.py delete mode 100644 tests/validators/notaryv1/__init__.py delete mode 100644 tests/validators/notaryv1/test_keystore.py delete mode 100644 tests/validators/notaryv1/test_notary.py delete mode 100644 tests/validators/notaryv1/test_notaryv1_validator.py delete mode 100644 tests/validators/notaryv1/test_trust_data.py delete mode 100644 tests/validators/notaryv1/test_tuf_role.py delete mode 100644 tests/validators/notaryv2/__init__.py delete mode 100644 tests/validators/notaryv2/test_notaryv2_validator.py delete mode 100644 tests/validators/static/__init__.py delete mode 100644 tests/validators/static/test_static_validator.py delete mode 100644 tests/validators/test_interface.py delete mode 100644 tests/validators/test_validators.py create mode 100644 tools/nv1_testdata_creator/main.go diff --git a/.dockerignore b/.dockerignore index 379416fb9..a0ea21853 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,13 +1,15 @@ -# ignore Markdown files +# ignore docs *.md +docs/ # ignore tests -connaisseur/tests/ -# ignore img -img/ -# ignore coverage files -connaisseur/coverage.xml -connaisseur/coverage.txt -# ignore pycache -connaisseur/__pycache__/ -# ignore venv folder -venv/ +tests/ +test/ +# ignore tools +tools/ +# ignore charts +charts/ +helm/ +# ignore misc +LICENSE +Makefile +mkdocs.yml diff --git a/.kube-linter/config.yaml b/.github/.kube-linter/config.yaml similarity index 100% rename from .kube-linter/config.yaml rename to .github/.kube-linter/config.yaml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 31f881541..7eaf2b666 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,4 +15,3 @@ Fixes # - [ ] Added tests (if necessary) - [ ] Extended README/Documentation (if necessary) - [ ] Adjusted versions of image and Helm chart in `Chart.yaml` (if necessary) - diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 7e949d3f4..e79c7fd39 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -61,10 +61,11 @@ runs: uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4.0.0 with: push: true - tags: ${{ steps.tags.outputs.tags }} + cache-from: type=gha + cache-to: type=gha,mode=max + file: build/Dockerfile labels: ${{ inputs.image_labels }} - file: docker/Dockerfile - build-args: COSIGN_VERSION=${{ inputs.cosign_version }} + tags: ${{ steps.tags.outputs.tags }} sbom: false # Duplicates SBOMs manually created below provenance: false #TODO: Set to false, as resulting format is not OCI (GHCR) compliant (https://github.com/docker/build-push-action/issues/820) and causes problems with GHCR and e.g. image deletion (https://github.com/snok/container-retention-policy/issues/63) - name: Create SBOM @@ -88,9 +89,9 @@ runs: - name: Verify build data id: verify run: | - mkdir build - cosign public-key --key env://COSIGN_PRIVATE_KEY > build/cosign.pub - PUBLIC_KEY="$(cat build/cosign.pub)" + mkdir ci + cosign public-key --key env://COSIGN_PRIVATE_KEY > ci/cosign.pub + PUBLIC_KEY="$(cat ci/cosign.pub)" cosign tree ${TAGS} PUBLIC_KEY=${PUBLIC_KEY} cosign verify --key env://PUBLIC_KEY ${TAGS} PUBLIC_KEY=${PUBLIC_KEY} cosign verify --key env://PUBLIC_KEY --attachment sbom ${TAGS} @@ -109,14 +110,14 @@ runs: uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cosign.pub - path: build/cosign.pub + path: ci/cosign.pub - name: Show build and signature information run: | - CONFIGURE="yq '. *+ load(\"tests/integration/var-img.yaml\")' tests/integration/ghcr-values.yaml > ghcr.yaml &&\n\t IMAGE=\"${{ inputs.image_registry }}/${{ inputs.image_repo }}\" TAG=\"${{ inputs.image_tag }}\" IMAGEPULLSECRET=\"\" envsubst < ghcr.yaml > update &&\n\t yq '. *+ load(\"update\")' -i helm/values.yaml &&\n\t rm ghcr.yaml update" + CONFIGURE="yq '. *+ load(\"test/integration/var-img.yaml\")' test/integration/ghcr-values.yaml > ghcr.yaml &&\n\t IMAGE=\"${{ inputs.image_registry }}/${{ inputs.image_repo }}\" TAG=\"${{ inputs.image_tag }}\" IMAGEPULLSECRET=\"\" envsubst < ghcr.yaml > update &&\n\t yq '. *+ load(\"update\")' -i charts/connaisseur/values.yaml &&\n\t rm ghcr.yaml update" CONFIGURE=$(printf -- "${CONFIGURE}") PUBLIC_KEY="${{ steps.verify.outputs.public_key }}" PUBLIC_KEY="$(printf -- "${PUBLIC_KEY//'
'/'\n'}")" - HELM_PATCH="yq e '.kubernetes.deployment.image.repository = \"${{ inputs.image_registry }}/${{ inputs.image_repo }}\"' -i helm/values.yaml\nyq e '.kubernetes.deployment.image.tag = \"${{ inputs.image_tag }}\"' -i helm/values.yaml" + HELM_PATCH="yq e '.kubernetes.deployment.image.repository = \"${{ inputs.image_registry }}/${{ inputs.image_repo }}\"' -i charts/connaisseur/values.yaml\nyq e '.kubernetes.deployment.image.tag = \"${{ inputs.image_tag }}\"' -i charts/connaisseur/values.yaml" HELM_PATCH=$(printf -- "${HELM_PATCH}") echo "# :building_construction: Build Information" >> ${GITHUB_STEP_SUMMARY} echo "" >> ${GITHUB_STEP_SUMMARY} diff --git a/.github/actions/context/action.yaml b/.github/actions/context/action.yaml index 6499abffb..62c482256 100644 --- a/.github/actions/context/action.yaml +++ b/.github/actions/context/action.yaml @@ -10,9 +10,6 @@ inputs: required: false default: "${{ github.repository }}" outputs: - cosign_version: - description: "Cosign version used for building Connaisseur image" - value: ${{ steps.get_context.outputs.COSIGN_VERSION }} chart_version: description: "Connaisseur Helm chart version" value: ${{ steps.get_context.outputs.CHART_VERSION }} @@ -53,24 +50,23 @@ runs: id: get_chart_version uses: mikefarah/yq@47f4f8c7939f887e851b35f14def6741b8f5396e # v4.31.2 with: - cmd: yq '.version' helm/Chart.yaml + cmd: yq '.version' charts/connaisseur/Chart.yaml - name: Get app version id: get_app_version uses: mikefarah/yq@47f4f8c7939f887e851b35f14def6741b8f5396e # v4.31.2 with: - cmd: yq '.appVersion' helm/Chart.yaml + cmd: yq '.appVersion' charts/connaisseur/Chart.yaml - name: Get original image id: get_original_image_repository uses: mikefarah/yq@47f4f8c7939f887e851b35f14def6741b8f5396e # v4.31.2 with: - cmd: yq '.kubernetes.deployment.image.repository' helm/values.yaml + cmd: yq '.kubernetes.deployment.image.repository' charts/connaisseur/values.yaml - name: Get context id: get_context run: | GHREF=${{ github.ref }} echo "github.ref is: ${GHREF}" CHART_VERSION=${{ steps.get_chart_version.outputs.result }} - COSIGN_VERSION=$(grep -Eo '^COSIGN_VERSION = .*' Makefile | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') CONFIGURED_IMAGE_REPO=${{ steps.get_original_image_repository.outputs.result }} ORIGINAL_REGISTRY=$(echo "${CONFIGURED_IMAGE_REPO}" | cut -d "/" -f 1) ORIGINAL_REPO=$(echo "${CONFIGURED_IMAGE_REPO}" | cut -d "/" -f 2- | cut -d ":" -f 1) @@ -84,7 +80,6 @@ runs: BUILD_REPO="${BUILD_REPO}-test" fi - echo COSIGN_VERSION=${COSIGN_VERSION} >> ${GITHUB_OUTPUT} echo CHART_VERSION=${CHART_VERSION} >> ${GITHUB_OUTPUT} echo ORIGINAL_REGISTRY=${ORIGINAL_REGISTRY} >> ${GITHUB_OUTPUT} echo ORIGINAL_REPO=${ORIGINAL_REPO} >> ${GITHUB_OUTPUT} @@ -122,8 +117,7 @@ runs: echo "# :clipboard: Context" >> ${GITHUB_STEP_SUMMARY} echo "
" >> ${GITHUB_STEP_SUMMARY} echo "" >> ${GITHUB_STEP_SUMMARY} - echo "" >> ${GITHUB_STEP_SUMMARY} - echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} echo "" >> ${GITHUB_STEP_SUMMARY} echo "" >> ${GITHUB_STEP_SUMMARY} echo "" >> ${GITHUB_STEP_SUMMARY} @@ -140,7 +134,6 @@ runs: echo "
:pushpin: Context Variables References" >> ${GITHUB_STEP_SUMMARY} echo "( job must run in workflow and needs: [context] mut be set for job)" >> ${GITHUB_STEP_SUMMARY} echo "
    " >> ${GITHUB_STEP_SUMMARY} - echo "
  • Cosign version:
    ${{ needs.context.outputs.cosign_version }}
  • " >> ${GITHUB_STEP_SUMMARY} echo "
  • Helm chart version:
    ${{ needs.context.outputs.chart_version }}
  • " >> ${GITHUB_STEP_SUMMARY} echo "
  • Original registry:
    ${{ needs.context.outputs.original_registry }}
  • " >> ${GITHUB_STEP_SUMMARY} echo "
  • Original repository:
    ${{ needs.context.outputs.original_repo }}
  • " >> ${GITHUB_STEP_SUMMARY} @@ -157,4 +150,3 @@ runs: echo "" >> ${GITHUB_STEP_SUMMARY} echo "Let's start building :rocket:" >> ${GITHUB_STEP_SUMMARY} shell: bash - diff --git a/.github/actions/k3s-cluster/action.yaml b/.github/actions/k3s-cluster/action.yaml index 8fca6a39a..f3a53b933 100644 --- a/.github/actions/k3s-cluster/action.yaml +++ b/.github/actions/k3s-cluster/action.yaml @@ -71,4 +71,3 @@ runs: kubectl wait --for=condition=complete --timeout=300s job/helm-install-traefik -n kube-system || true kubectl rollout status --watch --timeout 300s deployment/traefik -n kube-system shell: bash - diff --git a/.github/actions/k8s-version-config/action.yaml b/.github/actions/k8s-version-config/action.yaml index ae71c9d26..631aa4580 100644 --- a/.github/actions/k8s-version-config/action.yaml +++ b/.github/actions/k8s-version-config/action.yaml @@ -19,7 +19,7 @@ runs: - name: Adjust Configuration run: | if [[ $(echo "${{ inputs.k8s-version }}" | tail -c 3) -lt "19" ]]; then - yq e 'del(.kubernetes.deployment.securityContext.seccompProfile)' -i helm/values.yaml - yq e '.kubernetes.deployment.annotations."seccomp.security.alpha.kubernetes.io/pod" = "runtime/default"' -i helm/values.yaml + yq e 'del(.kubernetes.deployment.securityContext.seccompProfile)' -i charts/connaisseur/values.yaml + yq e '.kubernetes.deployment.annotations."seccomp.security.alpha.kubernetes.io/pod" = "runtime/default"' -i charts/connaisseur/values.yaml fi shell: bash diff --git a/.github/actions/safety/action.yaml b/.github/actions/safety/action.yaml deleted file mode 100644 index e257359cc..000000000 --- a/.github/actions/safety/action.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: safety -description: "Run Safety on image" -runs: - using: "composite" - steps: - - name: Install safety - run: | - pip3 install safety - shell: bash - - name: Run safety - run: | - safety check --file requirements.txt --file requirements_dev.txt --full-report -o screen --save-json safety-report.json - shell: bash - - name: Upload - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 - if: failure() - with: - name: safety-report - path: safety-report.json diff --git a/.github/actions/setup-notary/action.yaml b/.github/actions/setup-notary/action.yaml index 17017eff0..db628466f 100644 --- a/.github/actions/setup-notary/action.yaml +++ b/.github/actions/setup-notary/action.yaml @@ -15,7 +15,7 @@ runs: shell: bash - name: Trust root cert of notary instance run: | - sudo cp ./tests/data/notary_service_container/server/ca.crt /usr/local/share/ca-certificates/notary_root_ca.crt + sudo cp ./test/integration/notary_service_container/server/ca.crt /usr/local/share/ca-certificates/notary_root_ca.crt sudo update-ca-certificates shell: bash - name: Append notary ip to /etc/hosts @@ -24,9 +24,9 @@ runs: shell: bash - name: Configure notary client run: | - ./tests/integration/notary_init.sh + ./test/integration/notary_init.sh docker pull docker.io/securesystemsengineering/testimage:self-hosted-notary-signed DIGEST=$(docker images --digests | grep self-hosted-notary-signed | awk '{print $3}') export DIGEST_WITHOUT_PREFIX=$(echo ${DIGEST#sha256:}) - ./tests/integration/notary_addhash.sh ${DIGEST_WITHOUT_PREFIX} + ./test/integration/notary_addhash.sh ${DIGEST_WITHOUT_PREFIX} shell: bash diff --git a/.github/actions/trivy-config/action.yaml b/.github/actions/trivy-config/action.yaml index 1ae6df225..4e23b58bd 100644 --- a/.github/actions/trivy-config/action.yaml +++ b/.github/actions/trivy-config/action.yaml @@ -1,5 +1,9 @@ name: trivy-config description: 'Run Trivy on config' +inputs: + output: + description: 'Trivy output either "sarif" (GITHUB_TOKEN with security-events:write) or print results as "table" and fail on error' + required: false runs: using: "composite" steps: @@ -10,10 +14,25 @@ runs: - name: Render Helm charts run: | mkdir deployment - helm template helm > deployment/deployment.yaml + helm template charts/connaisseur > deployment/deployment.yaml shell: bash - name: Scan deployment.yaml uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # v0.12.0 + if: inputs.output == 'table' + with: + scan-type: "config" + scan-ref: "deployment" + format: 'table' + - name: Scan Dockerfiles + uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # v0.12.0 + if: inputs.output == 'table' + with: + scan-type: "config" + scan-ref: "build" + format: 'table' + - name: Scan deployment.yaml + uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # v0.12.0 + if: inputs.output == 'sarif' with: scan-type: "config" scan-ref: "deployment" @@ -21,12 +40,14 @@ runs: output: 'reports/trivy-k8s-results.sarif' - name: Scan Dockerfiles uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # v0.12.0 + if: inputs.output == 'sarif' with: scan-type: "config" - scan-ref: "docker" + scan-ref: "build" format: 'sarif' output: 'reports/trivy-docker-results.sarif' - name: Upload uses: github/codeql-action/upload-sarif@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2.2.5 + if: inputs.output == 'sarif' with: sarif_file: 'reports' diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1f67c1903..9f2a41815 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,19 +1,18 @@ version: 2 updates: - - package-ecosystem: "pip" + - package-ecosystem: "gomod" directory: "/" schedule: interval: "daily" commit-message: prefix: "update" - insecure-external-code-execution: "deny" target-branch: "develop" groups: - pip-packages: + gomod-packages: patterns: - "*" - package-ecosystem: "docker" - directory: "/docker" + directory: "/build" schedule: interval: "daily" commit-message: @@ -34,4 +33,3 @@ updates: gh-actions-packages: patterns: - "*" - diff --git a/.github/workflows/.reusable-build.yml b/.github/workflows/.reusable-build.yml index 07700f7b5..34ff5d1e1 100644 --- a/.github/workflows/.reusable-build.yml +++ b/.github/workflows/.reusable-build.yml @@ -1,17 +1,15 @@ name: build +permissions: {} + on: workflow_call: inputs: - build: - description: "Run actual build job?" - type: boolean - required: false - default: true + skip: + description: "Want to skip running certain jobs 'none', 'non-required', 'all'?" + type: string + default: "none" outputs: - cosign_version: - description: "Cosign version used for building Connaisseur image" - value: ${{ jobs.context.outputs.cosign_version }} cosign_public_key: description: "Cosign public key used for signing Connaisseur image" value: ${{ jobs.build.outputs.cosign_public_key }} @@ -49,19 +47,17 @@ on: description: "Repository- and workflow-specific build labels" value: ${{ jobs.context.outputs.build_labels }} -permissions: {} - jobs: context: runs-on: ubuntu-latest + if: inputs.skip != 'all' permissions: {} outputs: - cosign_version: ${{ steps.get_context.outputs.cosign_version }} chart_version: ${{ steps.get_context.outputs.chart_version }} original_registry: ${{ steps.get_context.outputs.original_registry }} original_repo: ${{ steps.get_context.outputs.original_repo }} - original_tag: ${{ steps.get_context.outputs.original_tag }} original_image: ${{ steps.get_context.outputs.original_image }} + original_tag: ${{ steps.get_context.outputs.original_tag }} build_registry: ${{ steps.get_context.outputs.build_registry }} build_repo: ${{ steps.get_context.outputs.build_repo }} build_tag: ${{ steps.get_context.outputs.build_tag }} @@ -77,7 +73,9 @@ jobs: build: runs-on: ubuntu-latest - if: inputs.build + if: | + inputs.skip != 'non-required' && + inputs.skip != 'all' needs: [context] permissions: packages: write @@ -97,6 +95,5 @@ jobs: image_labels: ${{ needs.context.outputs.build_labels }} repo_owner: ${{ github.repository_owner }} repo_token: ${{ secrets.GITHUB_TOKEN }} - cosign_version: ${{ needs.context.outputs.cosign_version }} cosign_private_key: ${{ secrets.COSIGN_PRIVATE_KEY }} cosign_password: ${{ secrets.COSIGN_PASSWORD }} diff --git a/.github/workflows/.reusable-ci.yml b/.github/workflows/.reusable-ci.yml new file mode 100644 index 000000000..edf77375d --- /dev/null +++ b/.github/workflows/.reusable-ci.yml @@ -0,0 +1,177 @@ +name: ci + +permissions: {} + +on: + workflow_call: + inputs: + skip_build: + description: "Want to skip running certain build jobs 'none', 'non-required', 'all'?" + type: string + default: "all" + required: false + skip_compliance_checks: + description: "Want to skip running certain compliance jobs 'none', 'non-required', 'all'?" + type: string + default: "all" + required: false + skip_unit_tests: + description: "Want to skip running certain unit test jobs 'none', 'non-required', 'all'?" + type: string + default: "all" + required: false + skip_sast: + description: "Want to skip running certain sast jobs 'none', 'non-required', 'all'?" + type: string + default: "all" + required: false + skip_sca: + description: "Want to skip running certain sca jobs 'none', 'non-required', 'all'?" + type: string + default: "all" + required: false + skip_docs: + description: "Want to skip running certain docs jobs 'none', 'non-required', 'all'?" + type: string + default: "all" + required: false + skip_integration_tests: + description: "Want to skip running certain integration test jobs 'none', 'non-required', 'all'?" + type: string + default: "all" + required: false + output_type: + description: 'Output either "sarif" (GITHUB_TOKEN with security-events:write) or print results as "table" and fail on error' + type: string + default: 'sarif' + required: false + +defaults: + run: + shell: bash + +jobs: + conditionals: + runs-on: ubuntu-latest + outputs: + skip_build: ${{ steps.conditionals.outputs.skip_build }} + skip_compliance_checks: ${{ steps.conditionals.outputs.skip_compliance_checks }} + skip_unit_tests: ${{ steps.conditionals.outputs.skip_unit_tests }} + skip_sast: ${{ steps.conditionals.outputs.skip_sast }} + skip_sca: ${{ steps.conditionals.outputs.skip_sca }} + skip_docs: ${{ steps.conditionals.outputs.skip_docs }} + skip_integration_tests: ${{ steps.conditionals.outputs.skip_integration_tests }} + output_type: ${{ steps.conditionals.outputs.output_type }} + steps: + - name: CI conditionals + id: conditionals + run: | + echo "skip_build=${{ inputs.skip_build }}" >> ${GITHUB_OUTPUT} + echo "skip_compliance_checks=${{ inputs.skip_compliance_checks }}" >> ${GITHUB_OUTPUT} + echo "skip_unit_tests=${{ inputs.skip_unit_tests }}" >> ${GITHUB_OUTPUT} + echo "skip_sast=${{ inputs.skip_sast }}" >> ${GITHUB_OUTPUT} + echo "skip_sca=${{ inputs.skip_sca }}" >> ${GITHUB_OUTPUT} + echo "skip_docs=${{ inputs.skip_docs }}" >> ${GITHUB_OUTPUT} + echo "skip_integration_tests=${{ inputs.skip_integration_tests }}" >> ${GITHUB_OUTPUT} + echo "output_type=${{ inputs.output_type }}" >> ${GITHUB_OUTPUT} + - name: Show conditionals + id: show_conditionals + run: | + get_output() { case "$1" in "none") echo ":white_check_mark:";; "non-required") echo ":information_source:";; "all") echo ":x:";; *) echo "Unknown value";; esac; } + echo "# :pencil: CI Settings" >> ${GITHUB_STEP_SUMMARY} + echo "
Build ContextValue
Cosign version${{ steps.get_context.outputs.COSIGN_VERSION }}
Helm chart version${{ steps.get_context.outputs.CHART_VERSION }}
Helm chart version${{ steps.get_context.outputs.CHART_VERSION }}
Original registry${{ steps.get_context.outputs.ORIGINAL_REGISTRY }}
Original repository${{ steps.get_context.outputs.ORIGINAL_REPO }}
Original tag${{ steps.get_context.outputs.ORIGINAL_TAG }}
" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + echo "
SettingValue
Run Docs$(get_output ${{ steps.conditionals.outputs.skip_docs }})
Run Build$(get_output ${{ steps.conditionals.outputs.skip_build }})
Run Compliance$(get_output ${{ steps.conditionals.outputs.skip_compliance_checks }})
Run Unit Tests$(get_output ${{ steps.conditionals.outputs.skip_unit_tests }})
Run SAST$(get_output ${{ steps.conditionals.outputs.skip_sast }})
Run SCA$(get_output ${{ steps.conditionals.outputs.skip_sca }})
Run Integration Tests$(get_output ${{ steps.conditionals.outputs.skip_integration_tests }})
Report type${{ steps.conditionals.outputs.output_type }}
" >> ${GITHUB_STEP_SUMMARY} + echo "($(get_output 'none') - run all jobs, $(get_output 'non-required') - run important/required jobs only, $(get_output 'all') - skip jobs)" >> ${GITHUB_STEP_SUMMARY} + echo "" >> ${GITHUB_STEP_SUMMARY} + + build: + uses: ./.github/workflows/.reusable-build.yml + needs: [conditionals] + permissions: + packages: write + secrets: inherit + with: + skip: ${{ needs.conditionals.outputs.skip_build }} + + compliance: + uses: ./.github/workflows/.reusable-compliance.yml + needs: [conditionals] + permissions: + contents: write + id-token: write + security-events: write + actions: read + checks: read + deployments: read + issues: read + discussions: read + packages: read + pages: read + pull-requests: read + repository-projects: read + statuses: read + secrets: inherit + with: + skip: ${{ needs.conditionals.outputs.skip_compliance_checks }} + + unit-test: + uses: ./.github/workflows/.reusable-unit-test.yml + needs: [conditionals] + with: + skip: ${{ needs.conditionals.outputs.skip_unit_tests }} + + sast: + uses: ./.github/workflows/.reusable-sast.yml + needs: [conditionals] + permissions: + security-events: write + pull-requests: read + with: + skip: ${{ needs.conditionals.outputs.skip_sast }} + output: ${{ needs.conditionals.outputs.output_type }} + + sca: + uses: ./.github/workflows/.reusable-sca.yml + needs: [conditionals, build] + permissions: + contents: write + security-events: write + packages: read + secrets: inherit + with: + registry: ${{ needs.build.outputs.build_registry }} + repo_owner: ${{ github.repository_owner }} + image: ${{ needs.build.outputs.build_image }} + skip: ${{ needs.conditionals.outputs.skip_sca }} + output: ${{ needs.conditionals.outputs.output_type }} + + docs: + uses: ./.github/workflows/.reusable-docs.yaml + needs: [conditionals] + permissions: + contents: write + with: + skip: ${{ needs.conditionals.outputs.skip_docs }} + + integration-test: + uses: ./.github/workflows/.reusable-integration-test.yml + needs: [conditionals, build] + permissions: + packages: read + secrets: inherit + with: + build_registry: ${{ needs.build.outputs.build_registry }} + repo_owner: ${{ github.repository_owner }} + build_image_repository: ${{ needs.build.outputs.build_registry }}/${{ needs.build.outputs.build_repo }} + build_tag: ${{ needs.build.outputs.build_tag }} + skip: ${{ needs.conditionals.outputs.skip_integration_tests }} + cosign_public_key: ${{ needs.build.outputs.cosign_public_key }} diff --git a/.github/workflows/.reusable-compliance.yml b/.github/workflows/.reusable-compliance.yml index 9efbd3434..2ad029de3 100644 --- a/.github/workflows/.reusable-compliance.yml +++ b/.github/workflows/.reusable-compliance.yml @@ -2,13 +2,21 @@ name: compliance on: workflow_call: + inputs: + skip: + description: "Want to skip running certain jobs 'none', 'non-required', 'all'?" + type: string + default: "none" permissions: read-all jobs: ossf-scorecard: runs-on: ubuntu-latest - if: ${{ github.ref_name == 'master' || github.event_name == 'pull_request' }} + if: | + (github.ref_name == 'master' || github.event_name == 'pull_request') && + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: security-events: write id-token: write @@ -32,7 +40,10 @@ jobs: dependency-review: name: dependency review runs-on: ubuntu-latest - if: github.event_name == 'pull_request' + if: | + github.event_name == 'pull_request' && + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: contents: write steps: @@ -43,8 +54,10 @@ jobs: check-commit-message: runs-on: ubuntu-latest + if: | + github.event_name == 'pull_request' && + inputs.skip != 'all' permissions: {} - if: ${{ github.event_name == 'pull_request' }} # Only run on PRs steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 diff --git a/.github/workflows/.reusable-docs.yaml b/.github/workflows/.reusable-docs.yaml index ecfd30988..a7e4830ae 100644 --- a/.github/workflows/.reusable-docs.yaml +++ b/.github/workflows/.reusable-docs.yaml @@ -1,13 +1,19 @@ name: docs +permissions: {} + on: workflow_call: - -permissions: {} + inputs: + skip: + description: "Want to skip running certain jobs 'none', 'non-required', 'all'?" + type: string + default: "none" jobs: deploy: runs-on: ubuntu-latest + if: inputs.skip != 'all' permissions: contents: write steps: @@ -25,6 +31,7 @@ jobs: run: | pip install -r docs/requirements_docs.txt - name: Deploy + if: inputs.skip != 'non-required' run: | if [[ "${GITHUB_REF}" == "refs/tags/v"* ]]; then diff --git a/.github/workflows/.reusable-integration-test.yml b/.github/workflows/.reusable-integration-test.yml index e07bdf040..09e7bd040 100644 --- a/.github/workflows/.reusable-integration-test.yml +++ b/.github/workflows/.reusable-integration-test.yml @@ -1,5 +1,7 @@ name: integration-test +permissions: {} + on: workflow_call: inputs: @@ -15,15 +17,14 @@ on: build_tag: description: "Tag of build image used for testing" type: string - skip_integration_tests: - description: "Want to skip running certain integration tests 'none', 'non-required', 'all'?" + skip: + description: "Want to skip running certain jobs 'none', 'non-required', 'all'?" type: string + default: "none" cosign_public_key: description: "Cosign public key used for signing the build image" type: string -permissions: {} - env: IMAGEPULLSECRET: dockerconfigjson-ghcr @@ -31,7 +32,7 @@ jobs: integration-test: name: functional runs-on: ubuntu-latest - if: inputs.skip_integration_tests != 'all' + if: inputs.skip != 'all' permissions: packages: read env: @@ -75,18 +76,18 @@ jobs: with: k8s-version: v1.25 - name: Get alerting endpoint IP - id: get_alerting_endpoint_ip + id: get_ip uses: ./.github/actions/alerting-endpoint - name: Run test run: | - bash tests/integration/integration-test.sh "${{ matrix.integration-test-arg }}" + bash test/integration/integration-test.sh "${{ matrix.integration-test-arg }}" env: - ALERTING_ENDPOINT_IP: ${{ steps.get_alerting_endpoint_ip.outputs.ip }} + ALERTING_ENDPOINT_IP: ${{ steps.get_ip.outputs.ip }} - name: Display Connaisseur configuration if: always() run: | echo "::group::values.yaml" - yq e '... comments=""' helm/values.yaml + yq e '... comments=""' charts/connaisseur/values.yaml echo "::endgroup::" - name: Display k8s state if integration test failed if: failure() @@ -102,8 +103,8 @@ jobs: name: optional runs-on: ubuntu-latest if: | - inputs.skip_integration_tests != 'non-required' && - inputs.skip_integration_tests != 'all' + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: packages: read env: @@ -141,18 +142,18 @@ jobs: with: k8s-version: v1.28 - name: Get alerting endpoint IP - id: get_alerting_endpoint_ip + id: get_ip uses: ./.github/actions/alerting-endpoint - name: Run test run: | - bash tests/integration/integration-test.sh "${{ matrix.integration-test-arg }}" + bash test/integration/integration-test.sh "${{ matrix.integration-test-arg }}" env: - ALERTING_ENDPOINT_IP: ${{ steps.get_alerting_endpoint_ip.outputs.ip }} + ALERTING_ENDPOINT_IP: ${{ steps.get_ip.outputs.ip }} - name: Display Connaisseur configuration if: always() run: | echo "::group::values.yaml" - yq e '... comments=""' helm/values.yaml + yq e '... comments=""' charts/connaisseur/values.yaml echo "::endgroup::" - name: Display k8s state if integration test failed if: failure() @@ -164,13 +165,10 @@ jobs: run: | kubectl logs -n connaisseur -lapp.kubernetes.io/name=connaisseur --prefix=true --tail=-1 - self-hosted-notary: - name: self-hosted-notary + k8s-versions: + name: k8s versions runs-on: ubuntu-latest - if: | - inputs.skip_integration_tests != 'self-hosted-notary' && - inputs.skip_integration_tests != 'non-required' && - inputs.skip_integration_tests != 'all' + if: inputs.skip != 'all' permissions: packages: read env: @@ -180,10 +178,12 @@ jobs: strategy: fail-fast: false matrix: - integration-test-arg: - [ - "self-hosted-notary" - ] + k8s-version: [ + "v1.26", + "v1.27", + "v1.28", + "v1.29", + ] steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -196,53 +196,32 @@ jobs: - name: Install yq run: | sudo snap install yq - - name: Setup notary signer instance - run: | - docker run -d -p 7899:7899 -v ./tests/data/notary_service_container/signer:/etc/docker/notary-signer/ notary:signer -config=/etc/docker/notary-signer/config.json - - name: Get notary signer instance IP - id: get_notary_signer_ip - uses: ./.github/actions/notary-signer-ip - - name: Setup notary server instance - run: | - docker run -d -p 4443:4443 --add-host notary.signer:${{ steps.get_notary_signer_ip.outputs.notary_signer_ip }} -v ./tests/data/notary_service_container/server:/etc/docker/notary-server notary:server -config=/etc/docker/notary-server/config.json -logf=json - - name: Get container IPs - id: get_notary_server_ip - uses: ./.github/actions/notary-server-ip - - name: Populate notary instance with trust data - uses: ./.github/actions/setup-notary - id: setup_notary - env: - NOTARY_IP: ${{ steps.get_notary_server_ip.outputs.notary_ip }} - uses: ./.github/actions/k8s-version-config name: Setup k8s cluster with: - k8s-version: v1.28 - - name: Run test - run: | - bash tests/integration/integration-test.sh "${{ matrix.integration-test-arg }}" - env: - NOTARY_IP: ${{ steps.get_notary_server_ip.outputs.notary_ip }} - - name: Display Connaisseur configuration - if: always() + k8s-version: ${{ matrix.k8s-version }} + - name: Run pre-config and workload integration tests run: | - echo "::group::values.yaml" - yq e '... comments=""' helm/values.yaml - echo "::endgroup::" - - name: Display k8s state if integration test failed + bash test/integration/integration-test.sh "pre-and-workload" + - name: Display k8s state and logs if integration test failed if: failure() run: | kubectl describe deployments.apps -n connaisseur -lapp.kubernetes.io/name=connaisseur kubectl describe pods -n connaisseur -lapp.kubernetes.io/name=connaisseur - - name: Display logs if integration test failed - if: failure() - run: | kubectl logs -n connaisseur -lapp.kubernetes.io/name=connaisseur --prefix=true --tail=-1 + - name: Display Connaisseur configuration + if: always() + run: | + echo "::group::values.yaml" + yq e '... comments=""' charts/connaisseur/values.yaml + echo "::endgroup::" - - k8s-versions: - name: k8s versions + optional-k8s-versions: + name: optional k8s versions runs-on: ubuntu-latest - if: inputs.skip_integration_tests != 'all' + if: | + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: packages: read env: @@ -253,15 +232,11 @@ jobs: fail-fast: false matrix: k8s-version: [ - "v1.20", "v1.21", "v1.22", "v1.23", "v1.24", "v1.25", - "v1.26", - "v1.27", - "v1.28", ] steps: - name: Checkout code @@ -281,10 +256,89 @@ jobs: k8s-version: ${{ matrix.k8s-version }} - name: Run pre-config and workload integration tests run: | - bash tests/integration/integration-test.sh "pre-and-workload" + bash test/integration/integration-test.sh "pre-and-workload" - name: Display k8s state and logs if integration test failed if: failure() run: | kubectl describe deployments.apps -n connaisseur -lapp.kubernetes.io/name=connaisseur kubectl describe pods -n connaisseur -lapp.kubernetes.io/name=connaisseur kubectl logs -n connaisseur -lapp.kubernetes.io/name=connaisseur --prefix=true --tail=-1 + - name: Display Connaisseur configuration + if: always() + run: | + echo "::group::values.yaml" + yq e '... comments=""' charts/connaisseur/values.yaml + echo "::endgroup::" + + self-hosted-notary: + name: self-hosted-notary + runs-on: ubuntu-latest + if: | + inputs.skip != 'non-required' && + inputs.skip != 'all' + permissions: + packages: read + env: + IMAGE: ${{ inputs.build_image_repository }} + TAG: ${{ inputs.build_tag }} + COSIGN_PUBLIC_KEY: ${{ inputs.cosign_public_key }} + strategy: + fail-fast: false + matrix: + integration-test-arg: + [ + "self-hosted-notary" + ] + steps: + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Login with registry + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ inputs.build_registry }} + username: ${{ inputs.repo_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Install yq + run: | + sudo snap install yq + - name: Setup notary signer instance + run: | + docker run -d -p 7899:7899 -v ./test/integration/notary_service_container/signer:/etc/docker/notary-signer/ notary:signer -config=/etc/docker/notary-signer/config.json + - name: Get notary signer instance IP + id: get_notary_signer_ip + uses: ./.github/actions/notary-signer-ip + - name: Setup notary server instance + run: | + docker run -d -p 4443:4443 --add-host notary.signer:${{ steps.get_notary_signer_ip.outputs.notary_signer_ip }} -v ./test/integration/notary_service_container/server:/etc/docker/notary-server notary:server -config=/etc/docker/notary-server/config.json -logf=json + - name: Get container IPs + id: get_notary_server_ip + uses: ./.github/actions/notary-server-ip + - name: Populate notary instance with trust data + uses: ./.github/actions/setup-notary + id: setup_notary + env: + NOTARY_IP: ${{ steps.get_notary_server_ip.outputs.notary_ip }} + - uses: ./.github/actions/k8s-version-config + name: Setup k8s cluster + with: + k8s-version: v1.28 + - name: Run test + run: | + bash test/integration/integration-test.sh "${{ matrix.integration-test-arg }}" + env: + NOTARY_IP: ${{ steps.get_notary_server_ip.outputs.notary_ip }} + - name: Display Connaisseur configuration + if: always() + run: | + echo "::group::values.yaml" + yq e '... comments=""' charts/connaisseur/values.yaml + echo "::endgroup::" + - name: Display k8s state if integration test failed + if: failure() + run: | + kubectl describe deployments.apps -n connaisseur -lapp.kubernetes.io/name=connaisseur + kubectl describe pods -n connaisseur -lapp.kubernetes.io/name=connaisseur + - name: Display logs if integration test failed + if: failure() + run: | + kubectl logs -n connaisseur -lapp.kubernetes.io/name=connaisseur --prefix=true --tail=-1 diff --git a/.github/workflows/.reusable-sast.yml b/.github/workflows/.reusable-sast.yml index 24ca5e6c6..2bbc45671 100644 --- a/.github/workflows/.reusable-sast.yml +++ b/.github/workflows/.reusable-sast.yml @@ -2,71 +2,129 @@ name: sast on: workflow_call: + inputs: + skip: + description: "Want to skip running certain jobs 'none', 'non-required', 'all'?" + type: string + default: "none" + output: + description: 'Output either "sarif" (GITHUB_TOKEN with security-events:write) or print results as "table" and fail on error' + type: string + required: false + default: 'sarif' permissions: {} jobs: - codeql: + checkov: runs-on: ubuntu-latest + if: | + (github.actor != 'dependabot[bot]') && + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: security-events: write - pull-requests: read steps: - - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Initialize CodeQL - uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 - with: - languages: 'python' - - name: Analyze - uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Render Helm charts + run: | + mkdir deployment + helm template charts/connaisseur --namespace connaisseur > deployment/deployment.yaml + shell: bash + - name: Scan + if: inputs.output == 'table' + uses: bridgecrewio/checkov-action@dc96629c5657b0f58e6eb7518a59086803c0572a # v12.2678.0 + with: + output_format: cli + soft_fail: false + file: deployment/deployment.yaml + - name: Scan + if: inputs.output == 'sarif' + uses: bridgecrewio/checkov-action@dc96629c5657b0f58e6eb7518a59086803c0572a # v12.2678.0 + with: + output_file_path: console,checkov-results.sarif + output_format: cli,sarif + soft_fail: true + file: deployment/deployment.yaml + - name: Upload + if: inputs.output == 'sarif' + uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + with: + sarif_file: checkov-results.sarif - black: + codeql: runs-on: ubuntu-latest + if: | + (github.actor != 'dependabot[bot]') && + inputs.skip != 'non-required' && + inputs.skip != 'all' && + inputs.output == 'sarif' + permissions: + pull-requests: read + security-events: write steps: - - name: Checkout code + - name: Checkout repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Install packages - run: | - pip3 install setuptools wheel - pip3 install black - - name: Test formatting - run: | - python3 -m black . 2>&1 | grep -q "reformatted" && { echo 'Not properly formatted.'; exit 1; } || true + - name: Initialize CodeQL + uses: github/codeql-action/init@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2.22.7 + with: + languages: 'go' + - name: Analyze + uses: github/codeql-action/analyze@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2.22.7 - pylint: + golangci-lint: runs-on: ubuntu-latest - container: - image: python:3.11-slim + if: | + (github.actor != 'dependabot[bot]') && + inputs.skip != 'all' + permissions: + security-events: write steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Install - run: | - pip3 install -r requirements_dev.txt - - name: Lint - run: pylint --ignore-patterns=tests,coverage connaisseur + - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + cache: false + go-version: '1.21' + - name: Analyze + uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 + with: + version: latest + args: '--timeout=10m --skip-dirs="test" --tests=false' - bandit: + gosec: runs-on: ubuntu-latest + if: | + (github.actor != 'dependabot[bot]') && + inputs.skip != 'all' permissions: security-events: write - container: - image: python:slim steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Install Bandit - run: pip3 install bandit bandit_sarif_formatter - - name: Run Bandit - run: bandit -r -f sarif -o bandit-results.sarif connaisseur/ --exit-zero + - name: Analyze + uses: securego/gosec@55d79496019a560e16e73e1948dee20a1fad631a # v2.18.2 + if: inputs.output == 'table' + with: + args: '-fmt text -exclude-dir=test -exclude-dir=tools ./...' + - name: Analyze + uses: securego/gosec@55d79496019a560e16e73e1948dee20a1fad631a # v2.18.2 + if: inputs.output == 'sarif' + with: + args: '-exclude-dir=test -exclude-dir=tools -no-fail -fmt sarif -out gosec-results.sarif ./...' - name: Upload - uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/upload-sarif@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 + if: inputs.output == 'sarif' with: - sarif_file: 'bandit-results.sarif' + sarif_file: 'gosec-results.sarif' hadolint: runs-on: ubuntu-latest + if: | + (github.actor != 'dependabot[bot]') && + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: security-events: write steps: @@ -74,84 +132,95 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Scan uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0 + if: inputs.output == 'table' with: - dockerfile: docker/Dockerfile - no-fail: true + dockerfile: build/Dockerfile + format: tty + no-fail: false + - name: Scan + uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0 + if: inputs.output == 'sarif' + with: + dockerfile: build/Dockerfile format: sarif + no-fail: true output-file: hadolint-results.sarif - name: Upload - uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/upload-sarif@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 + if: inputs.output == 'sarif' with: sarif_file: 'hadolint-results.sarif' kubelinter: runs-on: ubuntu-latest + if: | + (github.actor != 'dependabot[bot]') && + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: security-events: write steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Scan - continue-on-error: true uses: stackrox/kube-linter-action@ca0d55b925470deb5b04b556e6c4276ea94d03c3 # v1.0.4 + if: inputs.output == 'table' + with: + config: .github/.kube-linter/config.yaml + directory: charts/connaisseur + format: plain + - name: Scan + uses: stackrox/kube-linter-action@ca0d55b925470deb5b04b556e6c4276ea94d03c3 # v1.0.4 + if: inputs.output == 'sarif' with: - directory: helm - config: .kube-linter/config.yaml + config: .github/.kube-linter/config.yaml + directory: charts/connaisseur format: sarif output-file: kubelinter-results.sarif - name: Upload - uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/upload-sarif@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 + if: inputs.output == 'sarif' with: sarif_file: 'kubelinter-results.sarif' - trivy-config-scan: - name: trivy config - runs-on: ubuntu-latest - permissions: - security-events: write - steps: - - name: Checkout code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Run Trivy - uses: ./.github/actions/trivy-config - - checkov: + semgrep: runs-on: ubuntu-latest + if: | + (github.actor != 'dependabot[bot]') && + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: security-events: write + container: + image: returntocorp/semgrep steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Render Helm charts - run: | - rm -rf tests # remove 'tests' folder from scan - mkdir deployment - helm template helm > deployment/deployment.yaml - shell: bash - name: Scan - uses: bridgecrewio/checkov-action@dc96629c5657b0f58e6eb7518a59086803c0572a # v12.2678.0 - with: - soft_fail: true - output_format: cli,sarif - output_file_path: console,checkov-results.sarif + if: inputs.output == 'table' + run: semgrep ci --config=auto --suppress-errors --text + - name: Scan + if: inputs.output == 'sarif' + run: semgrep ci --config=auto --suppress-errors --sarif --output=semgrep-results.sarif || exit 0 - name: Upload uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + if: inputs.output == 'sarif' with: - sarif_file: checkov-results.sarif + sarif_file: semgrep-results.sarif - semgrep: + trivy-config-scan: + name: trivy config runs-on: ubuntu-latest + if: | + (github.actor != 'dependabot[bot]') && + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: security-events: write - container: - image: returntocorp/semgrep - if: (github.actor != 'dependabot[bot]') steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Scan - run: semgrep ci --config=auto --suppress-errors --sarif --output=semgrep-results.sarif || exit 0 - - name: Upload - uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + - name: Run Trivy + uses: ./.github/actions/trivy-config with: - sarif_file: semgrep-results.sarif + output: ${{ inputs.output }} diff --git a/.github/workflows/.reusable-sca.yml b/.github/workflows/.reusable-sca.yml index 23e62d9f6..6dc88a8dc 100644 --- a/.github/workflows/.reusable-sca.yml +++ b/.github/workflows/.reusable-sca.yml @@ -1,5 +1,7 @@ name: sca +permissions: {} + on: workflow_call: inputs: @@ -17,28 +19,21 @@ on: type: string required: false default: '' + skip: + description: "Want to skip running certain jobs 'none', 'non-required', 'all'?" + type: string + default: "none" output: description: 'Output either "sarif" (GITHUB_TOKEN with security-events:write) or print results as "table" and fail on error' type: string required: false default: 'sarif' -permissions: {} - jobs: - safety: - runs-on: ubuntu-latest - permissions: - packages: read - steps: - - name: Checkout code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Run - uses: ./.github/actions/safety - trivy-image-scan: name: trivy image runs-on: ubuntu-latest + if: inputs.skip != 'all' permissions: packages: read security-events: write @@ -59,6 +54,9 @@ jobs: grype: name: grype runs-on: ubuntu-latest + if: | + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: packages: read security-events: write @@ -80,6 +78,9 @@ jobs: dependency-submission: name: syft / dependency review runs-on: ubuntu-latest + if: | + inputs.skip != 'non-required' && + inputs.skip != 'all' permissions: packages: read contents: write @@ -96,4 +97,4 @@ jobs: with: image: ${{ inputs.image }} format: cyclonedx-json - dependency-snapshot: true + dependency-snapshot: ${{ inputs.output == 'sarif' }} diff --git a/.github/workflows/.reusable-unit-test.yml b/.github/workflows/.reusable-unit-test.yml index 0da4e15e2..2cce70847 100644 --- a/.github/workflows/.reusable-unit-test.yml +++ b/.github/workflows/.reusable-unit-test.yml @@ -1,24 +1,28 @@ name: unit-test +permissions: {} + on: workflow_call: - -permissions: {} + inputs: + skip: + description: "Want to skip running certain jobs 'none', 'all'?" + type: string + default: "none" jobs: - pytest: + gotest: + name: unit tests runs-on: ubuntu-latest - container: - image: python:3.11 + if: inputs.skip != 'all' steps: - name: Checkout code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Install - run: | - pip3 install -r requirements_dev.txt && pip3 install . + - name: Setup + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + go-version: '1.21' - name: Test - run: pytest --cov=connaisseur --cov-report=xml tests/ + run: go test ./... -race -coverprofile=coverage.out -covermode=atomic - name: Upload - uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab # v4.1.0 - with: - file: coverage.xml + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml deleted file mode 100644 index cb4886d8c..000000000 --- a/.github/workflows/cicd.yaml +++ /dev/null @@ -1,96 +0,0 @@ -name: cicd - -on: - push: - branches: - - master - - develop - pull_request: - branches: - - master - - develop - -permissions: {} - -defaults: - run: - shell: bash - -env: - SKIP_INTEGRATION_TESTS: 'self-hosted-notary' # 'none', 'non-required', 'all', 'self-hosted-notary' - -jobs: - conditionals: - runs-on: ubuntu-latest - outputs: - skip_integration_tests: ${{ steps.conditionals.outputs.skip_integration_tests }} - steps: - - name: CI conditionals - id: conditionals - run: | - echo "skip_integration_tests=${SKIP_INTEGRATION_TESTS}" >> ${GITHUB_OUTPUT} - - build: - uses: ./.github/workflows/.reusable-build.yml - permissions: - packages: write - secrets: inherit - - compliance: - uses: ./.github/workflows/.reusable-compliance.yml - permissions: - contents: write - id-token: write - security-events: write - actions: read - checks: read - deployments: read - issues: read - discussions: read - packages: read - pages: read - pull-requests: read - repository-projects: read - statuses: read - secrets: inherit - - sast: - uses: ./.github/workflows/.reusable-sast.yml - permissions: - security-events: write - pull-requests: read - - sca: - uses: ./.github/workflows/.reusable-sca.yml - needs: [build] - permissions: - contents: write - security-events: write - packages: read - secrets: inherit - with: - registry: ${{ needs.build.outputs.build_registry }} - repo_owner: ${{ github.repository_owner }} - image: ${{ needs.build.outputs.build_image }} - - unit-test: - uses: ./.github/workflows/.reusable-unit-test.yml - - docs: - uses: ./.github/workflows/.reusable-docs.yaml - permissions: - contents: write - - integration-test: - uses: ./.github/workflows/.reusable-integration-test.yml - needs: [conditionals, build] - permissions: - packages: read - secrets: inherit - with: - build_registry: ${{ needs.build.outputs.build_registry }} - repo_owner: ${{ github.repository_owner }} - build_image_repository: ${{ needs.build.outputs.build_registry }}/${{ needs.build.outputs.build_repo }} - build_tag: ${{ needs.build.outputs.build_tag }} - skip_integration_tests: ${{ needs.conditionals.outputs.skip_integration_tests }} - cosign_public_key: ${{ needs.build.outputs.cosign_public_key }} diff --git a/.github/workflows/nightly-build.yaml b/.github/workflows/nightly-build.yaml index dd30a8f79..e94e81fc8 100644 --- a/.github/workflows/nightly-build.yaml +++ b/.github/workflows/nightly-build.yaml @@ -1,58 +1,39 @@ name: nightly-build +permissions: {} + on: schedule: - cron: "30 1 * * *" -permissions: {} - defaults: run: shell: bash -env: - SKIP_INTEGRATION_TESTS: 'non-required' # 'none', 'non-required', 'all' - jobs: - conditionals: - runs-on: ubuntu-latest - outputs: - skip_integration_tests: ${{ steps.conditionals.outputs.skip_integration_tests }} - steps: - - name: CI conditionals - id: conditionals - run: | - echo "skip_integration_tests=${SKIP_INTEGRATION_TESTS}" >> ${GITHUB_OUTPUT} - - build: - uses: ./.github/workflows/.reusable-build.yml + ci: + uses: ./.github/workflows/.reusable-ci.yml permissions: packages: write - secrets: inherit - - sca: - uses: ./.github/workflows/.reusable-sca.yml - needs: [build] - permissions: contents: write + id-token: write security-events: write - packages: read - secrets: inherit - with: - registry: ${{ needs.build.outputs.build_registry }} - repo_owner: ${{ github.repository_owner }} - image: ${{ needs.build.outputs.build_image }} - - integration-test: - uses: ./.github/workflows/.reusable-integration-test.yml - needs: [conditionals, build] - permissions: - packages: read + actions: read + checks: read + deployments: read + issues: read + discussions: read + pages: read + pull-requests: read + repository-projects: read + statuses: read secrets: inherit with: - build_registry: ${{ needs.build.outputs.build_registry }} - repo_owner: ${{ github.repository_owner }} - build_image_repository: ${{ needs.build.outputs.build_registry }}/${{ needs.build.outputs.build_repo }} - build_tag: ${{ needs.build.outputs.build_tag }} - skip_integration_tests: ${{ needs.conditionals.outputs.skip_integration_tests }} - cosign_public_key: ${{ needs.build.outputs.cosign_public_key }} + skip_build: "none" + skip_compliance_checks: "all" + skip_unit_tests: "all" + skip_sast: "all" + skip_sca: "none" + skip_docs: "all" + skip_integration_tests: "non-required" + output_type: "table" diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index ddeaab5b6..87f3806c9 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -11,17 +11,10 @@ defaults: shell: bash jobs: - build: - uses: ./.github/workflows/.reusable-build.yml + ci: + uses: ./.github/workflows/.reusable-ci.yml permissions: packages: write - secrets: inherit - with: - build: false - - compliance: - uses: ./.github/workflows/.reusable-compliance.yml - permissions: contents: write id-token: write security-events: write @@ -30,43 +23,20 @@ jobs: deployments: read issues: read discussions: read - packages: read pages: read pull-requests: read repository-projects: read statuses: read secrets: inherit - - sca-released: - name: sca (released) - uses: ./.github/workflows/.reusable-sca.yml - needs: [build] - permissions: - contents: write - security-events: write - packages: read - secrets: inherit with: - image: ${{ needs.build.outputs.original_image }} - output: "table" - - get-root: - name: Build and test get-root - runs-on: ubuntu-latest - defaults: - run: - shell: sh - container: - image: docker:stable - steps: - - name: Checkout code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Build and test get-root utility - run: | - docker build -t get-root-key -f docker/Dockerfile.getRoot . - docker run --rm get-root-key -i securesystemsengineering/testimage > output - cat output | grep "KeyID: 76d211ff8d2317d78ee597dbc43888599d691dbfd073b8226512f0e9848f2508" - cat output | grep "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsx28WV7BsQfnHF1kZmpdCTTLJaWe" + skip_build: "none" + skip_compliance_checks: "none" + skip_unit_tests: "all" + skip_sast: "all" + skip_sca: "none" + skip_docs: "all" + skip_integration_tests: "all" + output_type: "table" cleanup-registry: uses: ./.github/workflows/.reusable-cleanup-registry.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 000000000..6a9b74286 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,41 @@ +name: pr + +permissions: {} + +on: + pull_request: + branches: + - master + - develop + +defaults: + run: + shell: bash + +jobs: + ci: + uses: ./.github/workflows/.reusable-ci.yml + permissions: + packages: write + contents: write + id-token: write + security-events: write + actions: read + checks: read + deployments: read + issues: read + discussions: read + pages: read + pull-requests: read + repository-projects: read + statuses: read + secrets: inherit + with: + skip_build: 'none' + skip_compliance_checks: 'none' + skip_unit_tests: 'none' + skip_sast: 'none' + skip_sca: 'none' + skip_docs: 'none' + skip_integration_tests: 'non-required' + output_type: 'table' diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..7ec3547c6 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,41 @@ +name: push + +permissions: {} + +on: + push: + branches: + - master + - develop + +defaults: + run: + shell: bash + +jobs: + ci: + uses: ./.github/workflows/.reusable-ci.yml + permissions: + packages: write + contents: write + id-token: write + security-events: write + actions: read + checks: read + deployments: read + issues: read + discussions: read + pages: read + pull-requests: read + repository-projects: read + statuses: read + secrets: inherit + with: + skip_build: "none" + skip_compliance_checks: "none" + skip_unit_tests: "none" + skip_sast: "none" + skip_sca: "none" + skip_docs: "none" + skip_integration_tests: "none" + output_type: "table" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9c07357fb..fb749d5df 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,12 +1,12 @@ name: release +permissions: {} + on: push: tags: - "v*" -permissions: {} - defaults: run: shell: bash @@ -90,13 +90,13 @@ jobs: CHART_VERSION="${{ needs.build.outputs.chart_version }}" helm package helm git checkout . # Remove changes to Chart for git checkout - mkdir -p charts - mv connaisseur*.tgz ./charts + mkdir -p tmp_charts + mv connaisseur*.tgz ./tmp_charts git checkout gh-pages - cd charts + cd tmp_charts helm repo index . --url https://sse-secure-systems.github.io/connaisseur/charts cd .. - git add ./charts + git add ./tmp_charts git commit -m "Publish helm chart ${CHART_VERSION}" git push https://${{ secrets.GITHUB_TOKEN }}@github.com/sse-secure-systems/connaisseur.git diff --git a/.gitignore b/.gitignore index 0b4e4f888..9949fd90b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,11 @@ -k8s/deployment.yaml -k8s/webhook.yaml -__pycache__ -bin/* -!bin/docker -src +*.code-workspace +cover.out +.vscode/ +.local/ +connaisseur.log +connaisseur.state +connaisseur.yaml *.crt *.key *.pem *.csr -*.srl -cosign.* -testing-env/aks/k8s/deployment.yaml -.vscode -.pytest_cache -*coverage* -.scannerwork/ -samples/* -demo/ -tls-*.conf -.venv/ -venv/ -.idea/ diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index c06dcf36d..000000000 --- a/.pylintrc +++ /dev/null @@ -1,501 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-whitelist= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -init-hook='' - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=raw-checker-failed, - bad-inline-option, - locally-disabled, - file-ignored, - suppressed-message, - useless-suppression, - deprecated-pragma, - use-symbolic-message-instead, - C0111, - R0903, - W0107, - R1705, - C0200, - R1710, - W0703, - W0511, - W0212, - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=6 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=105 - -# Maximum number of lines in a module. -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - ex, - Run, - _ - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[LOGGING] - -# Format style used to check logging format string. `old` means using % -# formatting, while `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package.. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[STRING] - -# This flag controls whether the implicit-str-concat-in-sequence should -# generate a warning on implicit string concatenation in sequences defined over -# several lines. -check-str-concat-over-line-jumps=no - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[DESIGN] - -# Maximum number of arguments for function / method. -max-args=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Maximum number of boolean expressions in an if statement. -max-bool-expr=5 - -# Maximum number of branch for function / method body. -max-branches=12 - -# Maximum number of locals for function / method body. -max-locals=20 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body. -max-returns=6 - -# Maximum number of statements in function / method body. -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled). -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled). -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception diff --git a/Makefile b/Makefile index 2fd5c8ef7..a8150fca7 100644 --- a/Makefile +++ b/Makefile @@ -1,36 +1,42 @@ NAMESPACE = connaisseur -IMAGE_REPOSITORY := $(shell yq e '.kubernetes.deployment.image.repository' helm/values.yaml) -VERSION := $(shell yq e '.appVersion' helm/Chart.yaml) -COSIGN_VERSION = 2.2.2 +IMAGE_REPO := $(shell yq e '.kubernetes.deployment.image.repository' charts/connaisseur/values.yaml) +VERSION := $(shell yq e '.appVersion' charts/connaisseur/Chart.yaml) +KD := kubernetes.deployment +ALL_RS := all,mutatingwebhookconfigurations,clusterroles,clusterrolebindings,configmaps,secrets,serviceaccounts,crds -.PHONY: all docker install uninstall upgrade annihilate - -all: docker install +.PHONY: docker install uninstall annihilate test install-dev upgrade lint +# meant for local building of docker image docker: - docker build --pull --build-arg COSIGN_VERSION=$(COSIGN_VERSION) -f docker/Dockerfile -t $(IMAGE_REPOSITORY):v$(VERSION) . + docker buildx build --pull -f build/Dockerfile -t $(IMAGE_REPO):v$(VERSION) . install: - # - #============================================= - # - # The installation may last up to 5 minutes. - # - #============================================= - # - helm install connaisseur helm --atomic --create-namespace --namespace $(NAMESPACE) - -dev-install: - helm install --set kubernetes.deployment.replicasCount=1,kubernetes.deployment.imagePullPolicy=Never,application.logLevel=DEBUG connaisseur helm --atomic --create-namespace --namespace $(NAMESPACE) + helm install connaisseur charts/connaisseur --atomic --create-namespace --namespace $(NAMESPACE) $(HELM_ARGS) + +install-dev: + helm install --set $(KD).replicasCount=1,$(KD).imagePullPolicy=Never,application.logLevel=debug \ + connaisseur charts/connaisseur --atomic --create-namespace --namespace $(NAMESPACE) $(HELM_ARGS) uninstall: helm uninstall connaisseur -n $(NAMESPACE) kubectl delete ns $(NAMESPACE) -upgrade: - helm upgrade connaisseur helm -n $(NAMESPACE) --wait - annihilate: - kubectl delete all,mutatingwebhookconfigurations,clusterroles,clusterrolebindings,configmaps,secrets,serviceaccounts,crds -lapp.kubernetes.io/instance=connaisseur -n $(NAMESPACE) + kubectl delete $(ALL_RS) -lapp.kubernetes.io/instance=connaisseur -n $(NAMESPACE) kubectl delete imagepolicies -lapp.kubernetes.io/instance=connaisseur -n $(NAMESPACE) || true kubectl delete ns $(NAMESPACE) + +test: + go test ./... -race -coverprofile=cover.out -covermode=atomic; go tool cover -func cover.out + +upgrade: + helm upgrade connaisseur charts/connaisseur --namespace $(NAMESPACE) --wait + +kind-int-test: + ./tests/integration/run_integration_tests.sh -c kind -r "regular cosign" + +kind-dev: + make docker && kind load docker-image $(IMAGE_REPO):v$(VERSION) && make install-dev + +lint: + golangci-lint run --skip-dirs="test" diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 000000000..d110d1120 --- /dev/null +++ b/build/Dockerfile @@ -0,0 +1,26 @@ +FROM golang:1.21-alpine AS build + +WORKDIR /go/src + +COPY go.mod go.sum ./ +ENV CGO_ENABLED=0 +ENV GOMODCACHE=/root/.cache/go-build +RUN --mount=type=cache,target=/root/.cache/go-build go mod tidy && \ + go mod download -x + +COPY . ./ +RUN --mount=type=cache,target=/root/.cache/go-build \ + go build -o /go/bin ./cmd/connaisseur + +FROM alpine:3 as certs +RUN apk --update --no-cache add ca-certificates=20230506-r0 + +FROM scratch + +WORKDIR / +COPY --from=build /go/bin/connaisseur /app/bin/connaisseur +COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +USER 10001:20001 + +ENTRYPOINT ["/app/bin/connaisseur"] diff --git a/helm/.helmignore b/charts/connaisseur/.helmignore similarity index 97% rename from helm/.helmignore rename to charts/connaisseur/.helmignore index 50af03172..0e8a0eb36 100644 --- a/helm/.helmignore +++ b/charts/connaisseur/.helmignore @@ -14,6 +14,7 @@ *.swp *.bak *.tmp +*.orig *~ # Various IDEs .project diff --git a/helm/Chart.yaml b/charts/connaisseur/Chart.yaml similarity index 72% rename from helm/Chart.yaml rename to charts/connaisseur/Chart.yaml index 06e614092..d0d7c0e1d 100644 --- a/helm/Chart.yaml +++ b/charts/connaisseur/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: connaisseur description: Helm chart for Connaisseur - a Kubernetes admission controller to integrate container image signature verification and trust pinning into a cluster. type: application -version: 2.3.4 -appVersion: 3.3.4 +version: 2.4.0 +appVersion: 3.4.0 keywords: - container image - signature @@ -17,5 +17,9 @@ maintainers: email: philipp.belitz@securesystems.de - name: Christoph Hamsen email: christoph.hamsen@securesystems.de - - name: Peter Thomassen - email: peter.thomassen@securesystems.de + - name: Teetje Stark + email: teetje.stark@securesystems.de + - name: Anneke Breust + email: anneke.breust@securesystems.de + - name: Christopher Filsinger + email: christopher.filsinger@securesystems.de diff --git a/helm/alert_payload_templates/ecs-1-12-0.json b/charts/connaisseur/alert_payload_templates/ecs-1-12-0.json similarity index 100% rename from helm/alert_payload_templates/ecs-1-12-0.json rename to charts/connaisseur/alert_payload_templates/ecs-1-12-0.json diff --git a/helm/alert_payload_templates/keybase.json b/charts/connaisseur/alert_payload_templates/keybase.json similarity index 100% rename from helm/alert_payload_templates/keybase.json rename to charts/connaisseur/alert_payload_templates/keybase.json diff --git a/helm/alert_payload_templates/msteams.json b/charts/connaisseur/alert_payload_templates/msteams.json similarity index 100% rename from helm/alert_payload_templates/msteams.json rename to charts/connaisseur/alert_payload_templates/msteams.json diff --git a/helm/alert_payload_templates/opsgenie.json b/charts/connaisseur/alert_payload_templates/opsgenie.json similarity index 100% rename from helm/alert_payload_templates/opsgenie.json rename to charts/connaisseur/alert_payload_templates/opsgenie.json diff --git a/helm/alert_payload_templates/slack.json b/charts/connaisseur/alert_payload_templates/slack.json similarity index 100% rename from helm/alert_payload_templates/slack.json rename to charts/connaisseur/alert_payload_templates/slack.json diff --git a/charts/connaisseur/templates/_helpers.tpl b/charts/connaisseur/templates/_helpers.tpl new file mode 100644 index 000000000..3600be7a9 --- /dev/null +++ b/charts/connaisseur/templates/_helpers.tpl @@ -0,0 +1,372 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "connaisseur.name" -}} +{{- .Chart.Name | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "connaisseur.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "connaisseur.labels" -}} +helm.sh/chart: {{ include "connaisseur.chart" . }} +{{ include "connaisseur.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- if .Values.kubernetes.additionalLabels }} +{{ toYaml .Values.kubernetes.additionalLabels }} +{{- end -}} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "connaisseur.selectorLabels" -}} +app.kubernetes.io/name: {{ include "connaisseur.name" . }} +app.kubernetes.io/instance: {{ .Chart.Name }} +{{- end }} + +{{/* +Selector labels for redis +*/}} +{{- define "connaisseur.redisSelectorLabels" -}} +app.kubernetes.io/name: {{ include "connaisseur.name" . }} +app.kubernetes.io/instance: redis +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "connaisseur.serviceName" -}} +{{- include "connaisseur.name" . }}-svc +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "connaisseur.serviceAccountName" -}} +{{- include "connaisseur.name" . }}-serviceaccount +{{- end }} + +{{/* +Create the name of the TLS secrets to use +*/}} +{{- define "connaisseur.TLSName" -}} +{{- include "connaisseur.name" . }}-tls +{{- end }} + +{{/* +Create the name of the webhook to use +*/}} +{{- define "connaisseur.webhookName" -}} +{{- include "connaisseur.name" . }}-webhook +{{- end }} + +{{/* +Create the name of the application configuration to use +*/}} +{{- define "connaisseur.appConfigName" -}} +{{- include "connaisseur.name" . }}-app-config +{{- end }} + +{{/* +Create the name of the enviroment variables to use +*/}} +{{- define "connaisseur.envName" -}} +{{- include "connaisseur.name" . }}-env +{{- end }} + +{{/* +Create the name of the environment variable secrets to use +*/}} +{{- define "connaisseur.envSecretName" -}} +{{- include "connaisseur.name" . }}-env-secret +{{- end }} + +{{/* +Create the name of the role to use +*/}} +{{- define "connaisseur.roleName" -}} +{{- include "connaisseur.name" . }}-role +{{- end }} + +{{/* +Create the name of the role binding to use +*/}} +{{- define "connaisseur.roleBindingName" -}} +{{- include "connaisseur.name" . }}-role-binding +{{- end }} + +{{/* +Create the name of the cluster role to use +*/}} +{{- define "connaisseur.clusterRoleName" -}} +{{- include "connaisseur.name" . }}-cluster-role +{{- end }} + +{{/* +Create the name of the cluster role binding to use +*/}} +{{- define "connaisseur.clusterRoleBindingName" -}} +{{- include "connaisseur.name" . }}-cluster-role-binding +{{- end }} + +{{/* +Create the name of the alerting templates +*/}} +{{- define "connaisseur.alertTemplatesName" -}} +{{- include "connaisseur.name" . }}-alert-templates +{{- end -}} + +{{/* +Create the name of the alerting configuration +*/}} +{{- define "connaisseur.alertConfigName" -}} +{{- include "connaisseur.name" . -}}-alert-config +{{- end -}} + +{{/* +Create the name of the redis depoyment +*/}} +{{- define "connaisseur.redisName" -}} +{{- include "connaisseur.name" . }}-redis +{{- end -}} + +{{/* +Create the name of the redis service +*/}} +{{- define "connaisseur.redisService" -}} +{{- include "connaisseur.name" . }}-redis-service +{{- end -}} + +{{/* +Create the name of the redis secret (password) +*/}} +{{- define "connaisseur.redisSecret" -}} +{{- include "connaisseur.name" . }}-redis-secret +{{- end -}} + +{{/* +Create the name of the redis tls secret +*/}} +{{- define "connaisseur.redisTLS" -}} +{{- include "connaisseur.name" . }}-redis-tls +{{- end -}} + +{{/* +Extract Kubernetes Minor Version. +*/}} +{{- define "connaisseur.k8s-version-minor" -}} +{{- trimSuffix "." (trimPrefix "v1." (regexFind "v\\d\\.\\d{1,2}\\." .Capabilities.KubeVersion.Version)) -}} {* TODO: not future safe *} +{{- end -}} + + +{{/* +Name of the connaisseur image +*/}} +{{- define "connaisseur.image" -}} + {{ .Values.kubernetes.deployment.image.repository }}:{{ default (print "v" .Chart.AppVersion) .Values.kubernetes.deployment.image.tag }} +{{- end -}} + +{{/* +Name of the namespace selector key +*/}} +{{- define "conaisseur.namespaceSelectorKey" -}} +securesystemsengineering.connaisseur/webhook +{{- end -}} + + +{{/* +Collect the names of all authentication secrets +*/}} +{{- define "connaisseur.validatorSecrets" -}} +{{- $secrets := list -}} +{{- range .Values.application.validators -}} + {{- if hasKey . "auth" -}} + {{- if hasKey .auth "secretName" -}} + {{- $secrets = append $secrets .auth.secretName -}} + {{- else if and (hasKey .auth "username") (hasKey .auth "password") (or (eq .type "notaryv1") (hasKey .auth "registry")) -}} + {{- $secrets = append $secrets (print .name "-auth") -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- $dict := dict "list" $secrets -}} +{{ $dict | toYaml }} +{{- end -}} + +{{/* +Volume definitions for all authentication secrets +*/}} +{{- define "connaisseur.validatorSecretVolumes" -}} +{{- $secrets := ((include "connaisseur.validatorSecrets" .) | fromYaml ) -}} +{{- range (get $secrets "list") }} +- name: {{ . }}-volume + secret: + secretName: {{ . }} +{{- end -}} +{{- end -}} + +{{/* +Volume mounts for all authentication secrets +*/}} +{{- define "connaisseur.validatorSecretMounts" -}} +{{- $secrets := ((include "connaisseur.validatorSecrets" .) | fromYaml ) -}} +{{- range (get $secrets "list" ) }} +- name: {{ . }}-volume + mountPath: /app/secrets/{{ . }} + readOnly: True +{{- end -}} +{{- end -}} + +{{/* +Render all configuration files +*/}} +{{- define "connaisseur.getConfigFiles" -}} +{{ include (print $.Template.BasePath "/configmaps.yaml") . }} +{{ include (print $.Template.BasePath "/env.yaml") . }} +{{ include (print $.Template.BasePath "/secrets.yaml") . }} +{{- end -}} + +{{/* +Get checksum of all configuration files. To be used for the deployment as annotation. +Should any configuration change, that the deployment must reload, the checksum +will change, cause the deployment to be redeployed. +*/}} +{{- define "connaisseur.getConfigChecksum" -}} +{{- $configs := list (include "connaisseur.getConfigFiles" . | sha256sum) -}} + +{{- if hasKey .Values.kubernetes.deployment "tls" | and (not (empty .Values.kubernetes.deployment.tls )) -}} + {{- $configs = append $configs (print .Values.kubernetes.deployment.tls | sha256sum) -}} +{{- end -}} +{{- if hasKey .Values.kubernetes.redis "tls" | and (not (empty .Values.kubernetes.redis.tls )) -}} + {{- $configs = append $configs (print .Values.kubernetes.redis.tls | sha256sum) -}} +{{- end -}} + +{{ join "\n" $configs | sha256sum }} +{{- end -}} + +{{/* +Volume definitions for all alerts +*/}} +{{- define "connaisseur.alertVolumes" -}} +- name: {{ include "connaisseur.alertConfigName" . }} + configMap: + name: {{ include "connaisseur.alertConfigName" . }} +{{- if .Values.alerting }} +- name: {{ include "connaisseur.alertTemplatesName" . }} + configMap: + name: {{ include "connaisseur.alertTemplatesName" . }} +{{- end -}} +{{- end -}} + +{{/* +Volume mounts for all alerts +*/}} +{{- define "connaisseur.alertMounts" -}} +- name: {{ include "connaisseur.alertConfigName" . }} + mountPath: /app/alerts/config.yaml + readOnly: true + subPath: config.yaml +{{- if .Values.alerting }} +- name: {{ include "connaisseur.alertTemplatesName" . }} + mountPath: /app/alerts/templates + readOnly: true +{{- end -}} +{{- end -}} + +{{/* +Will make a call to the Kubernetes API to get the value of a secret. +Expects a dictionary as input with the following keys: +- name: name of the secret +- key: key of the value to get +- namespace: namespace of the secret +*/}} +{{- define "connaisseur.LookUpSecret" -}} +{{- $data := (lookup "v1" "Secret" .namespace .name).data -}} +{{- if $data -}} + {{ get $data .key }} +{{- end -}} +{{- end -}} + +{{/* +Will look for an already installed redis password secret and return it. +If there is no such secret, it will generate a new password and return it. +*/}} +{{- define "connaisseur.redisPassword" -}} +{{- $args := dict "name" (include "connaisseur.redisSecret" .) "key" "REDIS_PASSWORD" "namespace" .Release.Namespace -}} +{{- $pw := (include "connaisseur.LookUpSecret" $args) | default (uuidv4 | b64enc) -}} +{{ $pw }} +{{- end -}} + +{{/* +Set up certificate and private key to use for TLS communication: +If there's a configured one in the values.yaml, use that to allow rotation +Otherwise, if there's an existing installation re-use the previous certificate +Otherwise, generate a new self-signed certificate + +Expects a dictionary as input with the following keys: +- deployment: the dict of the deployment, which should include a tls section (e.g. .Values.kubernetes.deployment or .Values.kubernetes.redis) +- tlsName: the name of the secret to potentially reuse +- svc: the name of the service +- namespace: the namespace of the service + +Returns a dictionary in yaml format with the following keys: +- cert: the certificate +- key: the private key +*/}} +{{- define "connaisseur.tlsCertificate" -}} +{{- include "connaisseur.validateTLSConfig" .deployment -}} + +{{- $altNames := list -}} +{{- $altNames = append $altNames (printf "%s" .svc) -}} +{{- $altNames = append $altNames (printf "%s.%s" .svc .namespace) -}} +{{- $altNames = append $altNames (printf "%s.%s.svc" .svc .namespace) -}} +{{- $altNames = append $altNames (printf "%s.%s.svc.cluster.local" .svc .namespace) -}} +{{- $newCertificate := genSelfSignedCert (printf "%s.%s.svc" .svc .namespace) nil $altNames 36500 -}} + +{{- $certArgs := dict "name" .tlsName "namespace" .namespace "key" "tls.crt" -}} +{{- $keyArgs := dict "name" .tlsName "namespace" .namespace "key" "tls.key" -}} +{{- $installedCert := include "connaisseur.LookUpSecret" $certArgs -}} +{{- $installedKey := include "connaisseur.LookUpSecret" $keyArgs -}} +{{ $encodedTLSCert := default ($newCertificate.Cert | b64enc) ($installedCert) }} +{{ $encodedTLSKey := default ($newCertificate.Key | b64enc) ($installedKey) }} + +{{- if hasKey .deployment "tls" -}} + {{- if hasKey .deployment.tls "key" -}} + {{- $certByHelmConfig := buildCustomCert (.deployment.tls.cert | b64enc) (.deployment.tls.key | b64enc) -}} + {{- $encodedTLSCert = $certByHelmConfig.Cert | b64enc -}} + {{- $encodedTLSKey = $certByHelmConfig.Key | b64enc -}} + {{- end -}} +{{- end -}} + +{{- $return := dict "cert" $encodedTLSCert "key" $encodedTLSKey -}} +{{ $return | toYaml}} +{{- end -}} + +{{/* +Validates the TLS configuration of a deployment. +If configured, the deployment must have both a cert and a key. + +Expects a dictionary as input with the following keys: +- deployment: the dict of the deployment, which should include a tls section (e.g. .Values.kubernetes.deployment or .Values.kubernetes.redis) +*/}} +{{- define "connaisseur.validateTLSConfig" -}} +# input: deployment, e.g. .Values.kubernetes.deployment or .Values.kubernetes.redis +{{- if hasKey . "tls" -}} + {{- if and (not (hasKey .tls "cert")) (hasKey .tls "key")}} + {{ fail "Helm configuration has a 'tls' section with a 'key' attribute, but is missing the 'cert' attribute." -}} + {{- end -}} + {{- if and (not (hasKey .tls "key")) (hasKey .tls "cert")}} + {{ fail "Helm configuration has a 'tls' section with a 'cert' attribute, but is missing the 'key' attribute." -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/connaisseur/templates/configmaps.yaml b/charts/connaisseur/templates/configmaps.yaml new file mode 100644 index 000000000..4aa7ac637 --- /dev/null +++ b/charts/connaisseur/templates/configmaps.yaml @@ -0,0 +1,46 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "connaisseur.appConfigName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +data: + config.yaml: | + validators: + {{- range $k,$v := .Values.application.validators }} + {{- $validator := deepCopy $v }} + {{- if and (hasKey $validator "auth") (and (hasKey $validator.auth "username") (hasKey $validator.auth "password")) }} + {{- $auth := dict "secretName" (print $validator.name "-auth") }} + {{- $_ := unset $validator "auth" }} + {{- $_ := set $validator "auth" $auth }} + {{- end }} + - {{- $validator | toYaml | trim | nindent 6 -}} + {{- end }} + policy: + {{- range $k,$v := .Values.application.policy }} + - {{- $v | toYaml | trim | nindent 6 -}} + {{- end }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "connaisseur.alertConfigName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +data: + config.yaml: | + {{- default (dict) .Values.alerting | toYaml | nindent 6 }} +{{- if .Values.alerting }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "connaisseur.alertTemplatesName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +data: + {{- (.Files.Glob "alert_payload_templates/*").AsConfig | nindent 2 }} +{{- end -}} diff --git a/charts/connaisseur/templates/deployment.yaml b/charts/connaisseur/templates/deployment.yaml new file mode 100644 index 000000000..e43ca1da9 --- /dev/null +++ b/charts/connaisseur/templates/deployment.yaml @@ -0,0 +1,100 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "connaisseur.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} + annotations: + checksum/config: {{ include "connaisseur.getConfigChecksum" . }} +spec: + replicas: {{ .Values.kubernetes.deployment.replicasCount }} + selector: + matchLabels: + {{- include "connaisseur.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "connaisseur.selectorLabels" . | nindent 8 }} + annotations: + checksum/config: {{ include "connaisseur.getConfigChecksum" . }} + spec: + serviceAccountName: {{ include "connaisseur.serviceAccountName" . }} + {{- with .Values.kubernetes.deployment.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + image: {{ include "connaisseur.image" . }} + imagePullPolicy: {{ .Values.kubernetes.deployment.pullPolicy }} + ports: + - name: https + containerPort: 5000 + protocol: TCP + livenessProbe: + httpGet: + path: /health + port: https + scheme: HTTPS + readinessProbe: + httpGet: + path: /ready + port: https + scheme: HTTPS + startupProbe: + httpGet: + path: /start + port: https + scheme: HTTPS + periodSeconds: 5 + failureThreshold: 30 + securityContext: + {{- toYaml .Values.kubernetes.deployment.securityContext | nindent 12 }} + resources: + {{- toYaml .Values.kubernetes.deployment.resources | nindent 12 }} + volumeMounts: + - name: certs + mountPath: /app/certs + readOnly: true + - name: app-config + mountPath: /app/config + readOnly: true + - name: redis-certs + mountPath: /app/redis-certs/tls.crt + readOnly: true + subPath: tls.crt + {{- include "connaisseur.alertMounts" . | nindent 12 }} + {{- include "connaisseur.validatorSecretMounts" . | nindent 12 }} + envFrom: + - configMapRef: + name: {{ include "connaisseur.envName" . }} + - secretRef: + name: {{ include "connaisseur.redisSecret" . }} + env: + - name: REDIS_HOST + value: {{ include "connaisseur.redisService" . }} + {{ if .Values.kubernetes.deployment.envs -}} + - secretRef: + name: {{ include "connaisseur.envSecretName" . }} + {{- end }} + volumes: + - name: certs + secret: + secretName: {{ include "connaisseur.TLSName" . }} + - name: redis-certs + secret: + secretName: {{ include "connaisseur.redisTLS" . }} + - name: app-config + configMap: + name: {{ include "connaisseur.appConfigName" . -}} + {{- include "connaisseur.alertVolumes" . | nindent 8 }} + {{- include "connaisseur.validatorSecretVolumes" . | nindent 8 }} + affinity: + {{- toYaml .Values.kubernetes.deployment.affinity | nindent 8 }} + tolerations: + {{- toYaml .Values.kubernetes.deployment.tolerations | nindent 8 }} + nodeSelector: + {{- toYaml .Values.kubernetes.deployment.nodeSelector | nindent 8 }} + securityContext: + {{- toYaml .Values.kubernetes.deployment.podSecurityContext | nindent 8 }} diff --git a/charts/connaisseur/templates/env.yaml b/charts/connaisseur/templates/env.yaml new file mode 100644 index 000000000..163d3f0c9 --- /dev/null +++ b/charts/connaisseur/templates/env.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "connaisseur.envName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +data: + {{- with .Values.application.features }} + AUTOMATIC_CHILD_APPROVAL: {{ print .automaticChildApproval | default "true" | quote }} + AUTOMATIC_UNCHANGED_APPROVAL: {{ print .automaticUnchangedApproval | default "false" | quote }} + DETECTION_MODE: {{ print .detectionMode | default "false" | quote }} + RESOURCE_VALIDATION_MODE: {{ print .resourceValidationMode | default "all" | quote }} + {{- end }} + LOG_LEVEL: {{ .Values.application.logLevel | default "info" | quote }} +--- +{{- if .Values.kubernetes.deployment.envs -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "connaisseur.envSecretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +type: Opaque +data: + {{- range $k,$v := .Values.kubernetes.deployment.envs }} + {{ $k }}: {{ $v | b64enc }} + {{- end -}} +{{- end -}} diff --git a/charts/connaisseur/templates/redis.yaml b/charts/connaisseur/templates/redis.yaml new file mode 100644 index 000000000..38b4caeef --- /dev/null +++ b/charts/connaisseur/templates/redis.yaml @@ -0,0 +1,135 @@ +{{- $svc := (include "connaisseur.redisService" .) -}} +{{- $tlsName := (include "connaisseur.redisTLS" .) -}} +{{- $input := dict "deployment" .Values.kubernetes.redis "tlsName" $tlsName "svc" $svc "namespace" .Release.Namespace -}} +{{ $tls := (include "connaisseur.tlsCertificate" $input) | fromYaml }} + +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "connaisseur.redisSecret" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +type: Opaque +data: + REDIS_PASSWORD: {{ include "connaisseur.redisPassword" . }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "connaisseur.redisTLS" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +type: Opaque +data: + tls.crt: {{ $tls.cert }} + tls.key: {{ $tls.key }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "connaisseur.redisName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} + annotations: + checksum/config: {{ include "connaisseur.getConfigChecksum" . }} +spec: + replicas: 1 + selector: + matchLabels: + {{- include "connaisseur.redisSelectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "connaisseur.redisSelectorLabels" . | nindent 8 }} + annotations: + checksum/config: {{ include "connaisseur.getConfigChecksum" . }} + spec: + automountServiceAccountToken: false + containers: + - name: redis + image: redis:7 + imagePullPolicy: {{ .Values.kubernetes.redis.pullPolicy }} + args: + - --requirepass + - $(REDIS_PASSWORD) + - --tls-cert-file + - /cfg/certs/tls.crt + - --tls-key-file + - /cfg/certs/tls.key + - --tls-auth-clients + - "no" + - --tls-port + - "6379" + - --port + - "6380" + {{ if eq (lower .Values.application.logLevel) "debug" -}} + - --loglevel + - "debug" + {{- else if eq (lower .Values.application.logLevel) "error" -}} + - --loglevel + - "warning" + {{- else -}} + - --loglevel + - "notice" + {{- end }} + - --rename-command + - FLUSHALL + - "" + - --rename-command + - FLUSHDB + - "" + ports: + - containerPort: 6379 + name: redis + protocol: TCP + livenessProbe: + exec: + command: + - redis-cli + - -p + - "6380" + - ping + readinessProbe: + exec: + command: + - redis-cli + - -p + - "6380" + - ping + volumeMounts: + - name: certs + mountPath: /cfg/certs + readOnly: true + envFrom: + - secretRef: + name: {{ include "connaisseur.redisSecret" . }} + resources: + {{- toYaml .Values.kubernetes.redis.resources | nindent 12 }} + securityContext: + {{- toYaml .Values.kubernetes.redis.securityContext | nindent 12 }} + securityContext: + {{- toYaml .Values.kubernetes.redis.podSecurityContext | nindent 8 }} + volumes: + - name: certs + secret: + secretName: {{ include "connaisseur.redisTLS" . }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "connaisseur.redisService" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - port: 6379 + targetPort: 6379 + protocol: TCP + name: http + selector: + {{- include "connaisseur.redisSelectorLabels" . | nindent 4 }} diff --git a/charts/connaisseur/templates/role.yaml b/charts/connaisseur/templates/role.yaml new file mode 100644 index 000000000..5e834fe56 --- /dev/null +++ b/charts/connaisseur/templates/role.yaml @@ -0,0 +1,31 @@ +{{ if print .Values.application.features.automaticChildApproval | default "true" | lower | eq "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "connaisseur.roleName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "connaisseur.clusterRoleName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +rules: +- apiGroups: ["apps"] + resources: ["deployments", "replicasets", "daemonsets", "statefulsets"] + verbs: ["get"] +- apiGroups: [""] + resources: ["pods", "replicationcontrollers"] + verbs: ["get"] +- apiGroups: ["batch"] + resources: ["jobs", "cronjobs"] + verbs: ["get"] +{{ end }} diff --git a/charts/connaisseur/templates/rolebinding.yaml b/charts/connaisseur/templates/rolebinding.yaml new file mode 100644 index 000000000..f464c2212 --- /dev/null +++ b/charts/connaisseur/templates/rolebinding.yaml @@ -0,0 +1,33 @@ +{{ if print .Values.application.features.automaticChildApproval | default "true" | lower | eq "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "connaisseur.roleBindingName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +subjects: +- kind: ServiceAccount + name: {{ include "connaisseur.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ include "connaisseur.roleName" . }} + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "connaisseur.clusterRoleBindingName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +subjects: +- kind: ServiceAccount + name: {{ include "connaisseur.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ include "connaisseur.clusterRoleName" . }} + apiGroup: rbac.authorization.k8s.io +{{ end }} diff --git a/charts/connaisseur/templates/secrets.yaml b/charts/connaisseur/templates/secrets.yaml new file mode 100644 index 000000000..08e13e7fd --- /dev/null +++ b/charts/connaisseur/templates/secrets.yaml @@ -0,0 +1,16 @@ +{{ $root := . -}} +{{ range .Values.application.validators -}} +{{ if and (hasKey . "auth") (and (hasKey .auth "username") (hasKey .auth "password") (or (eq .type "notaryv1") (hasKey .auth "registry"))) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .name }}-auth + namespace: {{ $root.Release.Namespace }} + labels: + {{- include "connaisseur.labels" $root | nindent 4 }} +type: Opaque +data: + secret.yaml: {{ .auth | toYaml | b64enc }} +--- +{{- end }} +{{- end }} diff --git a/helm/templates/service.yaml b/charts/connaisseur/templates/service.yaml similarity index 51% rename from helm/templates/service.yaml rename to charts/connaisseur/templates/service.yaml index 438feb4c6..b2ebeaf26 100644 --- a/helm/templates/service.yaml +++ b/charts/connaisseur/templates/service.yaml @@ -1,17 +1,16 @@ apiVersion: v1 kind: Service metadata: - name: {{ .Chart.Name }}-svc + name: {{ include "connaisseur.serviceName" . }} namespace: {{ .Release.Namespace }} labels: - app.kubernetes.io/component: connaisseur-core - {{- include "helm.labels" . | nindent 4 }} + {{- include "connaisseur.labels" . | nindent 4 }} spec: type: {{ .Values.kubernetes.service.type }} ports: - port: {{ .Values.kubernetes.service.port }} targetPort: 5000 + protocol: TCP name: http selector: - app.kubernetes.io/name: {{ include "helm.name" . }} - app.kubernetes.io/instance: {{ .Chart.Name }} + {{- include "connaisseur.selectorLabels" . | nindent 4 }} diff --git a/charts/connaisseur/templates/serviceaccount.yaml b/charts/connaisseur/templates/serviceaccount.yaml new file mode 100644 index 000000000..fc0574b3a --- /dev/null +++ b/charts/connaisseur/templates/serviceaccount.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "connaisseur.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} + {{- if .Values.kubernetes.serviceaccount.annotations }} + annotations: + {{- toYaml .Values.kubernetes.serviceaccount.annotations | nindent 4 }} + {{- end }} diff --git a/charts/connaisseur/templates/webhook+certificate.yaml b/charts/connaisseur/templates/webhook+certificate.yaml new file mode 100644 index 000000000..b8796eb74 --- /dev/null +++ b/charts/connaisseur/templates/webhook+certificate.yaml @@ -0,0 +1,90 @@ +{{- $svc := (include "connaisseur.serviceName" .) -}} +{{- $tlsName := (include "connaisseur.TLSName" .) -}} +{{- $input := dict "deployment" .Values.kubernetes.deployment "tlsName" $tlsName "svc" $svc "namespace" .Release.Namespace -}} +{{ $tls := (include "connaisseur.tlsCertificate" $input) | fromYaml }} + +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "connaisseur.TLSName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} +type: Opaque +data: + tls.crt: {{ $tls.cert }} + tls.key: {{ $tls.key }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ include "connaisseur.webhookName" . }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-delete + "helm.sh/hook-delete-policy": before-hook-creation, hook-succeeded, hook-failed +webhooks: + - name: {{ .Chart.Name }}-svc.{{ .Release.Namespace }}.svc + failurePolicy: Ignore + reinvocationPolicy: Never + clientConfig: + service: + name: {{ $svc }} + namespace: {{ .Release.Namespace }} + path: /mutate + caBundle: {{ $tls.cert }} + rules: [] + sideEffects: None + admissionReviewVersions: ["v1"] +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ include "connaisseur.webhookName" . }} + labels: + {{- include "connaisseur.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-install, post-upgrade, post-rollback +webhooks: + - name: {{ .Chart.Name }}-svc.{{ .Release.Namespace }}.svc + failurePolicy: {{ .Values.kubernetes.webhook.failurePolicy | default "Fail" }} + reinvocationPolicy: {{ .Values.kubernetes.webhook.reinvocationPolicy | default "Never" }} + clientConfig: + service: + name: {{ $svc }} + namespace: {{ .Release.Namespace }} + path: /mutate + caBundle: {{ $tls.cert }} + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments", "replicasets", "daemonsets", "statefulsets"] + - operations: ["CREATE", "UPDATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods", "pods/ephemeralcontainers", "replicationcontrollers"] + - operations: ["CREATE", "UPDATE"] + apiGroups: ["batch"] + apiVersions: ["v1"] + resources: ["jobs", "cronjobs"] + sideEffects: None + timeoutSeconds: 30 + admissionReviewVersions: ["v1"] + {{- with .Values.application.features }} + {{- if .namespacedValidation }} + {{- $operator := "NotIn" }} + {{- $value := "ignore" }} + {{- if and .namespacedValidation.mode (eq .namespacedValidation.mode "validate") }} + {{- $operator = "In" }} + {{- $value = "validate" }} + {{- end }} + namespaceSelector: + matchExpressions: + - key: {{ include "conaisseur.namespaceSelectorKey" . }} + operator: {{ $operator }} + values: + - {{ $value }} + {{- end }} + {{- end }} diff --git a/charts/connaisseur/values.yaml b/charts/connaisseur/values.yaml new file mode 100644 index 000000000..08d7e472d --- /dev/null +++ b/charts/connaisseur/values.yaml @@ -0,0 +1,186 @@ +kubernetes: + # changes to connaisseur deployment file + deployment: + image: + repository: docker.io/securesystemsengineering/connaisseur + # Running a different version than the version in the Chart.yaml may not work as the + # Connaisseur application version depends on the layout of the Helm deployment + # Thus, if unset, we default to the appVersion of the Chart.yaml + # tag: v1.2.3 + pullPolicy: Always + replicasCount: 3 + imagePullSecrets: [] + nodeSelector: {} + tolerations: [] + resources: + limits: + cpu: 1000m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 10001 # remove when using openshift or OKD 4 + runAsGroup: 20001 # remove when using openshift or OKD 4 + seccompProfile: # remove when using Kubernetes prior v1.19, openshift or OKD 4 + type: RuntimeDefault # remove when using Kubernetes prior v1.19, openshift or OKD 4 + podSecurityContext: {} + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: app.kubernetes.io/instance + operator: In + values: + - connaisseur + topologyKey: kubernetes.io/hostname + weight: 100 + tls: {} # set to use custom tls certificates for webhook (key and cert fields) + envs: {} # set to use custom environment variables for connaisseur + # ----------------------------------------------------- + + # changes to connaisseur service + service: + type: ClusterIP + port: 443 + # ----------------------------------------------------- + + # changes to connaisseur service account + serviceaccount: {} + # annotations: + # eks.amazonaws.com/role-arn: arn:aws:iam::XXX:role/myrole + + # ----------------------------------------------------- + + # changes to connaisseur admission controller + webhook: + failurePolicy: Fail + reinvocationPolicy: Never + # ----------------------------------------------------- + + # changes to redis key-value store + redis: + pullPolicy: Always + resources: + limits: + cpu: 1000m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 10001 # remove when using openshift or OKD 4 + runAsGroup: 20001 # remove when using openshift or OKD 4 + seccompProfile: # remove when using Kubernetes prior v1.19, openshift or OKD 4 + type: RuntimeDefault # remove when using Kubernetes prior v1.19, openshift or OKD 4 + podSecurityContext: {} + tls: {} # set to use custom tls certificates for redis +# ----------------------------------------------------- + + # additional labels for connaisseur resources + additionalLabels: {} + +# ----------------------------------------------------- + +# changes to connaisseur application logic +application: + # validator options: https://sse-secure-systems.github.io/connaisseur/latest/validators/ + validators: + - name: allow + type: static + approve: true + - name: deny + type: static + approve: false + - name: dockerhub + type: notaryv1 + trustRoots: + - name: default # root from dockerhub + key: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOXYta5TgdCwXTCnLU09W5T4M4r9f + QQrqJuADP6U7g5r9ICgPSmZuRHP/1AYUfOQW3baveKsT969EfELKj1lfCA== + -----END PUBLIC KEY----- + - name: sse # root from sse + key: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsx28WV7BsQfnHF1kZmpdCTTLJaWe + d0CA+JOi8H4REuBaWSZ5zPDe468WuOJ6f71E7WFg3CVEVYHuoZt2UYbN/Q== + -----END PUBLIC KEY----- + + # policy options: https://sse-secure-systems.github.io/connaisseur/latest/basics/#configuration-options_2 + # explanation of policy patterns: https://sse-secure-systems.github.io/connaisseur/latest/basics/#image-policy + policy: + - pattern: "*:*" + validator: deny + - pattern: "docker.io/library/*:*" + validator: dockerhub + - pattern: "docker.io/securesystemsengineering/*:*" + validator: dockerhub + with: + trustRoot: sse + - pattern: "registry.k8s.io/*:*" + validator: allow + + # feature descriptions: https://sse-secure-systems.github.io/connaisseur/latest/features/ + features: + # automatic approval of resources that are updated, without changing the image reference + automaticUnchangedApproval: false + # automatic approval of child resources (e.g. pod created from a deployment) + automaticChildApproval: true + # mode to never deny any resources, only warn should any errors occur during validation + detectionMode: false + # ignore or validate only in specific namespaces, according to namespace annotations + namespacedValidation: + mode: ignore # 'ignore' or 'validate' + # validate all resources or only pods (and warn on others) + resourceValidationMode: "all" # 'all' or 'podsOnly' + + logLevel: info + +# alerting options: https://sse-secure-systems.github.io/connaisseur/latest/features/alerting/#configuration-options +# alerting: +# clusterIdentifier: example-cluster-staging-europe # defaults to "not specified" +# admitRequest: +# receivers: +# #