├── .ci ├── build ├── pipeline_definitions ├── prepare_release ├── test └── verify ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── enhancement_request.md ├── dependabot.yaml └── pull_request_template.md ├── .gitignore ├── .golangci.yaml ├── .idea └── copyright │ ├── Gardener.xml │ └── profiles_settings.xml ├── .reuse └── dep5 ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── LICENSES ├── Apache-2.0.txt └── CC-BY-4.0.txt ├── Makefile ├── OWNERS ├── OWNERS_ALIASES ├── README.md ├── VERSION ├── charts └── cert-management │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ ├── 0helpers.tpl │ ├── ca-certificates-configmap.yaml │ ├── cert.gardener.cloud_certificaterevocations.yaml │ ├── cert.gardener.cloud_certificates.yaml │ ├── cert.gardener.cloud_issuers.yaml │ ├── clusterrole.yaml │ ├── clusterrolebinding.yaml │ ├── deployment.yaml │ ├── role.yaml │ ├── rolebinding.yaml │ └── serviceaccount.yaml │ └── values.yaml ├── cmd ├── cert-controller-manager │ ├── .import-restrictions │ └── main.go └── certman2 │ ├── .import-restrictions │ ├── app │ └── app.go │ └── main.go ├── docs ├── development │ ├── getting-started.md │ └── testing.md └── usage │ └── tutorials │ ├── gateway-api-gateways.md │ └── istio-gateways.md ├── examples ├── 10-crds.yaml ├── 11-dns.gardener.cloud_dnsentries.yaml ├── 20-issuer-ca.yaml ├── 20-issuer-productive.yaml ├── 20-issuer-selfsigned.yaml ├── 20-issuer-staging.yaml ├── 21-issuer-acme-eab.yaml ├── 30-cert-ca.yaml ├── 30-cert-csr.yaml ├── 30-cert-selfsigned.yaml ├── 30-cert-simple-with-keystores.yaml ├── 30-cert-simple.yaml ├── 30-cert-wildcard.yaml ├── 40-gateway-gateway-api.yaml ├── 40-gateway-istio.yaml ├── 40-ingress-echoheaders.yaml ├── 40-service-loadbalancer.yaml └── 50-certificate-revocation.yaml ├── go.mod ├── go.sum ├── hack ├── LICENSE_BOILERPLATE.txt ├── check-cert-secret.sh ├── copy-crds.sh ├── generate-code ├── generate-renovate-ignore-deps.sh ├── generateChartOptions.py ├── go-test.sh ├── kind │ ├── certman │ │ ├── certman-down.sh │ │ ├── issuer-down.sh │ │ └── issuer-up.sh │ ├── common.sh │ ├── dns-controller-manager │ │ └── dns-controller-manager-up.sh │ ├── kind-create-cluster.sh │ ├── kind-delete-cluster.sh │ ├── knot-dns │ │ ├── crd-dnsprovider.yaml │ │ ├── knot-dns-certman-support.yaml.template │ │ ├── knot-dns-service.yaml │ │ ├── knot-dns-up.sh │ │ ├── patch-coredns.sh │ │ └── patch-deployment-coredns.yaml │ ├── local-issuer │ │ ├── local-issuer-down.sh │ │ └── local-issuer-up.sh │ ├── pebble │ │ └── pebble-up.sh │ ├── registry │ │ ├── base │ │ │ ├── kustomization.yaml │ │ │ └── registry.yaml │ │ ├── docker │ │ │ └── kustomization.yaml │ │ ├── europe-docker-pkg-dev │ │ │ └── kustomization.yaml │ │ ├── ghcr │ │ │ └── kustomization.yaml │ │ ├── k8s │ │ │ └── kustomization.yaml │ │ ├── kustomization.yaml │ │ └── namespace.yaml │ ├── skaffold-after-hock.sh │ ├── skaffold-run.sh │ └── test-functional-local.sh ├── sast.sh └── tools.go ├── pkg ├── apis │ ├── .import-restrictions │ └── cert │ │ ├── crds │ │ ├── cert.gardener.cloud_certificaterevocations.yaml │ │ ├── cert.gardener.cloud_certificates.yaml │ │ ├── cert.gardener.cloud_issuers.yaml │ │ └── zz_generated_crds.go │ │ ├── register.go │ │ └── v1alpha1 │ │ ├── doc.go │ │ ├── register.go │ │ ├── state.go │ │ ├── types.go │ │ └── zz_generated.deepcopy.go ├── cert │ ├── .import-restrictions │ ├── client │ │ └── scheme.go │ ├── source │ │ ├── certinfo.go │ │ ├── controller.go │ │ ├── defaults.go │ │ ├── interface.go │ │ ├── reconciler.go │ │ ├── slaves.go │ │ └── utils.go │ └── utils │ │ ├── domainrange.go │ │ ├── domainrange_test.go │ │ ├── issuerkey.go │ │ ├── issuerkey_test.go │ │ ├── mock │ │ ├── doc.go │ │ └── mocks.go │ │ ├── utils_certificate.go │ │ ├── utils_certificate_test.go │ │ ├── utils_issuer.go │ │ ├── utils_mod.go │ │ ├── utils_mod_test.go │ │ └── utils_suite_test.go ├── certman2 │ ├── .import-restrictions │ ├── apis │ │ ├── cert │ │ │ ├── crd-cert.gardener.cloud_certificaterevocations.yaml │ │ │ ├── crd-cert.gardener.cloud_certificates.yaml │ │ │ ├── crd-cert.gardener.cloud_issuers.yaml │ │ │ └── register.go │ │ └── config │ │ │ ├── doc.go │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── v1alpha1 │ │ │ ├── defaults.go │ │ │ ├── defaults_test.go │ │ │ ├── doc.go │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── v1alpha1_suite_test.go │ │ │ ├── zz_generated.conversion.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.defaults.go │ │ │ └── zz_generated.deepcopy.go │ ├── client │ │ ├── clusteraccess.go │ │ └── scheme.go │ ├── controller │ │ ├── certificate │ │ │ ├── add.go │ │ │ ├── certificate_suite_test.go │ │ │ ├── reconciler.go │ │ │ ├── reconciler_delete.go │ │ │ └── reconciler_reconcile.go │ │ ├── issuer │ │ │ └── controlplane │ │ │ │ ├── acme │ │ │ │ ├── handler.go │ │ │ │ ├── handler_test.go │ │ │ │ └── issuer_suite_test.go │ │ │ │ ├── add.go │ │ │ │ ├── ca │ │ │ │ ├── handler.go │ │ │ │ ├── handler_test.go │ │ │ │ └── issuer_suite_test.go │ │ │ │ ├── issuer_suite_test.go │ │ │ │ └── reconciler.go │ │ ├── predicate.go │ │ └── source │ │ │ ├── add.go │ │ │ ├── common │ │ │ ├── certinput.go │ │ │ ├── certinput_collector.go │ │ │ ├── constants.go │ │ │ └── reconcilerbase.go │ │ │ ├── gateways_crd_watchdog │ │ │ ├── add.go │ │ │ ├── add_test.go │ │ │ ├── crd_suite_test.go │ │ │ ├── reconciler.go │ │ │ ├── reconciler_test.go │ │ │ └── testdata │ │ │ │ ├── crd-gateways.gateway.networking.k8s.io_v1.yaml │ │ │ │ ├── crd-gateways.gateway.networking.k8s.io_v1beta1.yaml │ │ │ │ ├── crd-gateways.networking.istio.io_v1.yaml │ │ │ │ ├── crd-gateways.networking.istio.io_v1beta1.yaml │ │ │ │ ├── crd-httproutes.gateway.networking.k8s.io_v1.yaml │ │ │ │ ├── crd-httproutes.gateway.networking.k8s.io_v1beta1.yaml │ │ │ │ ├── crd-virtualservices.networking.istio.io_v1.yaml │ │ │ │ └── crd-virtualservices.networking.istio.io_v1beta1.yaml │ │ │ ├── ingress │ │ │ ├── add.go │ │ │ ├── add_test.go │ │ │ ├── ingress_suite_test.go │ │ │ ├── reconciler.go │ │ │ ├── reconciler_reconcile.go │ │ │ └── reconciler_test.go │ │ │ ├── istio_gateway │ │ │ ├── add.go │ │ │ ├── add_test.go │ │ │ ├── istio_gateway_suite_test.go │ │ │ ├── reconciler.go │ │ │ ├── reconciler_certinputmap_test.go │ │ │ ├── reconciler_reconcile.go │ │ │ ├── reconciler_test.go │ │ │ └── versions.go │ │ │ ├── k8s_gateway │ │ │ ├── add.go │ │ │ ├── add_test.go │ │ │ ├── k8s_gateway_suite_test.go │ │ │ ├── reconciler.go │ │ │ ├── reconciler_certinputmap_test.go │ │ │ ├── reconciler_reconcile.go │ │ │ ├── reconciler_test.go │ │ │ └── versions.go │ │ │ └── service │ │ │ ├── add.go │ │ │ ├── add_test.go │ │ │ ├── reconciler.go │ │ │ ├── reconciler_reconcile.go │ │ │ ├── reconciler_test.go │ │ │ └── service_suite_test.go │ ├── core │ │ ├── associatedObjects.go │ │ ├── const.go │ │ ├── issuerDNSSelections.go │ │ ├── keys.go │ │ ├── objectnameset.go │ │ ├── quotas.go │ │ ├── referencedSecrets.go │ │ ├── referencedSecrets_test.go │ │ ├── state.go │ │ ├── support.go │ │ ├── utils.go │ │ └── wrappedregistration.go │ └── testutils │ │ └── assert_events.go ├── client │ ├── .import-restrictions │ └── cert │ │ ├── clientset │ │ └── versioned │ │ │ ├── clientset.go │ │ │ ├── fake │ │ │ ├── clientset_generated.go │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ └── typed │ │ │ └── cert │ │ │ └── v1alpha1 │ │ │ ├── cert_client.go │ │ │ ├── certificate.go │ │ │ ├── certificaterevocation.go │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_cert_client.go │ │ │ ├── fake_certificate.go │ │ │ ├── fake_certificaterevocation.go │ │ │ └── fake_issuer.go │ │ │ ├── generated_expansion.go │ │ │ └── issuer.go │ │ ├── informers │ │ └── externalversions │ │ │ ├── cert │ │ │ ├── interface.go │ │ │ └── v1alpha1 │ │ │ │ ├── certificate.go │ │ │ │ ├── certificaterevocation.go │ │ │ │ ├── interface.go │ │ │ │ └── issuer.go │ │ │ ├── factory.go │ │ │ ├── generic.go │ │ │ └── internalinterfaces │ │ │ └── factory_interfaces.go │ │ └── listers │ │ └── cert │ │ └── v1alpha1 │ │ ├── certificate.go │ │ ├── certificaterevocation.go │ │ ├── expansion_generated.go │ │ └── issuer.go ├── controller │ ├── .import-restrictions │ ├── common.go │ ├── issuer │ │ ├── acme │ │ │ └── handler.go │ │ ├── ca │ │ │ └── handler.go │ │ ├── certificate │ │ │ ├── backupsecret.go │ │ │ ├── certificate_suite_test.go │ │ │ ├── reconciler.go │ │ │ ├── utils.go │ │ │ └── utils_test.go │ │ ├── controller.go │ │ ├── core │ │ │ ├── associatedObjects.go │ │ │ ├── const.go │ │ │ ├── core_suite_test.go │ │ │ ├── issuerDNSSelections.go │ │ │ ├── objectnameset.go │ │ │ ├── quotas.go │ │ │ ├── referencedSecrets.go │ │ │ ├── referencedSecrets_test.go │ │ │ ├── state.go │ │ │ ├── support.go │ │ │ ├── support_test.go │ │ │ └── wrappedregistration.go │ │ ├── reconciler.go │ │ ├── revocation │ │ │ └── reconciler.go │ │ └── selfSigned │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ └── selfSigned_suite_test.go │ └── source │ │ ├── gateways │ │ ├── crdwatch │ │ │ └── controller.go │ │ ├── gatewayapi │ │ │ ├── controller.go │ │ │ ├── gateway_suite_test.go │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── httproutesReconciler.go │ │ │ ├── state.go │ │ │ └── state_test.go │ │ └── istio │ │ │ ├── controller.go │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── istio_gateway_suite_test.go │ │ │ ├── state.go │ │ │ ├── state_test.go │ │ │ ├── targetSourcesReconciler.go │ │ │ └── virtualservicesReconciler.go │ │ ├── helper.go │ │ ├── ingress │ │ ├── controller.go │ │ └── handler.go │ │ └── service │ │ ├── controller.go │ │ └── handler.go └── shared │ ├── .import-restrictions │ ├── const.go │ ├── dns_utils.go │ ├── dns_utils_test.go │ ├── issuerinfo.go │ ├── issuerkeyitf.go │ ├── legobridge │ ├── cakeypair.go │ ├── cakeypair_test.go │ ├── certificate.go │ ├── certificate_test.go │ ├── delegatingprovider.go │ ├── delegatingprovider_test.go │ ├── dnscontrollerprovider.go │ ├── dnsrecordprovider.go │ ├── keystores.go │ ├── keystores_test.go │ ├── legobridge_suite_test.go │ ├── pending.go │ ├── pending_test.go │ ├── pki.go │ ├── pki_test.go │ └── reguser.go │ ├── metrics │ └── metrics.go │ ├── shared_suite_test.go │ ├── utils_certificate.go │ └── utils_certificate_test.go ├── renovate.json5 ├── skaffold.yaml └── test ├── certman2 ├── .import-restrictions └── integration │ └── controller │ ├── issuer │ ├── issuer_suite_test.go │ └── issuer_test.go │ └── source │ ├── source_suite_test.go │ ├── source_test.go │ └── testdata │ ├── crd-gateways.gateway.networking.k8s.io_v1.yaml │ ├── crd-gateways.networking.istio.io_v1.yaml │ ├── crd-httproutes.gateway.networking.k8s.io_v1.yaml │ └── crd-virtualservices.networking.istio.io_v1.yaml ├── functional ├── .import-restrictions ├── basics.go ├── config │ ├── config.go │ └── utils.go ├── dnsrecords.go ├── functest-config-kind.yaml ├── functest-config.yaml ├── resources │ └── 10-crd-extensions.gardener.cloud_dnsrecords.yaml ├── run.sh ├── suite.go └── suite_test.go ├── integration ├── .import-restrictions └── controller │ └── issuer │ ├── certificate_test.go │ ├── issuer_suite_test.go │ └── issuer_test.go └── utils ├── generate_cert.go ├── logbridge.go └── pebble.go /.ci/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2018 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -e 8 | 9 | cd "$(dirname "$0")/.." 10 | echo "running build..." 11 | make release 12 | -------------------------------------------------------------------------------- /.ci/prepare_release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -e 8 | 9 | 10 | # add full-fleshed tar and helm 11 | BASE_URL="https://get.helm.sh" 12 | HELM_VERSION=v2.17.0 13 | TAR_FILE="helm-${HELM_VERSION}-linux-amd64.tar.gz" 14 | 15 | apk add --update --no-cache curl ca-certificates tar 16 | curl -L ${BASE_URL}/${TAR_FILE} |tar xvz 17 | mv linux-amd64/helm /usr/bin/helm 18 | chmod +x /usr/bin/helm 19 | 20 | 21 | if [[ -z "${SOURCE_PATH}" ]]; then 22 | export SOURCE_PATH="$(readlink -f "$(dirname ${0})/..")" 23 | else 24 | export SOURCE_PATH="$(readlink -f "${SOURCE_PATH}")" 25 | fi 26 | 27 | ## currently disabled, as controller registration happens per shoot by extension-shoot-cert-service 28 | #"${SOURCE_PATH}/hack/generate-controller-registration.sh" \ 29 | # cert-management \ 30 | # "${SOURCE_PATH}/charts/cert-management/" \ 31 | # "${SOURCE_PATH}/examples/gardener-controllerregistration.yaml" \ 32 | # Issuer:gardener 33 | 34 | VERSION_FILE="$(readlink -f "${SOURCE_PATH}/VERSION")" 35 | VERSION="$(cat "${VERSION_FILE}")" 36 | VERSIONTAG="${VERSION//-dev/-master}" 37 | 38 | sed -i -e "s/ tag: .*/ tag: ${VERSIONTAG}/" "${SOURCE_PATH}/charts/cert-management/values.yaml" 39 | -------------------------------------------------------------------------------- /.ci/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2018 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -e 8 | 9 | cd "$(dirname "$0")/.." 10 | echo "Running tests..." 11 | make test 12 | -------------------------------------------------------------------------------- /.ci/verify: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | cd "$(dirname $0)/.." 8 | 9 | git config --global user.email "gardener@sap.com" 10 | git config --global user.name "Gardener CI/CD" 11 | 12 | export GOTOOLCHAIN=auto 13 | make verify-extended 14 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | ** 3 | 4 | # Exclude folders relevant for build 5 | !.git 6 | !.dockerignore 7 | !charts/ 8 | !cmd/ 9 | !pkg/ 10 | !hack/ 11 | !go.mod 12 | !go.sum 13 | !Makefile 14 | !VERSION 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug 4 | labels: kind/bug 5 | 6 | --- 7 | 8 | **What happened**: 9 | 10 | **What you expected to happen**: 11 | 12 | **How to reproduce it (as minimally and precisely as possible)**: 13 | 14 | **Anything else we need to know**: 15 | 16 | **Environment**: 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Request 3 | about: Suggest an enhancement 4 | labels: kind/enhancement 5 | 6 | --- 7 | 8 | **What would you like to be added**: 9 | 10 | **Why is this needed**: 11 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Create PRs for golang version updates 4 | - package-ecosystem: docker 5 | directory: / 6 | schedule: 7 | interval: daily 8 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | **How to categorize this PR?** 2 | 12 | /kind TODO 13 | 14 | **What this PR does / why we need it**: 15 | 16 | **Which issue(s) this PR fixes**: 17 | Fixes # 18 | 19 | **Special notes for your reviewer**: 20 | 21 | **Release note**: 22 | 31 | ```other operator 32 | 33 | ``` 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | .idea/ 3 | !.idea/copyright/ 4 | *.sw[pq] 5 | tmp/ 6 | /dev/ 7 | /local/ 8 | /cmd/cert-controller-manager/cert-controller-manager 9 | /cert-controller-manager 10 | /certman2 11 | .vscode/ 12 | .kubeconfig-kind-integration 13 | 14 | *.coverprofile 15 | *.html 16 | 17 | # gosec 18 | gosec-report.sarif 19 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | concurrency: 4 4 | linters: 5 | enable: 6 | - copyloopvar 7 | - ginkgolinter 8 | - importas 9 | - nilerr 10 | - revive 11 | - whitespace 12 | settings: 13 | loggercheck: 14 | require-string-key: true 15 | no-printf-like: true 16 | revive: 17 | rules: 18 | - name: duplicated-imports 19 | - name: unused-parameter 20 | - name: unreachable-code 21 | - name: context-as-argument 22 | - name: early-return 23 | - name: exported 24 | exclusions: 25 | generated: lax 26 | rules: 27 | - linters: 28 | - staticcheck 29 | text: 'SA1019:' 30 | - linters: 31 | - staticcheck 32 | text: 'ST1001:' # should not use dot imports 33 | - path: (.+)\.go$ 34 | text: Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked 35 | - path: (.+)\.go$ 36 | text: var-naming 37 | - path: (.+)\.go$ 38 | text: dot-imports 39 | - path: (.+)\.go$ 40 | text: package-comments 41 | - path: (.+)\.go$ 42 | text: unexported-return 43 | - path: (.+)\.go$ 44 | text: indent-error-flow 45 | - path: (.+)\.go$ 46 | text: 'exported: (type|func) name will be used as .* by other packages, and that stutters;' 47 | - path: (.+)\.go$ 48 | text: 'undeclared name: `.*`' 49 | - path: (.+)\.go$ 50 | text: '".*" imported but not used' 51 | - path: (.+)\.go$ 52 | text: 'structured logging message should be capitalized: "garden(er-apiserver|er-controller-manager|er-admission-controller|er-operator|er-resource-manager|let)' 53 | paths: 54 | - zz_generated.*\.go$ 55 | - test/functional/config 56 | - third_party$ 57 | - builtin$ 58 | - examples$ 59 | formatters: 60 | exclusions: 61 | generated: lax 62 | paths: 63 | - third_party$ 64 | - builtin$ 65 | - examples$ 66 | -------------------------------------------------------------------------------- /.idea/copyright/Gardener.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: Gardener cert-management 3 | Upstream-Contact: The Gardener project 4 | Source: https://github.com/gardener/cert-management 5 | 6 | # -------------------------------------------------- 7 | # source code 8 | 9 | Files: * 10 | Copyright: 2017-2024 SAP SE or an SAP affiliate company and Gardener contributors 11 | License: Apache-2.0 12 | 13 | # -------------------------------------------------- 14 | # documentation 15 | 16 | Files: *.md 17 | Copyright: 2017-2024 SAP SE or an SAP affiliate company and Gardener contributors 18 | License: CC-BY-4.0 19 | 20 | # -------------------------------------------------- 21 | # third-party 22 | 23 | # --- copied source code --- 24 | # Files: 25 | # Copyright: 26 | # License: 27 | 28 | # --- vendor folder dependencies --- 29 | # Files: 30 | # Copyright: 31 | # License: -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please refer to the [Gardener contributor guide](https://gardener.cloud/docs/contribute). -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | ############# builder ############# 6 | FROM golang:1.24.3 AS builder 7 | 8 | WORKDIR /build 9 | 10 | # Copy go mod and sum files 11 | COPY go.mod go.sum ./ 12 | # Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed 13 | RUN go mod download 14 | 15 | COPY . . 16 | 17 | RUN make release 18 | 19 | ############# base 20 | FROM gcr.io/distroless/static-debian12:nonroot AS base 21 | 22 | ############# cert-controller-manager ############# 23 | FROM base AS cert-controller-manager 24 | 25 | WORKDIR / 26 | COPY --from=builder /build/cert-controller-manager /cert-controller-manager 27 | 28 | ENTRYPOINT ["/cert-controller-manager"] 29 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs at https://go.k8s.io/owners 2 | 3 | reviewers: 4 | - cert-management-reviewers 5 | approvers: 6 | - cert-management-approvers 7 | -------------------------------------------------------------------------------- /OWNERS_ALIASES: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs at https://go.k8s.io/owners 2 | 3 | aliases: 4 | cert-management-reviewers: 5 | - MartinWeindel 6 | - marc1404 7 | cert-management-approvers: 8 | - MartinWeindel 9 | - marc1404 10 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | v0.17.7-dev -------------------------------------------------------------------------------- /charts/cert-management/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | .vscode/ 23 | -------------------------------------------------------------------------------- /charts/cert-management/Chart.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: v1 6 | appVersion: "1.0" 7 | description: A Helm chart for the cert-management component 8 | name: cert-management 9 | version: 0.1.0 10 | -------------------------------------------------------------------------------- /charts/cert-management/templates/0helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "cert-management.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "cert-management.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "cert-management.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{- define "image" -}} 35 | {{- if hasPrefix "sha256:" (required "$.tag is required" $.tag) -}} 36 | {{ required "$.repository is required" $.repository }}@{{ required "$.tag is required" $.tag }} 37 | {{- else -}} 38 | {{ required "$.repository is required" $.repository }}:{{ required "$.tag is required" $.tag }} 39 | {{- end -}} 40 | {{- end -}} 41 | -------------------------------------------------------------------------------- /charts/cert-management/templates/ca-certificates-configmap.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | {{- if .Values.configuration.caCertificates }} 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: {{ include "cert-management.fullname" . }}-ca-certificates 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | helm.sh/chart: {{ include "cert-management.chart" . }} 10 | app.kubernetes.io/name: {{ include "cert-management.name" . }} 11 | app.kubernetes.io/instance: {{ .Release.Name }} 12 | app.kubernetes.io/managed-by: {{ .Release.Service }} 13 | data: 14 | certs.pem: {{- toYaml .Values.configuration.caCertificates | indent 2 }} 15 | {{- end}} -------------------------------------------------------------------------------- /charts/cert-management/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | --- 6 | apiVersion: rbac.authorization.k8s.io/v1 7 | kind: ClusterRoleBinding 8 | metadata: 9 | name: {{ include "cert-management.fullname" . }} 10 | labels: 11 | helm.sh/chart: {{ include "cert-management.chart" . }} 12 | app.kubernetes.io/name: {{ include "cert-management.name" . }} 13 | app.kubernetes.io/instance: {{ .Release.Name }} 14 | app.kubernetes.io/managed-by: {{ .Release.Service }} 15 | roleRef: 16 | apiGroup: rbac.authorization.k8s.io 17 | kind: ClusterRole 18 | name: {{ include "cert-management.fullname" . }} 19 | subjects: 20 | - kind: ServiceAccount 21 | name: {{ include "cert-management.fullname" . }} 22 | namespace: {{ .Release.Namespace }} 23 | -------------------------------------------------------------------------------- /charts/cert-management/templates/role.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | --- 6 | apiVersion: rbac.authorization.k8s.io/v1 7 | kind: Role 8 | metadata: 9 | name: {{ include "cert-management.fullname" . }} 10 | namespace: {{ .Release.Namespace }} 11 | labels: 12 | helm.sh/chart: {{ include "cert-management.chart" . }} 13 | app.kubernetes.io/name: {{ include "cert-management.name" . }} 14 | app.kubernetes.io/instance: {{ .Release.Name }} 15 | app.kubernetes.io/managed-by: {{ .Release.Service }} 16 | rules: 17 | - apiGroups: 18 | - "" 19 | resources: 20 | - configmaps 21 | resourceNames: 22 | - {{ include "cert-management.fullname" . }}-controllers 23 | verbs: 24 | - get 25 | - update 26 | - apiGroups: 27 | - "" 28 | resources: 29 | - configmaps 30 | verbs: 31 | - create 32 | - apiGroups: 33 | - coordination.k8s.io 34 | resources: 35 | - leases 36 | verbs: 37 | - create 38 | - apiGroups: 39 | - coordination.k8s.io 40 | resources: 41 | - leases 42 | resourceNames: 43 | - {{ include "cert-management.fullname" . }}-controllers 44 | verbs: 45 | - get 46 | - watch 47 | - update 48 | -------------------------------------------------------------------------------- /charts/cert-management/templates/rolebinding.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | --- 6 | apiVersion: rbac.authorization.k8s.io/v1 7 | kind: RoleBinding 8 | metadata: 9 | name: {{ include "cert-management.fullname" . }} 10 | namespace: {{ .Release.Namespace }} 11 | labels: 12 | helm.sh/chart: {{ include "cert-management.chart" . }} 13 | app.kubernetes.io/name: {{ include "cert-management.name" . }} 14 | app.kubernetes.io/instance: {{ .Release.Name }} 15 | app.kubernetes.io/managed-by: {{ .Release.Service }} 16 | roleRef: 17 | apiGroup: rbac.authorization.k8s.io 18 | kind: Role 19 | name: {{ include "cert-management.fullname" . }} 20 | subjects: 21 | - kind: ServiceAccount 22 | name: {{ include "cert-management.fullname" . }} 23 | namespace: {{ .Release.Namespace }} 24 | -------------------------------------------------------------------------------- /charts/cert-management/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | --- 6 | apiVersion: v1 7 | kind: ServiceAccount 8 | metadata: 9 | name: {{ include "cert-management.fullname" . }} 10 | namespace: {{ .Release.Namespace }} 11 | labels: 12 | helm.sh/chart: {{ include "cert-management.chart" . }} 13 | app.kubernetes.io/name: {{ include "cert-management.name" . }} 14 | app.kubernetes.io/instance: {{ .Release.Name }} 15 | app.kubernetes.io/managed-by: {{ .Release.Service }} 16 | -------------------------------------------------------------------------------- /cmd/cert-controller-manager/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - '' 5 | forbiddenPrefixes: 6 | - github.com/gardener/cert-management/pkg/certman2 7 | -------------------------------------------------------------------------------- /cmd/certman2/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - github.com/gardener/cert-management/cmd/certman2 5 | - github.com/gardener/cert-management/pkg/apis 6 | - github.com/gardener/cert-management/pkg/certman2 7 | - github.com/gardener/cert-management/pkg/shared 8 | - selectorRegexp: github[.]com/gardener/controller-manager-library 9 | forbiddenPrefixes: 10 | - '' 11 | -------------------------------------------------------------------------------- /cmd/certman2/main.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/gardener/gardener/cmd/utils" 12 | "sigs.k8s.io/controller-runtime/pkg/manager/signals" 13 | 14 | "github.com/gardener/cert-management/cmd/certman2/app" 15 | ) 16 | 17 | func main() { 18 | utils.DeduplicateWarnings() 19 | 20 | if err := app.NewCommand().ExecuteContext(signals.SetupSignalHandler()); err != nil { 21 | fmt.Println(err) 22 | os.Exit(1) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/10-crds.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # required CRDs will be deployed automatically by the controllers 3 | # therefore there is no need to deploy those CRDs manually. 4 | # -------------------------------------------------------------------------------- /examples/20-issuer-ca.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: v1 6 | data: 7 | tls.crt: ... 8 | tls.key: ... 9 | kind: Secret 10 | metadata: 11 | name: issuer-ca-secret 12 | type: kubernetes.io/tls 13 | --- 14 | apiVersion: cert.gardener.cloud/v1alpha1 15 | kind: Issuer 16 | metadata: 17 | name: issuer-ca 18 | namespace: default 19 | spec: 20 | ca: 21 | privateKeySecretRef: 22 | name: issuer-ca-secret 23 | namespace: default 24 | -------------------------------------------------------------------------------- /examples/20-issuer-productive.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: Issuer 7 | metadata: 8 | name: issuer-prod 9 | namespace: default 10 | spec: 11 | acme: 12 | server: https://acme-v02.api.letsencrypt.org/directory 13 | email: some.user@mydomain.com 14 | autoRegistration: true 15 | -------------------------------------------------------------------------------- /examples/20-issuer-selfsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert.gardener.cloud/v1alpha1 2 | kind: Issuer 3 | metadata: 4 | name: issuer-selfsigned 5 | namespace: default 6 | spec: 7 | selfSigned: {} 8 | -------------------------------------------------------------------------------- /examples/20-issuer-staging.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: Issuer 7 | metadata: 8 | name: issuer-staging 9 | namespace: default 10 | spec: 11 | acme: 12 | server: https://acme-staging-v02.api.letsencrypt.org/directory 13 | email: some.user@mydomain.com 14 | autoRegistration: true 15 | # with 'autoRegistration: true' a new account will be created if the secretRef is not existing 16 | privateKeySecretRef: 17 | name: issuer-staging-secret 18 | namespace: default -------------------------------------------------------------------------------- /examples/21-issuer-acme-eab.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: Issuer 7 | metadata: 8 | name: issuer-with-external-account 9 | namespace: default 10 | spec: 11 | acme: 12 | server: https://some.acme.provider.com/directory 13 | email: some.user@mydomain.com 14 | autoRegistration: true 15 | externalAccountBinding: 16 | keyID: mykey 17 | keySecretRef: 18 | # the secret must contain the data key 'hmacKey' 19 | name: issuer-external-account-secret 20 | namespace: default 21 | # For some special setups, the DNS challenges are only performed pro forma. In this case the 22 | # DNS Entry creation and DNS propagation check can be disabled with 'skipDNSChallengeValidation: true' 23 | # skipDNSChallengeValidation: true 24 | 25 | # optionally restrict domain ranges for which certificates can be requested 26 | # domains: 27 | # include: 28 | # - sub1.mydomain.com 29 | # - sub2.mydomain.com 30 | # exclude: 31 | # - private.sub1.mydomain.com -------------------------------------------------------------------------------- /examples/30-cert-ca.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: Certificate 7 | metadata: 8 | name: cert-ca 9 | namespace: default 10 | spec: 11 | commonName: cert1.mydomain.com 12 | dnsNames: 13 | - cert1.my-other-domain.com 14 | # if issuer is not specified, the default issuer is used 15 | issuerRef: 16 | name: issuer-ca 17 | # optionally specify secret to store certificate 18 | secretRef: 19 | name: cert-ca-secret 20 | namespace: default 21 | -------------------------------------------------------------------------------- /examples/30-cert-csr.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: Certificate 7 | metadata: 8 | name: cert-csr 9 | namespace: default 10 | spec: 11 | csr: ... 12 | issuerRef: 13 | name: issuer-staging 14 | # optionally specify secret to store certificate 15 | secretRef: 16 | name: cert-csr-secret 17 | namespace: default -------------------------------------------------------------------------------- /examples/30-cert-selfsigned.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert.gardener.cloud/v1alpha1 2 | kind: Certificate 3 | metadata: 4 | name: cert-selfsigned 5 | namespace: default 6 | spec: 7 | commonName: ca1.mydomain.com 8 | isCA: true 9 | # optional: default is 90 days (2160h). Must be at least 2*30 days (1440h) with the default renewal window of 30 days. 10 | # duration: 1440h 11 | # optional defaults to RSA 2048 12 | # privateKey: 13 | # algorithm: ECDSA 14 | # size: 384 15 | # CSR can also be specified 16 | # csr: ... 17 | issuerRef: 18 | name: issuer-selfsigned 19 | namespace: default # must be specified when issuer runs in shoot! 20 | # optional: secret where the certificate should be stored 21 | #secretRef: 22 | # name: cert-selfsigned-foo 23 | # namespace: default 24 | -------------------------------------------------------------------------------- /examples/30-cert-simple-with-keystores.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: Certificate 7 | metadata: 8 | annotations: 9 | # class annotation only needed if cert-controller-manager is started with --cert-class=myclass 10 | #cert.gardener.cloud/class: myclass 11 | name: cert-simple-with-keystores 12 | namespace: default 13 | spec: 14 | commonName: cert1.mydomain.com 15 | dnsNames: 16 | - cert1.my-other-domain.com 17 | # optionally specify secret to store certificate 18 | secretRef: 19 | name: cert-simple-secret 20 | namespace: default 21 | # optionally set labels for the secret 22 | #secretLabels: 23 | # key1: value1 24 | # key2: value2 25 | 26 | # enable keystore creation for both JKS and PKCS#12 27 | # This will create additional data entries in the certificate secret named `keystore.jks`, `truststore.jks` for JKS 28 | # and `keystore.p12`, `truststore.p12` for PKCS#12 29 | keystores: 30 | jks: 31 | create: true 32 | passwordSecretRef: 33 | secretName: keystore-secret 34 | key: password 35 | pkcs12: 36 | create: true 37 | passwordSecretRef: 38 | secretName: keystore-secret 39 | key: password 40 | --- 41 | apiVersion: v1 42 | kind: Secret 43 | metadata: 44 | name: keystore-secret 45 | namespace: default 46 | data: 47 | password: cGFzcw== # example password is `pass` -------------------------------------------------------------------------------- /examples/30-cert-simple.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: Certificate 7 | metadata: 8 | annotations: 9 | # class annotation only needed if cert-controller-manager is started with --cert-class=myclass 10 | #cert.gardener.cloud/class: myclass 11 | # annotations needed when using DNSRecords 12 | #cert.gardener.cloud/dnsrecord-provider-type: aws-route53 13 | #cert.gardener.cloud/dnsrecord-secret-ref: myns/mysecret 14 | #cert.gardener.cloud/dnsrecord-class: garden # optional, only required on Garden runtime cluster 15 | name: cert-simple 16 | namespace: default 17 | spec: 18 | commonName: cert1.mydomain.com 19 | dnsNames: 20 | - cert1.my-other-domain.com 21 | # if issuer is not specified, the default issuer is used 22 | issuerRef: 23 | name: issuer-staging 24 | # for shoot issuers, the namespace must be specified 25 | #namespace: my-ns 26 | # optionally specify secret to store certificate 27 | secretRef: 28 | name: cert-simple-secret 29 | namespace: default 30 | # optionally set labels for the secret 31 | #secretLabels: 32 | # key1: value1 33 | # key2: value2 34 | 35 | # If delegated domain for DNS01 challenge should be used. This has only an effect if a CNAME record is set for 36 | # either '_acme-challenge.cert1.mydomain.com' or '_acme-challenge.cert1.my-other-domain.com'. 37 | # For example: If a CNAME record exists '_acme-challenge.cert1.mydomain.com' => '_acme-challenge.writable.domain.com', 38 | # the DNS challenge will be written to '_acme-challenge.writable.domain.com'. 39 | # followCNAME: true 40 | 41 | # Optionally specify the preferred certificate chain: if the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used. 42 | # preferredChain: "ISRG Root X1" 43 | 44 | # Optionally specify algorithm and key size for private key 45 | # privateKey: 46 | # algorithm: ECDSA 47 | # size: 384 -------------------------------------------------------------------------------- /examples/30-cert-wildcard.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: Certificate 7 | metadata: 8 | name: cert-wildcard 9 | namespace: default 10 | spec: 11 | commonName: "*.cert2.martin.mydomain.com" 12 | issuerRef: 13 | name: issuer-staging 14 | # optionally specify secret to store certificate 15 | secretRef: 16 | name: cert-wildcard-secret 17 | namespace: default 18 | # optionally set labels for the secret 19 | #secretLabels: 20 | # key1: value1 21 | # key2: value2 -------------------------------------------------------------------------------- /examples/40-gateway-gateway-api.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: Gateway 3 | metadata: 4 | annotations: 5 | cert.gardener.cloud/purpose: managed 6 | #cert.gardener.cloud/commonname: "*.demo.mydomain.com" # optional, if not specified the first name from spec.tls[].hosts is used as common name 7 | #cert.gardener.cloud/dnsnames: "" # optional, if not specified the names from spec.tls[].hosts are used 8 | #cert.gardener.cloud/follow-cname: "true" # optional, to activate CNAME following for the DNS challenge 9 | #cert.gardener.cloud/secret-labels: "key1=value1,key2=value2" # optional labels for the certificate secret 10 | #cert.gardener.cloud/issuer: issuer-name # optional to specify custom issuer (use namespace/name for shoot issuers) 11 | #cert.gardener.cloud/preferred-chain: "chain name" # optional to specify preferred-chain (value is the Subject Common Name of the root issuer) 12 | #cert.gardener.cloud/private-key-algorithm: ECDSA # optional to specify algorithm for private key, allowed values are 'RSA' or 'ECDSA' 13 | #cert.gardener.cloud/private-key-size: "384" # optional to specify size of private key, allowed values for RSA are "2048", "3072", "4096" and for ECDSA "256" and "384" 14 | # annotations needed when using DNSRecords 15 | #cert.gardener.cloud/dnsrecord-provider-type: aws-route53 16 | #cert.gardener.cloud/dnsrecord-secret-ref: myns/mysecret 17 | name: my-gateway 18 | namespace: default 19 | spec: 20 | gatewayClassName: my-gateway-class 21 | listeners: 22 | - allowedRoutes: 23 | namespaces: 24 | from: Selector 25 | selector: 26 | matchLabels: 27 | shared-gateway-access: "true" 28 | hostname: foo.example.com 29 | name: https 30 | port: 443 31 | protocol: HTTPS 32 | tls: 33 | certificateRefs: 34 | - name: my-tls-secret # note: listeners are only considered if they have exactly one certificateRefs item -------------------------------------------------------------------------------- /examples/40-gateway-istio.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1 2 | kind: Gateway 3 | metadata: 4 | annotations: 5 | cert.gardener.cloud/purpose: managed 6 | #cert.gardener.cloud/commonname: "*.demo.mydomain.com" # optional, if not specified the first name from spec.tls[].hosts is used as common name 7 | #cert.gardener.cloud/dnsnames: "" # optional, if not specified the names from spec.tls[].hosts are used 8 | #cert.gardener.cloud/follow-cname: "true" # optional, to activate CNAME following for the DNS challenge 9 | #cert.gardener.cloud/secret-labels: "key1=value1,key2=value2" # optional labels for the certificate secret 10 | #cert.gardener.cloud/issuer: issuer-name # optional to specify custom issuer (use namespace/name for shoot issuers) 11 | #cert.gardener.cloud/preferred-chain: "chain name" # optional to specify preferred-chain (value is the Subject Common Name of the root issuer) 12 | #cert.gardener.cloud/private-key-algorithm: ECDSA # optional to specify algorithm for private key, allowed values are 'RSA' or 'ECDSA' 13 | #cert.gardener.cloud/private-key-size: "384" # optional to specify size of private key, allowed values for RSA are "2048", "3072", "4096" and for ECDSA "256" and "384" 14 | #cert.gardener.cloud/secret-namespace: "istio-system" # optional to specify the namespace where the certificate secret should be created 15 | name: my-gateway 16 | namespace: default 17 | spec: 18 | selector: 19 | istio: ingressgateway 20 | servers: 21 | - hosts: 22 | - uk.example.com 23 | - eu.example.com 24 | port: 25 | name: https-443 26 | number: 443 27 | protocol: HTTPS 28 | tls: # this server is ignored by the cert-controller-manager, as `tls.credentialName` is not set 29 | mode: SIMPLE 30 | privateKey: /etc/certs/privatekey.pem 31 | serverCertificate: /etc/certs/servercert.pem 32 | - hosts: 33 | - bookinfo-namespace/*.example2.com 34 | port: 35 | name: https-9443 36 | number: 9443 37 | protocol: HTTPS 38 | tls: 39 | credentialName: my-secret # only servers with credentialName will be considered 40 | mode: SIMPLE -------------------------------------------------------------------------------- /examples/40-ingress-echoheaders.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: networking.k8s.io/v1 6 | kind: Ingress 7 | metadata: 8 | name: echoheaders 9 | namespace: default 10 | annotations: 11 | cert.gardener.cloud/purpose: managed 12 | #dns.gardener.cloud/class: garden # needed on Gardener shoot clusters for managed DNS record creation 13 | #cert.gardener.cloud/commonname: "*.demo.mydomain.com" # optional, if not specified the first name from spec.tls[].hosts is used as common name 14 | #cert.gardener.cloud/dnsnames: "" # optional, if not specified the names from spec.tls[].hosts are used 15 | #cert.gardener.cloud/follow-cname: "true" # optional, same as spec.followCNAME in certificates 16 | #cert.gardener.cloud/secret-labels: "key1=value1,key2=value2" # optional labels for the certificate secret 17 | #cert.gardener.cloud/issuer: issuer-name # optional to specify custom issuer (use namespace/name for shoot issuers) 18 | #cert.gardener.cloud/preferred-chain: "chain name" # optional to specify preferred-chain (value is the Subject Common Name of the root issuer) 19 | #cert.gardener.cloud/private-key-algorithm: ECDSA # optional to specify algorithm for private key, allowed values are 'RSA' or 'ECDSA' 20 | #cert.gardener.cloud/private-key-size: "384" # optional to specify size of private key, allowed values for RSA are "2048", "3072", "4096" and for ECDSA "256" and "384" 21 | # annotations needed when using DNSRecords 22 | #cert.gardener.cloud/dnsrecord-provider-type: aws-route53 23 | #cert.gardener.cloud/dnsrecord-secret-ref: myns/mysecret 24 | spec: 25 | tls: 26 | - hosts: 27 | - echoheaders.demo.mydomain.com 28 | secretName: cert-echoheaders 29 | rules: 30 | - host: echoheaders.demo.mydomain.com 31 | http: 32 | paths: 33 | - backend: 34 | service: 35 | name: echoheaders 36 | port: 37 | number: 80 38 | path: / 39 | pathType: Prefix 40 | -------------------------------------------------------------------------------- /examples/40-service-loadbalancer.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: v1 6 | kind: Service 7 | metadata: 8 | annotations: 9 | cert.gardener.cloud/secretname: test-service-secret 10 | dns.gardener.cloud/dnsnames: test-service.demo.mydomain.com 11 | dns.gardener.cloud/ttl: "600" 12 | #dns.gardener.cloud/class: garden # needed on Gardener shoot clusters for managed DNS record creation 13 | #cert.gardener.cloud/commonname: "*.demo.mydomain.com" # optional, if not specified the first name from dns.gardener.cloud/dnsnames is used as common name 14 | #cert.gardener.cloud/dnsnames: "" # optional, if specified overrides dns.gardener.cloud/dnsnames annotation for certificate names 15 | #cert.gardener.cloud/follow-cname: "true" # optional, same as spec.followCNAME in certificates 16 | #cert.gardener.cloud/secret-labels: "key1=value1,key2=value2" # optional labels for the certificate secret 17 | #cert.gardener.cloud/issuer: issuer-name # optional to specify custom issuer (use namespace/name for shoot issuers) 18 | #cert.gardener.cloud/preferred-chain: "chain name" # optional to specify preferred-chain (value is the Subject Common Name of the root issuer) 19 | #cert.gardener.cloud/private-key-algorithm: ECDSA # optional to specify algorithm for private key, allowed values are 'RSA' or 'ECDSA' 20 | #cert.gardener.cloud/private-key-size: "384" # optional to specify size of private key, allowed values for RSA are "2048", "3072", "4096" and for ECDSA "256" and "384" 21 | #cert.gardener.cloud/secret-namespace: "my-namespace" # optional to specify the namespace where the certificate secret should be created 22 | 23 | # annotations needed when using DNSRecords 24 | #cert.gardener.cloud/dnsrecord-provider-type: aws-route53 25 | #cert.gardener.cloud/dnsrecord-secret-ref: myns/mysecret 26 | 27 | name: test-service 28 | namespace: default 29 | spec: 30 | ports: 31 | - name: http 32 | port: 80 33 | protocol: TCP 34 | targetPort: 8080 35 | type: LoadBalancer -------------------------------------------------------------------------------- /examples/50-certificate-revocation.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | apiVersion: cert.gardener.cloud/v1alpha1 6 | kind: CertificateRevocation 7 | metadata: 8 | name: revoke-sample 9 | namespace: default 10 | spec: 11 | certificateRef: 12 | name: mycert 13 | namespace: default 14 | 15 | # Uncomment the following line if the certificate should be renewed before revoking the old one(s) 16 | #renew: true 17 | 18 | # Optionally specify a qualifying date. All certificates requested before this date will be revoked. 19 | # If not specified, the current time is used by default. 20 | #qualifyingDate: "2020-12-22T17:00:35Z" 21 | -------------------------------------------------------------------------------- /hack/LICENSE_BOILERPLATE.txt: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | -------------------------------------------------------------------------------- /hack/copy-crds.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | 8 | set -e 9 | 10 | source_dir="$(dirname "$0")/../pkg/apis/cert/crds" 11 | destination_dir="$(dirname "$0")/../charts/cert-management/templates/" 12 | 13 | # Function to update the metadata section and copy to destination 14 | update_and_copy() { 15 | local source_file="$1" 16 | local dest_file="$2" 17 | 18 | # Use awk to update the metadata and copy to destination 19 | awk '/^metadata:/ { 20 | print 21 | metadata_found = 1 22 | if ($0 == "metadata:") { 23 | print " labels:" 24 | print " helm.sh/chart: {{ include \"cert-management.chart\" . }}" 25 | print " app.kubernetes.io/name: {{ include \"cert-management.name\" . }}" 26 | print " app.kubernetes.io/instance: {{ .Release.Name }}" 27 | print " app.kubernetes.io/managed-by: {{ .Release.Service }}" 28 | } 29 | next 30 | } 31 | metadata_found == 1 { metadata_found = 0 } 32 | {print}' "$source_file" > "$dest_file" 33 | } 34 | 35 | # Function to add header and footer lines 36 | add_header_and_footer() { 37 | local source_file="$1" 38 | local temp_file="$(mktemp)" 39 | 40 | # Add header, original content, and footer to a temporary file 41 | { 42 | if [[ "$source_file" == *"cert.gardener.cloud_issuers.yaml" ]]; then 43 | echo '{{- if .Values.createCRDs.issuers }}' 44 | else 45 | echo '{{- if .Values.createCRDs.certificates }}' 46 | fi 47 | cat "$source_file" 48 | echo '{{- end }}' 49 | } > "$temp_file" 50 | 51 | # Move the temporary file to the original source file 52 | mv "$temp_file" "$source_file" 53 | } 54 | 55 | # Iterate through each YAML file in the source directory 56 | for source_file in "$source_dir"/*.yaml; do 57 | if [ -f "$source_file" ]; then 58 | dest_file="$destination_dir/$(basename "$source_file")" 59 | update_and_copy "$source_file" "$dest_file" 60 | add_header_and_footer "$dest_file" 61 | fi 62 | done 63 | -------------------------------------------------------------------------------- /hack/generate-code: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -e 8 | 9 | SOURCE_PATH="$(readlink -f "$(dirname ${0})/..")" 10 | 11 | PROJECT_ROOT=$(dirname $0)/.. 12 | 13 | # setup virtual GOPATH 14 | export REPO_ROOT=${REPO_ROOT} 15 | source "$GARDENER_HACK_DIR"/vgopath-setup.sh 16 | 17 | # cleanup generated files 18 | rm -f ${GOPATH}/bin/*-gen 19 | rm -rf "${SOURCE_PATH}/pkg/client/cert" 20 | 21 | CODE_GEN_DIR=$(go list -m -f '{{.Dir}}' k8s.io/code-generator) 22 | source "${CODE_GEN_DIR}/kube_codegen.sh" 23 | 24 | kube::codegen::gen_helpers \ 25 | --boilerplate "${PROJECT_ROOT}/hack/LICENSE_BOILERPLATE.txt" \ 26 | "${PROJECT_ROOT}/pkg/apis" 27 | 28 | kube::codegen::gen_helpers \ 29 | --boilerplate "${PROJECT_ROOT}/hack/LICENSE_BOILERPLATE.txt" \ 30 | --extra-peer-dir k8s.io/apimachinery/pkg/apis/meta/v1 \ 31 | --extra-peer-dir k8s.io/apimachinery/pkg/conversion \ 32 | --extra-peer-dir k8s.io/component-base/config \ 33 | --extra-peer-dir k8s.io/component-base/config/v1alpha1 \ 34 | "${PROJECT_ROOT}/pkg/certman2/apis/config" 35 | 36 | kube::codegen::gen_client \ 37 | --with-watch \ 38 | --one-input-api "cert/v1alpha1" \ 39 | --output-dir "${PROJECT_ROOT}/pkg/client/cert" \ 40 | --output-pkg "github.com/gardener/cert-management/pkg/client/cert" \ 41 | --boilerplate "${PROJECT_ROOT}/hack/LICENSE_BOILERPLATE.txt" \ 42 | "${PROJECT_ROOT}/pkg/apis" 43 | -------------------------------------------------------------------------------- /hack/generate-renovate-ignore-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | 7 | # Takes the content of a go.mod file and an array to add the extracted dependencies to. 8 | extract_dependencies() { 9 | local go_mod=$1 10 | local dependencies=$2 11 | 12 | while IFS= read -r line; do 13 | dependency=$(echo "$line" | awk '{print $1}') # Splits the line by spaces and takes the first part omitting the version and the //indirect comment. 14 | eval "$dependencies+=('$dependency')" 15 | done <<< "$go_mod" 16 | } 17 | 18 | echo "🪧 Generating ignoreDeps section for 'renovate.json5'" 19 | echo "🛜 Downloading the latest 'go.mod' from gardener/gardener..." 20 | 21 | # Only the dependencies in a `go.mod` file are indented with a tab. 22 | certman_go_mod=$(grep -P '^\t' go.mod) # Uses Perl-style regular expressions to match a tab at the beginning of a line. 23 | gardener_go_mod=$(curl -s https://raw.githubusercontent.com/gardener/gardener/refs/heads/master/go.mod | grep -P '^\t') 24 | 25 | certman_dependencies=() 26 | gardener_dependencies=() 27 | 28 | extract_dependencies "$certman_go_mod" certman_dependencies 29 | extract_dependencies "$gardener_go_mod" gardener_dependencies 30 | 31 | echo "📜 Found ${#certman_dependencies[@]} cert-manager dependencies." 32 | echo "🚜 Found ${#gardener_dependencies[@]} gardener dependencies." 33 | 34 | # Extract the intersection of the two arrays by iterating over them in a nested fashion. 35 | common_dependencies=() 36 | 37 | for certman_dependency in "${certman_dependencies[@]}"; do 38 | for gardener_dependency in "${gardener_dependencies[@]}"; do 39 | if [[ "$certman_dependency" == "$gardener_dependency" ]]; then 40 | common_dependencies+=("$certman_dependency") 41 | break # Continue with the next element of the outer loop. 42 | fi 43 | done 44 | done 45 | 46 | echo "☯️ Found ${#common_dependencies[@]} common dependencies." 47 | 48 | ignore_deps=$(printf ',"%s"' "${common_dependencies[@]}") # Add a comma to the beginning of each element and concatenate them. 49 | ignore_deps="[${ignore_deps:1}]" # Remove the leading comma and wrap the string in square brackets to format it as a JSON array. 50 | 51 | # Format the JSON array as a string, indent it, and use sed to replace the lines between the markers 52 | echo "$ignore_deps" | yq -o json '.[]' | sed 's/^/ /; s/$/,/' | sed -i -e ' / ignoreDeps: \[/, /\]/{//!d;}' -e ' / ignoreDeps: \[/r /dev/stdin' renovate.json5 53 | -------------------------------------------------------------------------------- /hack/generateChartOptions.py: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | 3 | # SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | # helper script to regenerate helm chart file: partial of charts/cert-management/templates/deployment.yaml 8 | 9 | 10 | import re 11 | import os 12 | 13 | helpFilename = "/tmp/cert-controller-manager-help.txt" 14 | rc = os.system("make build-local && ./cert-controller-manager --help | grep ' --' > {}".format(helpFilename)) 15 | if rc != 0: 16 | exit(rc) 17 | f = open(helpFilename,"r") 18 | options = f.read() 19 | os.remove(helpFilename) 20 | 21 | def toCamelCase(name): 22 | str = ''.join(x.capitalize() for x in re.split("[.-]", name)) 23 | str = str[0].lower() + str[1:] 24 | return str 25 | 26 | excluded = {"name", "help"} 27 | for line in options.split("\n"): 28 | m = re.match(r"\s+(?:-[^-]+)?--(\S+)\s", line) 29 | if m: 30 | name = m.group(1) 31 | if name != "" and not name in excluded: 32 | camelCase = toCamelCase(name) 33 | txt = """ {{- if .Values.configuration.%s }} 34 | - --%s={{ .Values.configuration.%s }} 35 | {{- end }}""" % (camelCase, name, camelCase) 36 | print(txt) 37 | 38 | print("\n\n\n") 39 | 40 | defaultValues = { 41 | "serverPortHttp": "8080" 42 | } 43 | 44 | print("configuration:") 45 | for line in options.split("\n"): 46 | m = re.match(r"\s+(?:-[^-]+)?--(\S+)\s", line) 47 | if m: 48 | name = m.group(1) 49 | if name != "" and not name in excluded: 50 | camelCase = toCamelCase(name) 51 | if camelCase in defaultValues: 52 | txt = " %s: %s" % (camelCase, defaultValues[camelCase]) 53 | else: 54 | txt = "# %s:" % camelCase 55 | print(txt) -------------------------------------------------------------------------------- /hack/go-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -e 8 | set -u 9 | set -o pipefail 10 | 11 | go test $@ | grep -v 'no test files' 12 | -------------------------------------------------------------------------------- /hack/kind/certman/certman-down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source $(dirname ${0})/../common.sh /.. 11 | 12 | kubectl delete -f ${SOURCE_PATH}/dev/manifests.yaml 13 | -------------------------------------------------------------------------------- /hack/kind/certman/issuer-down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source $(dirname ${0})/../common.sh /.. 11 | 12 | kubectl delete issuer kind-issuer 13 | -------------------------------------------------------------------------------- /hack/kind/certman/issuer-up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source $(dirname ${0})/../common.sh /.. 11 | 12 | kubectl apply -f ${SOURCE_PATH}/pkg/apis/cert/crds/cert.gardener.cloud_issuers.yaml 13 | 14 | kubectl wait crd issuers.cert.gardener.cloud --for=condition=NamesAccepted 15 | 16 | cat << EOF | kubectl apply -f - 17 | apiVersion: cert.gardener.cloud/v1alpha1 18 | kind: Issuer 19 | metadata: 20 | name: kind-issuer 21 | namespace: default 22 | spec: 23 | acme: 24 | server: https://acme.certman-support.svc.cluster.local/dir 25 | email: some.user@certman.kind 26 | autoRegistration: true 27 | precheckNameservers: 28 | - 10.96.0.10:53 # service kube-system/kube-dns (coredns) 29 | EOF 30 | -------------------------------------------------------------------------------- /hack/kind/common.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | # For the check step concourse will set the following environment variables: 8 | # SOURCE_PATH - path to component repository root directory. 9 | if [[ -z "${SOURCE_PATH}" ]]; then 10 | export SOURCE_PATH="$(readlink -f "$(dirname ${0})/../..${1}")" 11 | else 12 | export SOURCE_PATH="$(readlink -f ${SOURCE_PATH})" 13 | fi 14 | 15 | mkdir -p ${SOURCE_PATH}/dev 16 | export KUBECONFIG=${SOURCE_PATH}/dev/kind-kubeconfig.yaml -------------------------------------------------------------------------------- /hack/kind/dns-controller-manager/dns-controller-manager-up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | VERSION=v0.18.6 11 | 12 | source $(dirname ${0})/../common.sh /.. 13 | 14 | target_dir=${SOURCE_PATH}/dev/external-dns-management-${VERSION#v} 15 | 16 | download_external_dns_management_helm_charts() 17 | { 18 | if [ -d $target_dir ]; then 19 | echo "charts already at $target_dir" 20 | return 21 | fi 22 | 23 | curl -sL "https://github.com/gardener/external-dns-management/archive/refs/tags/$VERSION.tar.gz" | tar xvz -C ${SOURCE_PATH}/dev external-dns-management-${VERSION#v}/charts/external-dns-management 24 | } 25 | 26 | install_dns_controller_manager() 27 | { 28 | kubectl get ns certman-support >/dev/null 2>&1 || kubectl create ns certman-support 29 | helm template $target_dir/charts/external-dns-management -n certman-support \ 30 | --set configuration.identifier="host-$(hostname)" \ 31 | --set createCRDs=true \ 32 | --set vpa.enabled=false \ 33 | | kubectl apply -f - 34 | } 35 | 36 | download_external_dns_management_helm_charts 37 | install_dns_controller_manager -------------------------------------------------------------------------------- /hack/kind/kind-delete-cluster.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source $(dirname ${0})/common.sh '' 11 | 12 | export KUBECONFIG=${SOURCE_PATH}/dev/kind-kubeconfig.yaml 13 | 14 | kind delete cluster --name cert-management 15 | 16 | rm -f $KUBECONFIG 17 | -------------------------------------------------------------------------------- /hack/kind/knot-dns/knot-dns-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | dns.gardener.cloud/dnsnames: dns.certman.kind 6 | labels: 7 | app.kubernetes.io/instance: knot-dns 8 | app.kubernetes.io/name: knot-dns 9 | name: knot-dns 10 | namespace: certman-support 11 | spec: 12 | ports: 13 | - name: dns-tcp 14 | port: 53 15 | protocol: TCP 16 | targetPort: 53 17 | nodePort: 30053 18 | - name: dns-udp 19 | port: 53 20 | protocol: UDP 21 | targetPort: 53 22 | nodePort: 30053 23 | selector: 24 | app.kubernetes.io/instance: knot-dns 25 | app.kubernetes.io/name: knot-dns 26 | type: NodePort -------------------------------------------------------------------------------- /hack/kind/knot-dns/knot-dns-up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source $(dirname ${0})/../common.sh /.. 11 | 12 | # dummy password 13 | PASSWORD="123456" 14 | PASSWORD_BASE64=$(echo -n $PASSWORD | base64 -w0) 15 | 16 | wait_for_service() 17 | { 18 | timeout=15 19 | 20 | echo looking up IP address for knot-dns service 21 | for ((i=0; i<$timeout; i++)); do 22 | set +e 23 | SERVICE_IP_ADDRESS=$(kubectl get svc knot-dns -n certman-support '-ojsonpath={.spec.clusterIP}' 2> /dev/null) 24 | set -e 25 | if [ -n "$SERVICE_IP_ADDRESS" ]; then 26 | echo 27 | echo "knot-dns service IP address: $SERVICE_IP_ADDRESS" 28 | return 0 29 | fi 30 | echo -n . 31 | sleep 1 32 | done 33 | echo failed 34 | return 1 35 | } 36 | 37 | kubectl apply -f $(dirname ${0})/crd-dnsprovider.yaml 38 | kubectl get ns certman-support >/dev/null 2>&1 || kubectl create ns certman-support 39 | kubectl apply -f $(dirname ${0})/knot-dns-service.yaml 40 | wait_for_service 41 | kubectl apply -f <(sed -e "s/#secret-injection/$PASSWORD_BASE64/g" $(dirname ${0})/knot-dns-certman-support.yaml.template | \ 42 | sed -e "s/#server-injection/$SERVICE_IP_ADDRESS/g") 43 | 44 | # patch coredns on all clusters 45 | $(dirname ${0})/patch-coredns.sh $SERVICE_IP_ADDRESS & 46 | -------------------------------------------------------------------------------- /hack/kind/knot-dns/patch-coredns.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | knot_dns_ip=$1 8 | 9 | echo patching deployment kube-system/coredns on cluster $(kubectl config current-context) 10 | 11 | # patch configmap coredns to contain "import custom/*.override" and "import custom/*.server" 12 | corefileOrg=$(kubectl -n kube-system get cm coredns '-ojsonpath={.data.Corefile}') 13 | if ! [[ "$corefileOrg" == *"import custom/"* ]]; then 14 | tmp="${corefileOrg/%\}/}" 15 | tmp="${tmp//$'\n'/$'\n' }" 16 | cat < 0 )); then 51 | kubectl -n kube-system delete pod -l k8s-app=kube-dns 52 | else 53 | kubectl -n kube-system patch deploy coredns --patch-file $(dirname ${0})/patch-deployment-coredns.yaml 54 | fi 55 | 56 | echo "" 57 | -------------------------------------------------------------------------------- /hack/kind/knot-dns/patch-deployment-coredns.yaml: -------------------------------------------------------------------------------- 1 | spec: 2 | template: 3 | spec: 4 | containers: 5 | - name: coredns 6 | volumeMounts: 7 | - mountPath: /etc/coredns/custom 8 | name: custom-config-volume 9 | readOnly: true 10 | volumes: 11 | - configMap: 12 | defaultMode: 420 13 | name: coredns-custom 14 | optional: true 15 | name: custom-config-volume 16 | -------------------------------------------------------------------------------- /hack/kind/local-issuer/local-issuer-down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source $(dirname ${0})/../common.sh /.. 11 | 12 | kubectl delete issuer local-issuer 13 | -------------------------------------------------------------------------------- /hack/kind/local-issuer/local-issuer-up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source $(dirname ${0})/../common.sh /.. 11 | 12 | kubectl apply -f ${SOURCE_PATH}/pkg/apis/cert/crds/cert.gardener.cloud_issuers.yaml 13 | 14 | kubectl wait crd issuers.cert.gardener.cloud --for=condition=NamesAccepted 15 | 16 | cat << EOF | kubectl apply -f - 17 | apiVersion: cert.gardener.cloud/v1alpha1 18 | kind: Issuer 19 | metadata: 20 | name: local-issuer 21 | namespace: default 22 | spec: 23 | acme: 24 | server: https://localhost:5443/dir 25 | email: some.user@certman.kind 26 | autoRegistration: true 27 | precheckNameservers: 28 | - 127.0.0.1:5053 29 | EOF 30 | 31 | cat << EOF > ${SOURCE_PATH}/dev/source-lego-env.sh 32 | export LEGO_CA_CERTIFICATES=${SOURCE_PATH}/dev/pebble-cert.pem 33 | export LEGO_CA_SYSTEM_CERT_POOL=true 34 | EOF 35 | 36 | echo For running cert-controller-manager outside of the kind cluster, 37 | echo please add these environment variables: 38 | echo 39 | cat ${SOURCE_PATH}/dev/source-lego-env.sh 40 | echo 41 | echo or run "'"source ${SOURCE_PATH}/dev/source-lego-env.sh"'" -------------------------------------------------------------------------------- /hack/kind/registry/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - registry.yaml 6 | -------------------------------------------------------------------------------- /hack/kind/registry/base/registry.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: registry 6 | spec: 7 | replicas: 1 8 | strategy: 9 | type: Recreate 10 | template: 11 | spec: 12 | automountServiceAccountToken: false 13 | containers: 14 | - name: registry 15 | image: europe-docker.pkg.dev/gardener-project/releases/3rd/registry:2.8.3 16 | imagePullPolicy: IfNotPresent 17 | ports: 18 | - name: registry 19 | containerPort: 5001 20 | env: 21 | - name: REGISTRY_HTTP_ADDR 22 | value: :5001 23 | volumeMounts: 24 | - name: cache 25 | mountPath: /var/lib/registry 26 | hostNetwork: true 27 | nodeSelector: 28 | node-role.kubernetes.io/control-plane: "" 29 | tolerations: 30 | - effect: NoExecute 31 | operator: Exists 32 | - effect: NoSchedule 33 | operator: Exists 34 | volumes: 35 | - name: cache 36 | hostPath: 37 | path: /var/local-registry 38 | type: DirectoryOrCreate 39 | -------------------------------------------------------------------------------- /hack/kind/registry/docker/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | 5 | resources: 6 | - ../base 7 | 8 | patches: 9 | - patch: | 10 | - op: replace 11 | path: /metadata/name 12 | value: docker-io 13 | - op: replace 14 | path: /spec/template/spec/containers/0/env 15 | value: 16 | - name: REGISTRY_PROXY_REMOTEURL 17 | value: https://registry-1.docker.io 18 | - name: REGISTRY_HTTP_ADDR 19 | value: :5009 20 | - op: replace 21 | path: /spec/template/spec/containers/0/ports/0/containerPort 22 | value: 5009 23 | - op: replace 24 | path: /spec/template/spec/volumes/0/hostPath/path 25 | value: /var/local-registry/docker 26 | target: 27 | group: apps 28 | kind: Deployment 29 | name: registry 30 | labels: 31 | - includeSelectors: true 32 | pairs: 33 | upstream: k8s 34 | -------------------------------------------------------------------------------- /hack/kind/registry/europe-docker-pkg-dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | 5 | resources: 6 | - ../base 7 | 8 | patches: 9 | - patch: | 10 | - op: replace 11 | path: /metadata/name 12 | value: registry-europe-docker-pkg-dev 13 | - op: replace 14 | path: /spec/template/spec/containers/0/env 15 | value: 16 | - name: REGISTRY_PROXY_REMOTEURL 17 | value: https://europe-docker.pkg.dev 18 | - name: REGISTRY_HTTP_ADDR 19 | value: :5008 20 | - op: replace 21 | path: /spec/template/spec/containers/0/ports/0/containerPort 22 | value: 5008 23 | - op: replace 24 | path: /spec/template/spec/volumes/0/hostPath/path 25 | value: /var/local-registry/europe-docker-pkg-dev 26 | target: 27 | group: apps 28 | kind: Deployment 29 | name: registry 30 | labels: 31 | - includeSelectors: true 32 | pairs: 33 | upstream: europe-docker-pkg-dev 34 | -------------------------------------------------------------------------------- /hack/kind/registry/ghcr/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | 5 | resources: 6 | - ../base 7 | 8 | patches: 9 | - patch: | 10 | - op: replace 11 | path: /metadata/name 12 | value: registry-ghcr 13 | - op: replace 14 | path: /spec/template/spec/containers/0/env 15 | value: 16 | - name: REGISTRY_PROXY_REMOTEURL 17 | value: https://ghcr.io 18 | - name: REGISTRY_HTTP_ADDR 19 | value: :5005 20 | - op: replace 21 | path: /spec/template/spec/containers/0/ports/0/containerPort 22 | value: 5005 23 | - op: replace 24 | path: /spec/template/spec/volumes/0/hostPath/path 25 | value: /var/ghcr 26 | target: 27 | group: apps 28 | kind: Deployment 29 | name: registry 30 | labels: 31 | - includeSelectors: true 32 | pairs: 33 | upstream: ghcr 34 | -------------------------------------------------------------------------------- /hack/kind/registry/k8s/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | 5 | resources: 6 | - ../base 7 | 8 | patches: 9 | - patch: | 10 | - op: replace 11 | path: /metadata/name 12 | value: registry-k8s 13 | - op: replace 14 | path: /spec/template/spec/containers/0/env 15 | value: 16 | - name: REGISTRY_PROXY_REMOTEURL 17 | value: https://registry.k8s.io 18 | - name: REGISTRY_HTTP_ADDR 19 | value: :5006 20 | - op: replace 21 | path: /spec/template/spec/containers/0/ports/0/containerPort 22 | value: 5006 23 | - op: replace 24 | path: /spec/template/spec/volumes/0/hostPath/path 25 | value: /var/local-registry/k8s 26 | target: 27 | group: apps 28 | kind: Deployment 29 | name: registry 30 | labels: 31 | - includeSelectors: true 32 | pairs: 33 | upstream: k8s 34 | -------------------------------------------------------------------------------- /hack/kind/registry/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: registry 5 | 6 | resources: 7 | - namespace.yaml 8 | - docker 9 | - ghcr 10 | - k8s 11 | - europe-docker-pkg-dev 12 | labels: 13 | - includeSelectors: true 14 | pairs: 15 | app: registry 16 | -------------------------------------------------------------------------------- /hack/kind/registry/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: registry 5 | -------------------------------------------------------------------------------- /hack/kind/skaffold-after-hock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | helm template charts/cert-management -n default \ 11 | --set createCRDs.issuers=true \ 12 | --set createCRDs.certificates=true \ 13 | --set image.repository=local-skaffold/cert-controller-manager \ 14 | --set image.tag=$SKAFFOLD_IMAGE_TAG \ 15 | --set configuration.defaultIssuer=kind-issuer \ 16 | --set configuration.caCertificates="$(cat dev/pebble-cert.pem)" \ 17 | --set configuration.precheckAdditionalWait=1s \ 18 | --set configuration..configuration.issuerDefaultPoolSize=5 \ 19 | > dev/manifests.yaml 20 | 21 | helm template charts/cert-management -n default \ 22 | --set createCRDs.issuers=true \ 23 | --set createCRDs.certificates=true \ 24 | --set image.repository=local-skaffold/cert-controller-manager \ 25 | --set image.tag=$SKAFFOLD_IMAGE_TAG \ 26 | --set configuration.defaultIssuer=kind-issuer \ 27 | --set configuration.caCertificates="$(cat dev/pebble-cert.pem)" \ 28 | --set configuration.precheckAdditionalWait=1s \ 29 | --set configuration..configuration.issuerDefaultPoolSize=5 \ 30 | --set configuration.useDnsrecords=true \ 31 | > dev/manifests-dnsrecords.yaml 32 | -------------------------------------------------------------------------------- /hack/kind/skaffold-run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source "$(dirname ${0})/common.sh" '' 11 | 12 | touch "$SOURCE_PATH/dev/manifests.yaml" 13 | touch "$SOURCE_PATH/dev/manifests-dnsrecords.yaml" 14 | skaffold run "$@" 15 | -------------------------------------------------------------------------------- /hack/kind/test-functional-local.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | source "$(dirname ${0})/common.sh" '' 11 | 12 | cd "$SOURCE_PATH/test/functional" 13 | 14 | FUNCTEST_CONFIG=functest-config-kind.yaml DNS_KUBECONFIG=$KUBECONFIG DNS_DOMAIN=functest.certman.kind USE_DNSRECORDS=$USE_DNSRECORDS ginkgo --succinct 15 | -------------------------------------------------------------------------------- /hack/sast.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -e 8 | 9 | report_dir="$(git rev-parse --show-toplevel)" 10 | 11 | gosec_report="false" 12 | gosec_report_parse_flags="" 13 | exclude_dirs="hack" 14 | 15 | parse_flags() { 16 | while test $# -gt 1; do 17 | case "$1" in 18 | --gosec-report) 19 | shift; gosec_report="$1" 20 | ;; 21 | --report-dir) 22 | shift; report_dir="$1" 23 | ;; 24 | --exclude-dirs) 25 | shift; exclude_dirs="$1" 26 | ;; 27 | *) 28 | echo "Unknown argument: $1" 29 | exit 1 30 | ;; 31 | esac 32 | shift 33 | done 34 | } 35 | 36 | parse_flags "$@" 37 | 38 | echo "> Running gosec" 39 | gosec --version 40 | if [[ "$gosec_report" != "false" ]]; then 41 | echo "Exporting report to ${report_dir}/gosec-report.sarif" 42 | gosec_report_parse_flags="-track-suppressions -fmt=sarif -out=${report_dir}/gosec-report.sarif -stdout" 43 | fi 44 | 45 | # Gardener uses code-generators https://github.com/kubernetes/code-generator and https://github.com/protocolbuffers/protobuf 46 | # which create lots of G103 (CWE-242: Use of unsafe calls should be audited) & G104 (CWE-703: Errors unhandled) errors. 47 | # However, those generators are best-practice in Kubernetes environment and their results are tested well. 48 | # Thus, generated code is excluded from gosec scan. 49 | # Nested go modules are not supported by gosec (see https://github.com/securego/gosec/issues/501), so the ./hack folder 50 | # is excluded too. It does not contain productive code anyway. 51 | gosec -exclude-generated $(echo "$exclude_dirs" | awk -v RS=',' '{printf "-exclude-dir %s ", $1}') $gosec_report_parse_flags ./... 52 | -------------------------------------------------------------------------------- /hack/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | // SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 5 | // 6 | // SPDX-License-Identifier: Apache-2.0 7 | 8 | // This package imports things required by build scripts, to force `go mod` to see them as dependencies 9 | package tools 10 | 11 | import ( 12 | _ "github.com/ahmetb/gen-crd-api-reference-docs" 13 | _ "github.com/onsi/ginkgo/v2/ginkgo" 14 | _ "github.com/onsi/gomega" 15 | _ "golang.org/x/lint/golint" 16 | _ "k8s.io/code-generator" 17 | _ "k8s.io/kube-openapi/cmd/openapi-gen" 18 | _ "sigs.k8s.io/kind" 19 | 20 | _ "github.com/gardener/controller-manager-library/hack" 21 | ) 22 | -------------------------------------------------------------------------------- /pkg/apis/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - '' 5 | forbiddenPrefixes: 6 | - github.com/gardener/cert-management 7 | # TODO(marc1404): controller-manager-library references should be removed 8 | # - selectorRegexp: github[.]com/gardener/controller-manager-library 9 | # forbiddenPrefixes: 10 | # - '' 11 | -------------------------------------------------------------------------------- /pkg/apis/cert/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | //go:generate sh -c "CONTROLLER_GEN=$CONTROLLER_GEN bash $CONTROLLER_MANAGER_LIB_HACK_DIR/generate-crds" 8 | 9 | package cert 10 | -------------------------------------------------------------------------------- /pkg/apis/cert/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | // +k8s:deepcopy-gen=package,register 8 | 9 | // Package v1alpha1 is the v1alpha1 version of the API. 10 | // +groupName=cert.gardener.cloud 11 | package v1alpha1 12 | -------------------------------------------------------------------------------- /pkg/apis/cert/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package v1alpha1 8 | 9 | import ( 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "k8s.io/apimachinery/pkg/runtime" 12 | "k8s.io/apimachinery/pkg/runtime/schema" 13 | ) 14 | 15 | const ( 16 | // Version is the version of the API. 17 | Version = "v1alpha1" 18 | // GroupName is the group name of the API. 19 | GroupName = "cert.gardener.cloud" 20 | 21 | // IssuerKind is the issuer kind. 22 | IssuerKind = "Issuer" 23 | 24 | // CertificateKind is the certificate kind. 25 | CertificateKind = "Certificate" 26 | 27 | // CertificateRevocationKind is the certificate revocation kind. 28 | CertificateRevocationKind = "CertificateRevocation" 29 | ) 30 | 31 | // SchemeGroupVersion is group version used to register these objects 32 | var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: Version} 33 | 34 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 35 | func Kind(kind string) schema.GroupKind { 36 | return SchemeGroupVersion.WithKind(kind).GroupKind() 37 | } 38 | 39 | // Resource takes an unqualified resources and returns a Group qualified GroupResource 40 | func Resource(resource string) schema.GroupResource { 41 | return SchemeGroupVersion.WithResource(resource).GroupResource() 42 | } 43 | 44 | var ( 45 | // SchemeBuilder is a new Scheme Builder which registers our API. 46 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 47 | // AddToScheme is a reference to the Scheme Builder's AddToScheme function. 48 | AddToScheme = SchemeBuilder.AddToScheme 49 | ) 50 | 51 | // Adds the list of known types to Scheme. 52 | func addKnownTypes(scheme *runtime.Scheme) error { 53 | scheme.AddKnownTypes(SchemeGroupVersion, 54 | &Issuer{}, 55 | &IssuerList{}, 56 | &Certificate{}, 57 | &CertificateList{}, 58 | &CertificateRevocation{}, 59 | &CertificateRevocationList{}, 60 | ) 61 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /pkg/apis/cert/v1alpha1/state.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package v1alpha1 8 | 9 | const ( 10 | // StatePending is the pending state. 11 | StatePending = "Pending" 12 | // StateWaiting is the waiting state. 13 | StateWaiting = "Waiting" 14 | // StateError is the error state. 15 | StateError = "Error" 16 | // StateReady is the ready state. 17 | StateReady = "Ready" 18 | // StateRevoked is the revoked state. 19 | StateRevoked = "Revoked" 20 | // StateRevocationApplied is the applied state. 21 | StateRevocationApplied = "Applied" 22 | // StateRevocationPartialApplied is the partial applied state (partial success). 23 | StateRevocationPartialApplied = "PartialApplied" 24 | ) 25 | -------------------------------------------------------------------------------- /pkg/cert/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - '' 5 | forbiddenPrefixes: 6 | - github.com/gardener/cert-management/pkg/certman2 7 | -------------------------------------------------------------------------------- /pkg/cert/client/scheme.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package client 6 | 7 | import ( 8 | dnsmanv1alpha1 "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1" 9 | gardenerextensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" 10 | istionetworkingv1 "istio.io/client-go/pkg/apis/networking/v1" 11 | istionetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" 12 | istionetworkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" 13 | apiextensionsinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" 14 | "k8s.io/apimachinery/pkg/runtime" 15 | "k8s.io/apimachinery/pkg/runtime/serializer" 16 | "k8s.io/apimachinery/pkg/runtime/serializer/json" 17 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 18 | kubernetesscheme "k8s.io/client-go/kubernetes/scheme" 19 | gatewayapisv1 "sigs.k8s.io/gateway-api/apis/v1" 20 | gatewayapisv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" 21 | gatewayapisv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" 22 | 23 | certv1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 24 | ) 25 | 26 | var ( 27 | // ClusterScheme is the scheme used in garden runtime and unmanaged seed clusters. 28 | ClusterScheme = runtime.NewScheme() 29 | 30 | // ClusterSerializer is a YAML serializer using the 'ClusterScheme'. 31 | ClusterSerializer = json.NewSerializerWithOptions(json.DefaultMetaFactory, ClusterScheme, ClusterScheme, json.SerializerOptions{Yaml: true, Pretty: false, Strict: false}) 32 | // ClusterCodec is a codec factory using the 'ClusterScheme'. 33 | ClusterCodec = serializer.NewCodecFactory(ClusterScheme) 34 | ) 35 | 36 | func init() { 37 | clusterSchemeBuilder := runtime.NewSchemeBuilder( 38 | kubernetesscheme.AddToScheme, 39 | certv1alpha1.AddToScheme, 40 | dnsmanv1alpha1.AddToScheme, 41 | istionetworkingv1.AddToScheme, 42 | istionetworkingv1alpha3.AddToScheme, 43 | istionetworkingv1beta1.AddToScheme, 44 | gatewayapisv1.AddToScheme, 45 | gatewayapisv1alpha2.AddToScheme, 46 | gatewayapisv1beta1.AddToScheme, 47 | gardenerextensionsv1alpha1.AddToScheme, 48 | ) 49 | 50 | utilruntime.Must(clusterSchemeBuilder.AddToScheme(ClusterScheme)) 51 | apiextensionsinstall.Install(ClusterScheme) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/cert/source/certinfo.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package source 8 | 9 | import ( 10 | "strings" 11 | 12 | "github.com/gardener/controller-manager-library/pkg/logger" 13 | "github.com/gardener/controller-manager-library/pkg/resources" 14 | ) 15 | 16 | func (r *sourceReconciler) getCertsInfo(logger logger.LogContext, obj resources.Object, s CertSource) (*CertsInfo, CertFeedback, error) { 17 | if !r.classes.IsResponsibleFor(logger, obj) { 18 | return nil, nil, nil 19 | } 20 | info, err := s.GetCertsInfo(logger, obj.Data()) 21 | return info, s.CreateCertFeedback(logger, obj), err 22 | } 23 | 24 | // DomainsString returns all domains as comma separated string (common name and DNS names) 25 | func (info CertInfo) DomainsString() string { 26 | return DomainsString(info.Domains) 27 | } 28 | 29 | // DomainsString creates a comma separated string. 30 | func DomainsString(domains []string) string { 31 | if domains == nil { 32 | return "" 33 | } 34 | return strings.Join(domains, ",") 35 | } 36 | -------------------------------------------------------------------------------- /pkg/cert/source/slaves.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package source 8 | 9 | import ( 10 | "github.com/gardener/controller-manager-library/pkg/controllermanager/controller" 11 | "github.com/gardener/controller-manager-library/pkg/controllermanager/controller/reconcile" 12 | "github.com/gardener/controller-manager-library/pkg/controllermanager/controller/reconcile/reconcilers" 13 | "k8s.io/apimachinery/pkg/api/errors" 14 | ) 15 | 16 | // SlaveReconcilerType creates a slaveReconciler. 17 | func SlaveReconcilerType(c controller.Interface) (reconcile.Interface, error) { 18 | reconciler := &slaveReconciler{ 19 | controller: c, 20 | slaves: c.(*reconcilers.SlaveReconciler), 21 | } 22 | return reconciler, nil 23 | } 24 | 25 | type slaveReconciler struct { 26 | reconcile.DefaultReconciler 27 | controller controller.Interface 28 | slaves *reconcilers.SlaveReconciler 29 | } 30 | 31 | func (r *slaveReconciler) Start() { 32 | r.controller.Infof("determining dangling certificates...") 33 | cluster := r.controller.GetMainCluster() 34 | main := cluster.GetId() 35 | for k := range r.slaves.GetMasters(false) { 36 | if k.Cluster() == main { 37 | if _, err := cluster.GetCachedObject(k); errors.IsNotFound(err) { 38 | r.controller.Infof("trigger vanished origin %s", k.ObjectKey()) 39 | _ = r.controller.EnqueueKey(k) 40 | } else { 41 | r.controller.Debugf("found origin %s", k.ObjectKey()) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/cert/source/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package source 8 | 9 | import ( 10 | "strings" 11 | 12 | "github.com/gardener/controller-manager-library/pkg/resources" 13 | ) 14 | 15 | func requireFinalizer(src resources.Object, cluster resources.Cluster) bool { 16 | return src.GetCluster() != cluster 17 | } 18 | 19 | // ExtractSecretLabels extracts label key value map from annotation. 20 | func ExtractSecretLabels(objData resources.ObjectData) (secretLabels map[string]string) { 21 | if labels, ok := resources.GetAnnotation(objData, AnnotCertSecretLabels); ok { 22 | secretLabels = map[string]string{} 23 | for _, pair := range strings.Split(labels, ",") { 24 | pair = strings.TrimSpace(pair) 25 | items := strings.SplitN(pair, "=", 2) 26 | if len(items) == 2 { 27 | secretLabels[items[0]] = items[1] 28 | } 29 | } 30 | } 31 | return 32 | } 33 | 34 | // CopyDNSRecordsAnnotations extracts DNSRecord related annotations. 35 | func CopyDNSRecordsAnnotations(data resources.ObjectData) (annotations map[string]string) { 36 | for _, annotKey := range []string{AnnotDNSRecordProviderType, AnnotDNSRecordSecretRef, AnnotDNSRecordClass} { 37 | if value := data.GetAnnotations()[annotKey]; value != "" { 38 | if annotations == nil { 39 | annotations = map[string]string{} 40 | } 41 | annotations[annotKey] = value 42 | } 43 | } 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /pkg/cert/utils/domainrange.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package utils 8 | 9 | import "strings" 10 | 11 | // NormalizeDomainRange normalizes domain to lower case, drops wildcard and suffix dot. 12 | func NormalizeDomainRange(domainRange string) string { 13 | normalized := strings.ToLower(domainRange) 14 | if strings.HasPrefix(normalized, "*.") { 15 | normalized = normalized[1:] 16 | } 17 | normalized = strings.TrimSuffix(normalized, ".") 18 | return normalized 19 | } 20 | 21 | // IsInDomainRanges returns true if domain is in domain ranges. 22 | func IsInDomainRanges(domain string, domainRanges []string) bool { 23 | if domainRanges == nil { 24 | return true 25 | } 26 | for _, domainRange := range domainRanges { 27 | if IsInDomainRange(domain, domainRange) { 28 | return true 29 | } 30 | } 31 | return false 32 | } 33 | 34 | // BestDomainRange returns best fitting domain range value or "". 35 | func BestDomainRange(domain string, domainRanges []string) string { 36 | if domainRanges == nil { 37 | return "*" 38 | } 39 | best := "" 40 | for _, domainRange := range domainRanges { 41 | if IsInDomainRange(domain, domainRange) { 42 | if len(best) < len(domainRange) { 43 | best = domainRange 44 | } 45 | } 46 | } 47 | return best 48 | } 49 | 50 | // IsInDomainRange returns true if domain is in domain range. 51 | func IsInDomainRange(domain, domainRange string) bool { 52 | if domainRange == "" { 53 | return true 54 | } 55 | domain = strings.ToLower(domain) 56 | domain = strings.TrimSuffix(domain, ".") 57 | if !strings.HasSuffix(domain, domainRange) { 58 | return false 59 | } 60 | if len(domain) == len(domainRange) { 61 | return true 62 | } 63 | 64 | return domainRange[0] == '.' || domain[len(domain)-len(domainRange)-1] == '.' 65 | } 66 | -------------------------------------------------------------------------------- /pkg/cert/utils/domainrange_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package utils_test 8 | 9 | import ( 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | 13 | "github.com/gardener/cert-management/pkg/cert/utils" 14 | ) 15 | 16 | var _ = Describe("DomainRange", func() { 17 | DescribeTable("IsInDomainRange", 18 | func(domain, domainRange string, wanted bool) { 19 | domainRange = utils.NormalizeDomainRange(domainRange) 20 | result := utils.IsInDomainRange(domain, domainRange) 21 | Expect(result).To(Equal(wanted)) 22 | }, 23 | Entry("a.b in b", "a.b", "b", true), 24 | Entry("A.B in b", "A.B", "b", true), 25 | Entry("a.b in .b", "a.b", ".b", true), 26 | Entry("a.b in *.B", "a.b", "*.B", true), 27 | Entry("a.b in a.b", "a.b", "a.b", true), 28 | Entry("a.b not in c.b", "a.b", "c.b", false), 29 | Entry("a.b not in a.b.c", "a.b", "a.b.c", false), 30 | Entry("a.b.c in b.c", "a.b.c", "b.c", true), 31 | Entry("a.xb.c not in b.c", "a.xb.c", "b.c", false), 32 | Entry("a.b in b.", "a.b", "b.", true), 33 | Entry("a.b. in b", "a.b.", "b", true), 34 | Entry("Empty domain range acts as wildcard", "a.b.c", "", true), 35 | ) 36 | 37 | DescribeTable("IsInDomainRanges", 38 | func(domain string, domainRanges []string, wanted bool) { 39 | for i, domainRange := range domainRanges { 40 | domainRanges[i] = utils.NormalizeDomainRange(domainRange) 41 | } 42 | result := utils.IsInDomainRanges(domain, domainRanges) 43 | Expect(result).To(Equal(wanted)) 44 | }, 45 | Entry("a.b in {b}", "a.b", []string{"b"}, true), 46 | Entry("a.b in {b, c}", "a.b", []string{"b", "c"}, true), 47 | Entry("a.b in {c, b}", "a.b", []string{"c", "b"}, true), 48 | Entry("a.b not in {c, d}", "a.b", []string{"c", "d"}, false), 49 | Entry("Nil acts as wildcard", "a.b", nil, true), 50 | ) 51 | 52 | DescribeTable("BestDomainRange", 53 | func(domain string, domainRanges []string, wanted string) { 54 | for i, domainRange := range domainRanges { 55 | domainRanges[i] = utils.NormalizeDomainRange(domainRange) 56 | } 57 | result := utils.BestDomainRange(domain, domainRanges) 58 | Expect(result).To(Equal(wanted)) 59 | }, 60 | Entry("a.b in {b} returns b", "a.b", []string{"b"}, "b"), 61 | Entry("a.b in {b, c} returns b", "a.b", []string{"b", "c"}, "b"), 62 | Entry("a.b.c in {b.c., c} returns b.c", "a.b.c", []string{"b.c", "c"}, "b.c"), 63 | Entry("Nil acts as wildcard", "a.b", nil, "*"), 64 | ) 65 | 66 | }) 67 | -------------------------------------------------------------------------------- /pkg/cert/utils/mock/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | //go:generate mockgen -package=mock -destination=mocks.go github.com/gardener/controller-manager-library/pkg/resources Object 6 | 7 | package mock 8 | -------------------------------------------------------------------------------- /pkg/cert/utils/utils_issuer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package utils 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/gardener/controller-manager-library/pkg/logger" 13 | "github.com/gardener/controller-manager-library/pkg/resources" 14 | "sigs.k8s.io/controller-runtime/pkg/client" 15 | 16 | api "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 17 | "github.com/gardener/cert-management/pkg/shared/legobridge" 18 | ) 19 | 20 | var issuerType = (*api.Issuer)(nil) 21 | 22 | // IssuerObject encapsulates the issuer resource object. 23 | type IssuerObject struct { 24 | resources.Object 25 | } 26 | 27 | // Issuer returns the issuer. 28 | func (o *IssuerObject) Issuer() *api.Issuer { 29 | return o.Data().(*api.Issuer) 30 | } 31 | 32 | // Issuer returns the issuer object. 33 | func Issuer(o resources.Object) *IssuerObject { 34 | if o.IsA(issuerType) { 35 | return &IssuerObject{o} 36 | } 37 | return nil 38 | } 39 | 40 | // Spec returns the issuer resource object spec. 41 | func (o *IssuerObject) Spec() *api.IssuerSpec { 42 | return &o.Issuer().Spec 43 | } 44 | 45 | // Status returns the issuer resource object status. 46 | func (o *IssuerObject) Status() *api.IssuerStatus { 47 | return &o.Issuer().Status 48 | } 49 | 50 | // LoggerFactory is the logger factory for DNS challenges. 51 | func LoggerFactory(key client.ObjectKey, serial uint32) legobridge.LoggerInfof { 52 | return logger.NewContext("DNSChallengeProvider", fmt.Sprintf("dns-challenge-provider: %s-%d", key, serial)) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/cert/utils/utils_mod.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package utils 8 | 9 | import ( 10 | "github.com/gardener/controller-manager-library/pkg/resources/abstract" 11 | ) 12 | 13 | // AssureStringSlice handles modification of a string slice. 14 | func AssureStringSlice(mod *abstract.ModificationState, dst *[]string, value []string) { 15 | if mod == nil || dst == nil { 16 | return 17 | } 18 | if value == nil { 19 | value = []string{} 20 | } 21 | if !EqualStringSlice(*dst, value) { 22 | *dst = value 23 | mod.Modify(true) 24 | } 25 | } 26 | 27 | // EqualStringSlice compares string slices. 28 | func EqualStringSlice(a, b []string) bool { 29 | if len(a) != len(b) { 30 | return false 31 | } 32 | for i, v := range a { 33 | if v != b[i] { 34 | return false 35 | } 36 | } 37 | return true 38 | } 39 | -------------------------------------------------------------------------------- /pkg/cert/utils/utils_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package utils_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestUtils(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "Utils Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - github.com/gardener/cert-management/pkg/apis 5 | - github.com/gardener/cert-management/pkg/certman2 6 | - github.com/gardener/cert-management/pkg/shared 7 | - selectorRegexp: github[.]com/gardener/controller-manager-library 8 | forbiddenPrefixes: 9 | - '' 10 | -------------------------------------------------------------------------------- /pkg/certman2/apis/cert/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | //go:generate sh -c "CONTROLLER_GEN=$CONTROLLER_GEN bash $GARDENER_HACK_DIR/generate-crds.sh -p 'crd-' cert.gardener.cloud" 8 | 9 | package cert 10 | -------------------------------------------------------------------------------- /pkg/certman2/apis/config/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // +k8s:deepcopy-gen=package 6 | // +groupName=config.cert.gardener.cloud 7 | 8 | package config // import "github.com/gardener/cert-management/pkg/certman2/apis/config" 9 | -------------------------------------------------------------------------------- /pkg/certman2/apis/config/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package config 6 | 7 | import ( 8 | "k8s.io/apimachinery/pkg/runtime" 9 | "k8s.io/apimachinery/pkg/runtime/schema" 10 | ) 11 | 12 | // GroupName is the group name used in this package. 13 | const GroupName = "config.cert.gardener.cloud" 14 | 15 | // SchemeGroupVersion is group version used to register these objects. 16 | var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} 17 | 18 | // Kind takes an unqualified kind and returns a Group qualified GroupKind. 19 | func Kind(kind string) schema.GroupKind { 20 | return SchemeGroupVersion.WithKind(kind).GroupKind() 21 | } 22 | 23 | // Resource takes an unqualified resource and returns a Group qualified GroupResource. 24 | func Resource(resource string) schema.GroupResource { 25 | return SchemeGroupVersion.WithResource(resource).GroupResource() 26 | } 27 | 28 | var ( 29 | // SchemeBuilder used to register the Shoot resource. 30 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 31 | // AddToScheme is a pointer to SchemeBuilder.AddToScheme. 32 | AddToScheme = SchemeBuilder.AddToScheme 33 | ) 34 | 35 | // Adds the list of known types to api.Scheme. 36 | func addKnownTypes(scheme *runtime.Scheme) error { 37 | scheme.AddKnownTypes(SchemeGroupVersion, 38 | &CertManagerConfiguration{}, 39 | ) 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/certman2/apis/config/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // +k8s:deepcopy-gen=package 6 | // +k8s:conversion-gen=github.com/gardener/cert-management/pkg/certman2/apis/config 7 | // +k8s:openapi-gen=true 8 | // +k8s:defaulter-gen=TypeMeta 9 | 10 | package v1alpha1 // import "github.com/gardener/cert-management/pkg/certman2/apis/config/v1alpha1" 11 | -------------------------------------------------------------------------------- /pkg/certman2/apis/config/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package v1alpha1 6 | 7 | import ( 8 | "k8s.io/apimachinery/pkg/runtime" 9 | "k8s.io/apimachinery/pkg/runtime/schema" 10 | ) 11 | 12 | // GroupName is the group name used in this package. 13 | const GroupName = "config.cert.gardener.cloud" 14 | 15 | // SchemeGroupVersion is group version used to register these objects. 16 | var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} 17 | 18 | // Resource takes an unqualified resource and returns a Group qualified GroupResource. 19 | func Resource(resource string) schema.GroupResource { 20 | return SchemeGroupVersion.WithResource(resource).GroupResource() 21 | } 22 | 23 | var ( 24 | // SchemeBuilder used to register the Shoot resource. 25 | SchemeBuilder runtime.SchemeBuilder 26 | localSchemeBuilder = &SchemeBuilder 27 | // AddToScheme is a pointer to SchemeBuilder.AddToScheme. 28 | AddToScheme = localSchemeBuilder.AddToScheme 29 | ) 30 | 31 | func init() { 32 | // We only register manually written functions here. The registration of the 33 | // generated functions takes place in the generated files. The separation 34 | // makes the code compile even when the generated files are missing. 35 | localSchemeBuilder.Register(addDefaultingFuncs, addKnownTypes) 36 | } 37 | 38 | // Adds the list of known types to api.Scheme. 39 | func addKnownTypes(scheme *runtime.Scheme) error { 40 | scheme.AddKnownTypes(SchemeGroupVersion, 41 | &CertManagerConfiguration{}, 42 | ) 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /pkg/certman2/apis/config/v1alpha1/v1alpha1_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package v1alpha1_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestV1alpha1(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "CertManagement APIs Config V1alpha1 Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/apis/config/v1alpha1/zz_generated.defaults.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 5 | // 6 | // SPDX-License-Identifier: Apache-2.0 7 | 8 | // Code generated by defaulter-gen. DO NOT EDIT. 9 | 10 | package v1alpha1 11 | 12 | import ( 13 | runtime "k8s.io/apimachinery/pkg/runtime" 14 | ) 15 | 16 | // RegisterDefaults adds defaulters functions to the given scheme. 17 | // Public to allow building arbitrary schemes. 18 | // All generated defaulters are covering - they call all nested defaulters. 19 | func RegisterDefaults(scheme *runtime.Scheme) error { 20 | scheme.AddTypeDefaultingFunc(&CertManagerConfiguration{}, func(obj interface{}) { SetObjectDefaults_CertManagerConfiguration(obj.(*CertManagerConfiguration)) }) 21 | return nil 22 | } 23 | 24 | func SetObjectDefaults_CertManagerConfiguration(in *CertManagerConfiguration) { 25 | SetDefaults_CertManagerConfiguration(in) 26 | if in.ClientConnection != nil { 27 | SetDefaults_ClientConnection(in.ClientConnection) 28 | } 29 | if in.ControlPlaneClientConnection != nil { 30 | SetDefaults_ControlPlaneClientConnection(in.ControlPlaneClientConnection) 31 | } 32 | if in.DNSClientConnection != nil { 33 | SetDefaults_DNSClientConnection(in.DNSClientConnection) 34 | } 35 | SetDefaults_LeaderElectionConfiguration(&in.LeaderElection) 36 | SetDefaults_ServerConfiguration(&in.Server) 37 | SetDefaults_IssuerControllerConfig(&in.Controllers.Issuer) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/certman2/client/clusteraccess.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package client 6 | 7 | import ( 8 | "github.com/gardener/gardener/pkg/client/kubernetes" 9 | "github.com/go-logr/logr" 10 | "sigs.k8s.io/controller-runtime/pkg/client" 11 | ) 12 | 13 | // ClusterAccess contains clients for various connected Kubernetes clusters. 14 | type ClusterAccess struct { 15 | mainClient client.Client 16 | issuerClient client.Client 17 | dnsClient client.Client 18 | } 19 | 20 | // NewClusterAccess returns a new instance of ClusterAccess for all clusters. 21 | func NewClusterAccess(log logr.Logger, main, issuer, dns kubernetes.Interface) *ClusterAccess { 22 | if issuer == nil { 23 | issuer = main 24 | log.Info("using main cluster for provided issuers") 25 | if dns == nil { 26 | dns = main 27 | log.Info("using main cluster for DNS resources") 28 | } 29 | } else if dns == nil { 30 | dns = issuer 31 | log.Info("using issuer cluster for DNS resources") 32 | } 33 | 34 | return &ClusterAccess{ 35 | mainClient: main.Client(), 36 | issuerClient: issuer.Client(), 37 | dnsClient: dns.Client(), 38 | } 39 | } 40 | 41 | // MainClient returns client for the main cluster containing certificate and source resources. 42 | func (a *ClusterAccess) MainClient() client.Client { 43 | return a.mainClient 44 | } 45 | 46 | // IssuerClient returns client for the cluster containing provided issuers. 47 | func (a *ClusterAccess) IssuerClient() client.Client { 48 | return a.issuerClient 49 | } 50 | 51 | // DNSClient returns client for the cluster used for DNSEntries or DNSRecords. 52 | func (a *ClusterAccess) DNSClient() client.Client { 53 | return a.dnsClient 54 | } 55 | -------------------------------------------------------------------------------- /pkg/certman2/client/scheme.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package client 6 | 7 | import ( 8 | dnsmanv1alpha1 "github.com/gardener/external-dns-management/pkg/apis/dns/v1alpha1" 9 | gardenerextensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" 10 | istionetworkingv1 "istio.io/client-go/pkg/apis/networking/v1" 11 | istionetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" 12 | istionetworkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" 13 | apiextensionsinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" 14 | "k8s.io/apimachinery/pkg/runtime" 15 | "k8s.io/apimachinery/pkg/runtime/serializer" 16 | "k8s.io/apimachinery/pkg/runtime/serializer/json" 17 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 18 | kubernetesscheme "k8s.io/client-go/kubernetes/scheme" 19 | gatewayapisv1 "sigs.k8s.io/gateway-api/apis/v1" 20 | gatewayapisv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" 21 | gatewayapisv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" 22 | 23 | certmanv1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 24 | ) 25 | 26 | var ( 27 | // ClusterScheme is the scheme used in garden runtime and unmanaged seed clusters. 28 | ClusterScheme = runtime.NewScheme() 29 | 30 | // ClusterSerializer is a YAML serializer using the 'ClusterScheme'. 31 | ClusterSerializer = json.NewSerializerWithOptions(json.DefaultMetaFactory, ClusterScheme, ClusterScheme, json.SerializerOptions{Yaml: true, Pretty: false, Strict: false}) 32 | // ClusterCodec is a codec factory using the 'ClusterScheme'. 33 | ClusterCodec = serializer.NewCodecFactory(ClusterScheme) 34 | ) 35 | 36 | func init() { 37 | clusterSchemeBuilder := runtime.NewSchemeBuilder( 38 | kubernetesscheme.AddToScheme, 39 | dnsmanv1alpha1.AddToScheme, 40 | certmanv1alpha1.AddToScheme, 41 | istionetworkingv1.AddToScheme, 42 | istionetworkingv1alpha3.AddToScheme, 43 | istionetworkingv1beta1.AddToScheme, 44 | gatewayapisv1.AddToScheme, 45 | gatewayapisv1alpha2.AddToScheme, 46 | gatewayapisv1beta1.AddToScheme, 47 | gardenerextensionsv1alpha1.AddToScheme, 48 | ) 49 | 50 | utilruntime.Must(clusterSchemeBuilder.AddToScheme(ClusterScheme)) 51 | apiextensionsinstall.Install(ClusterScheme) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/certman2/controller/certificate/add.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package certificate 6 | 7 | import ( 8 | "k8s.io/utils/clock" 9 | "sigs.k8s.io/controller-runtime/pkg/builder" 10 | "sigs.k8s.io/controller-runtime/pkg/controller" 11 | "sigs.k8s.io/controller-runtime/pkg/manager" 12 | 13 | "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 14 | certcontroller "github.com/gardener/cert-management/pkg/certman2/controller" 15 | ) 16 | 17 | // ControllerName is the name of this controller. 18 | const ControllerName = "certificate" 19 | 20 | // AddToManager adds Reconciler to the given manager. 21 | func (r *Reconciler) AddToManager(mgr manager.Manager) error { 22 | r.Client = mgr.GetClient() 23 | if r.Clock == nil { 24 | r.Clock = clock.RealClock{} 25 | } 26 | if r.Recorder == nil { 27 | r.Recorder = mgr.GetEventRecorderFor(ControllerName + "-controller") 28 | } 29 | 30 | return builder. 31 | ControllerManagedBy(mgr). 32 | Named(ControllerName). 33 | For( 34 | &v1alpha1.Certificate{}, 35 | builder.WithPredicates( 36 | certcontroller.CertClassPredicate(r.Config.Class), 37 | ), 38 | ). 39 | WithOptions(controller.Options{ 40 | MaxConcurrentReconciles: 1, 41 | }). 42 | Complete(r) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/certman2/controller/certificate/certificate_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package certificate_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestLandscape(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "Certificate Controller Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/controller/certificate/reconciler.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package certificate 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | 11 | apierrors "k8s.io/apimachinery/pkg/api/errors" 12 | "k8s.io/client-go/tools/record" 13 | "k8s.io/utils/clock" 14 | "sigs.k8s.io/controller-runtime/pkg/client" 15 | logf "sigs.k8s.io/controller-runtime/pkg/log" 16 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 17 | 18 | "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 19 | "github.com/gardener/cert-management/pkg/certman2/apis/config" 20 | ) 21 | 22 | // Reconciler is a reconciler for provided Certificate resources. 23 | type Reconciler struct { 24 | Client client.Client 25 | Clock clock.Clock 26 | Recorder record.EventRecorder 27 | Config config.CertManagerConfiguration 28 | } 29 | 30 | // Reconcile reconciles Certificate resources. 31 | func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { 32 | log := logf.FromContext(ctx).WithName(ControllerName) 33 | 34 | cert := &v1alpha1.Certificate{} 35 | if err := r.Client.Get(ctx, req.NamespacedName, cert); err != nil { 36 | if apierrors.IsNotFound(err) { 37 | log.V(1).Info("Object is gone, stop reconciling") 38 | return reconcile.Result{}, nil 39 | } 40 | return reconcile.Result{}, fmt.Errorf("error retrieving object from store: %w", err) 41 | } 42 | 43 | if cert.DeletionTimestamp != nil { 44 | return r.delete(ctx, log, cert) 45 | } else { 46 | return r.reconcile(ctx, log, cert) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pkg/certman2/controller/certificate/reconciler_delete.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package certificate 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | 11 | "github.com/go-logr/logr" 12 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 13 | 14 | "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 15 | ) 16 | 17 | func (r *Reconciler) delete( 18 | _ context.Context, 19 | log logr.Logger, 20 | _ *v1alpha1.Certificate, 21 | ) ( 22 | reconcile.Result, 23 | error, 24 | ) { 25 | log.Info("deleting certificate") 26 | return reconcile.Result{}, fmt.Errorf("not yet supported") 27 | } 28 | -------------------------------------------------------------------------------- /pkg/certman2/controller/certificate/reconciler_reconcile.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package certificate 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | 11 | "github.com/go-logr/logr" 12 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 13 | 14 | "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 15 | ) 16 | 17 | func (r *Reconciler) reconcile( 18 | ctx context.Context, 19 | log logr.Logger, 20 | landscape *v1alpha1.Certificate, 21 | ) ( 22 | reconcile.Result, 23 | error, 24 | ) { 25 | log.Info("reconcile certificate") 26 | _ = ctx 27 | _ = landscape 28 | return reconcile.Result{}, fmt.Errorf("not yet supported") 29 | } 30 | -------------------------------------------------------------------------------- /pkg/certman2/controller/issuer/controlplane/acme/issuer_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package acme 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestLandscape(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "ACME Issuer Controller Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/controller/issuer/controlplane/ca/issuer_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package ca 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestLandscape(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "CA Issuer Controller Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/controller/issuer/controlplane/issuer_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package controlplane_test 8 | 9 | import ( 10 | "testing" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | ) 15 | 16 | func TestLandscape(t *testing.T) { 17 | RegisterFailHandler(Fail) 18 | RunSpecs(t, "Issuer-Control-Plane Controller Suite") 19 | } 20 | -------------------------------------------------------------------------------- /pkg/certman2/controller/predicate.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package controller 8 | 9 | import ( 10 | "sigs.k8s.io/controller-runtime/pkg/client" 11 | "sigs.k8s.io/controller-runtime/pkg/event" 12 | "sigs.k8s.io/controller-runtime/pkg/predicate" 13 | 14 | configv1alpha1 "github.com/gardener/cert-management/pkg/certman2/apis/config/v1alpha1" 15 | "github.com/gardener/cert-management/pkg/certman2/core" 16 | ) 17 | 18 | // CertClassPredicate returns a predicate that filters objects by their class. 19 | func CertClassPredicate(expectedClass string) predicate.Predicate { 20 | return FilterPredicate(func(obj client.Object) bool { 21 | class := obj.GetAnnotations()[core.AnnotationClass] 22 | if class == "" { 23 | class = configv1alpha1.DefaultClass 24 | } 25 | return class == expectedClass 26 | }) 27 | } 28 | 29 | // FilterPredicate returns a predicate that filters old or new objects. 30 | func FilterPredicate(filter func(obj client.Object) bool) predicate.Predicate { 31 | return predicate.Funcs{ 32 | CreateFunc: func(e event.CreateEvent) bool { 33 | return filter(e.Object) 34 | }, 35 | UpdateFunc: func(e event.UpdateEvent) bool { 36 | return filter(e.ObjectOld) || filter(e.ObjectNew) 37 | }, 38 | DeleteFunc: func(e event.DeleteEvent) bool { 39 | return filter(e.Object) 40 | }, 41 | GenericFunc: func(e event.GenericEvent) bool { 42 | return filter(e.Object) 43 | }, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/common/certinput_collector.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package common 8 | 9 | import ( 10 | "context" 11 | "fmt" 12 | 13 | "github.com/go-logr/logr" 14 | "sigs.k8s.io/controller-runtime/pkg/client" 15 | 16 | "github.com/gardener/cert-management/pkg/certman2/core" 17 | ) 18 | 19 | // TLSDataCollector collects TLS secret names for hosts. 20 | type TLSDataCollector func(ctx context.Context, obj client.Object) ([]*TLSData, error) 21 | 22 | // TLSData contains the collection results: secret name and host list. 23 | type TLSData struct { 24 | SecretNamespace string 25 | SecretName string 26 | Hosts []string 27 | } 28 | 29 | // GetCertInputByCollector collects data from annotations and from the resources needed for creating certificates. 30 | func GetCertInputByCollector(ctx context.Context, log logr.Logger, obj client.Object, tlsDataCollector TLSDataCollector) (CertInputMap, error) { 31 | inputMap := CertInputMap{} 32 | 33 | if obj.GetAnnotations()[AnnotationPurposeKey] != AnnotationPurposeValueManaged { 34 | return nil, nil 35 | } 36 | 37 | tlsDataArray, err := tlsDataCollector(ctx, obj) 38 | if err != nil { 39 | return inputMap, err 40 | } 41 | if tlsDataArray == nil { 42 | log.V(5).Info("No TLS data") 43 | return inputMap, nil 44 | } 45 | 46 | annotatedDomains, cn := getDomainsFromAnnotations(obj.GetAnnotations(), false) 47 | for _, tls := range tlsDataArray { 48 | if tls.SecretName == "" { 49 | err = fmt.Errorf("tls entry for hosts %s has no secretName", core.DomainsString(tls.Hosts)) 50 | continue 51 | } 52 | var domains []string 53 | if annotatedDomains != nil { 54 | domains = annotatedDomains 55 | } else { 56 | domains = mergeCommonName(cn, tls.Hosts) 57 | } 58 | key := client.ObjectKey{Namespace: tls.SecretNamespace, Name: tls.SecretName} 59 | inputMap[key] = augmentFromCommonAnnotations(obj.GetAnnotations(), CertInput{ 60 | SecretObjectKey: key, 61 | Domains: domains, 62 | }) 63 | } 64 | return inputMap, err 65 | } 66 | 67 | func mergeCommonName(cn string, hosts []string) []string { 68 | if cn == "" { 69 | return hosts 70 | } 71 | result := []string{cn} 72 | for _, host := range hosts { 73 | if host != cn { 74 | result = append(result, host) 75 | } 76 | } 77 | return result 78 | } 79 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/gateways_crd_watchdog/add_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package gateways_crd_watchdog_test 6 | 7 | import ( 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | "github.com/onsi/gomega/types" 11 | apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 12 | "sigs.k8s.io/controller-runtime/pkg/event" 13 | "sigs.k8s.io/controller-runtime/pkg/predicate" 14 | 15 | . "github.com/gardener/cert-management/pkg/certman2/controller/source/gateways_crd_watchdog" 16 | ) 17 | 18 | var _ = Describe("Add", func() { 19 | Describe("#Predicate", func() { 20 | var ( 21 | crdPredicate predicate.Predicate 22 | crd *apiextensionsv1.CustomResourceDefinition 23 | 24 | test func(*apiextensionsv1.CustomResourceDefinition, types.GomegaMatcher) 25 | ) 26 | 27 | BeforeEach(func() { 28 | crdPredicate = Predicate() 29 | 30 | crd = &apiextensionsv1.CustomResourceDefinition{} 31 | 32 | test = func( 33 | crd *apiextensionsv1.CustomResourceDefinition, 34 | match types.GomegaMatcher, 35 | ) { 36 | Expect(crdPredicate.Create(event.CreateEvent{Object: crd})).To(match) 37 | Expect(crdPredicate.Update(event.UpdateEvent{ObjectOld: crd, ObjectNew: crd})).To(match) 38 | Expect(crdPredicate.Delete(event.DeleteEvent{Object: crd})).To(match) 39 | Expect(crdPredicate.Generic(event.GenericEvent{Object: crd})).To(BeFalse()) 40 | } 41 | }) 42 | 43 | It("should handle nil objects as expected", func() { 44 | test(nil, BeFalse()) 45 | }) 46 | 47 | It("should handle unmanaged objects as expected", func() { 48 | crd.Name = "foo" 49 | test(crd, BeFalse()) 50 | }) 51 | 52 | It("should handle relevant crd", func() { 53 | for _, name := range []string{ 54 | "gateways.networking.istio.io", 55 | "virtualservices.networking.istio.io", 56 | "gateways.gateway.networking.k8s.io", 57 | "httproutes.gateway.networking.k8s.io", 58 | } { 59 | crd.Name = name 60 | test(crd, BeTrue()) 61 | } 62 | }) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/gateways_crd_watchdog/crd_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package gateways_crd_watchdog_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestLandscape(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "CustomResourceDefintion Watchdog Controller Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/ingress/ingress_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package ingress_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestLandscape(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "Ingress Source Controller Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/ingress/reconciler.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package ingress 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | 11 | networkingv1 "k8s.io/api/networking/v1" 12 | apierrors "k8s.io/apimachinery/pkg/api/errors" 13 | "k8s.io/apimachinery/pkg/runtime/schema" 14 | logf "sigs.k8s.io/controller-runtime/pkg/log" 15 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 16 | 17 | "github.com/gardener/cert-management/pkg/certman2/controller/source/common" 18 | ) 19 | 20 | // Reconciler is a reconciler for provided Certificate resources. 21 | type Reconciler struct { 22 | common.ReconcilerBase 23 | } 24 | 25 | // Complete implements the option completer. 26 | func (r *Reconciler) Complete() { 27 | r.GVK = schema.GroupVersionKind{Group: "networking.k8s.io", Version: "v1", Kind: "Ingress"} 28 | } 29 | 30 | // Reconcile reconciles Service resources. 31 | func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { 32 | log := logf.FromContext(ctx).WithName(ControllerName) 33 | 34 | ingress := &networkingv1.Ingress{} 35 | if err := r.Client.Get(ctx, req.NamespacedName, ingress); err != nil { 36 | if apierrors.IsNotFound(err) { 37 | log.V(1).Info("Object is gone, stop reconciling") 38 | return reconcile.Result{}, nil 39 | } 40 | return reconcile.Result{}, fmt.Errorf("error retrieving object from store: %w", err) 41 | } 42 | 43 | if ingress.Annotations[common.AnnotationPurposeKey] != common.AnnotationPurposeValueManaged { 44 | return r.DoDelete(ctx, log, ingress) 45 | } else { 46 | return r.reconcile(ctx, log, ingress) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/ingress/reconciler_reconcile.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package ingress 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | 11 | "github.com/go-logr/logr" 12 | corev1 "k8s.io/api/core/v1" 13 | networkingv1 "k8s.io/api/networking/v1" 14 | "sigs.k8s.io/controller-runtime/pkg/client" 15 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 16 | 17 | "github.com/gardener/cert-management/pkg/certman2/controller/source/common" 18 | ) 19 | 20 | func (r *Reconciler) reconcile( 21 | ctx context.Context, 22 | log logr.Logger, 23 | ingress *networkingv1.Ingress, 24 | ) ( 25 | reconcile.Result, 26 | error, 27 | ) { 28 | log.Info("reconcile") 29 | 30 | var certInputMap common.CertInputMap 31 | if isRelevant(ingress, r.Class) { 32 | var err error 33 | certInputMap, err = r.getCertificateInputMap(ctx, log, ingress) 34 | if err != nil { 35 | r.Recorder.Eventf(ingress, corev1.EventTypeWarning, "Invalid", "%s", err) 36 | return reconcile.Result{}, err 37 | } 38 | } 39 | 40 | return r.DoReconcile(ctx, log, ingress, certInputMap) 41 | } 42 | 43 | func (r *Reconciler) getCertificateInputMap(ctx context.Context, log logr.Logger, ingress *networkingv1.Ingress) (common.CertInputMap, error) { 44 | return common.GetCertInputByCollector(ctx, log, ingress, func(_ context.Context, obj client.Object) ([]*common.TLSData, error) { 45 | data, ok := obj.(*networkingv1.Ingress) 46 | if !ok { 47 | return nil, fmt.Errorf("unexpected ingress type: %t", obj) 48 | } 49 | if data.Spec.TLS == nil { 50 | return nil, nil 51 | } 52 | var array []*common.TLSData 53 | for _, item := range data.Spec.TLS { 54 | array = append(array, &common.TLSData{ 55 | SecretNamespace: obj.GetNamespace(), 56 | SecretName: item.SecretName, 57 | Hosts: item.Hosts, 58 | }) 59 | } 60 | return array, nil 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/istio_gateway/istio_gateway_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package istio_gateway 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestLandscape(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "Istio Gateway Source Controller Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/k8s_gateway/k8s_gateway_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package k8s_gateway 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestLandscape(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "Kubernetes Gateway Source Controller Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/k8s_gateway/versions.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package k8s_gateway 8 | 9 | import ( 10 | "strings" 11 | 12 | apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 13 | "k8s.io/apimachinery/pkg/util/sets" 14 | "sigs.k8s.io/controller-runtime/pkg/client" 15 | gatewayapisv1 "sigs.k8s.io/gateway-api/apis/v1" 16 | gatewayapisv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" 17 | ) 18 | 19 | // Version is the version of the istio gateway. 20 | type Version string 21 | 22 | const ( 23 | // VersionV1 is the v1 version of the Kubernetes Gateway API gateway. 24 | VersionV1 Version = "v1" 25 | // VersionV1beta1 is the v1beta1 version of the Kubernetes Gateway API gateway. 26 | VersionV1beta1 Version = "v1beta1" 27 | // VersionNone is zero version of the Kubernetes Gateway API gateway. 28 | VersionNone Version = "" 29 | ) 30 | 31 | // GetPreferredVersion retrieves the preferred version from the custom resource definition. 32 | func GetPreferredVersion(crd *apiextensionsv1.CustomResourceDefinition) Version { 33 | if !strings.HasSuffix(crd.GetName(), "gateway.networking.k8s.io") { 34 | return VersionNone 35 | } 36 | 37 | versions := sets.Set[string]{} 38 | for _, v := range crd.Spec.Versions { 39 | if !v.Served { 40 | continue 41 | } 42 | versions.Insert(v.Name) 43 | } 44 | for _, vv := range []Version{VersionV1, VersionV1beta1} { 45 | if versions.Has(string(vv)) { 46 | return vv 47 | } 48 | } 49 | return VersionNone 50 | } 51 | 52 | func newGateway(version Version) client.Object { 53 | switch version { 54 | case VersionV1: 55 | return &gatewayapisv1.Gateway{} 56 | case VersionV1beta1: 57 | return &gatewayapisv1beta1.Gateway{} 58 | default: 59 | return nil 60 | } 61 | } 62 | 63 | func newHTTPRoute(version Version) client.Object { 64 | switch version { 65 | case VersionV1: 66 | return &gatewayapisv1.HTTPRoute{} 67 | case VersionV1beta1: 68 | return &gatewayapisv1beta1.HTTPRoute{} 69 | default: 70 | return nil 71 | } 72 | } 73 | 74 | func newHTTPRouteList(version Version) client.ObjectList { 75 | switch version { 76 | case VersionV1: 77 | return &gatewayapisv1.HTTPRouteList{} 78 | case VersionV1beta1: 79 | return &gatewayapisv1beta1.HTTPRouteList{} 80 | default: 81 | return nil 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/service/reconciler.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package service 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | 11 | corev1 "k8s.io/api/core/v1" 12 | apierrors "k8s.io/apimachinery/pkg/api/errors" 13 | "k8s.io/apimachinery/pkg/runtime/schema" 14 | logf "sigs.k8s.io/controller-runtime/pkg/log" 15 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 16 | 17 | "github.com/gardener/cert-management/pkg/certman2/controller/source/common" 18 | ) 19 | 20 | // Reconciler is a reconciler for provided Certificate resources. 21 | type Reconciler struct { 22 | common.ReconcilerBase 23 | } 24 | 25 | // Complete implements the option completer. 26 | func (r *Reconciler) Complete() { 27 | r.GVK = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"} 28 | } 29 | 30 | // Reconcile reconciles Service resources. 31 | func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { 32 | log := logf.FromContext(ctx).WithName(ControllerName) 33 | 34 | service := &corev1.Service{} 35 | if err := r.Client.Get(ctx, req.NamespacedName, service); err != nil { 36 | if apierrors.IsNotFound(err) { 37 | log.V(1).Info("Object is gone, stop reconciling") 38 | return reconcile.Result{}, nil 39 | } 40 | return reconcile.Result{}, fmt.Errorf("error retrieving object from store: %w", err) 41 | } 42 | 43 | if service.Spec.Type != corev1.ServiceTypeLoadBalancer { 44 | return r.DoDelete(ctx, log, service) 45 | } else { 46 | return r.reconcile(ctx, log, service) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/service/reconciler_reconcile.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package service 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | 11 | "github.com/go-logr/logr" 12 | corev1 "k8s.io/api/core/v1" 13 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 14 | 15 | "github.com/gardener/cert-management/pkg/certman2/controller/source/common" 16 | ) 17 | 18 | func (r *Reconciler) reconcile( 19 | ctx context.Context, 20 | log logr.Logger, 21 | service *corev1.Service, 22 | ) ( 23 | reconcile.Result, 24 | error, 25 | ) { 26 | log.Info("reconcile") 27 | 28 | var certInputMap common.CertInputMap 29 | if isRelevant(service, r.Class) { 30 | // build certificate from service annotations 31 | var err error 32 | certInputMap, err = r.getCertificateInputMap(log, service) 33 | if err != nil { 34 | r.Recorder.Eventf(service, corev1.EventTypeWarning, "Invalid", "%s", err) 35 | return reconcile.Result{}, err 36 | } 37 | } 38 | 39 | return r.DoReconcile(ctx, log, service, certInputMap) 40 | } 41 | 42 | func (r *Reconciler) getCertificateInputMap(log logr.Logger, service *corev1.Service) (common.CertInputMap, error) { 43 | inputMap, err := common.GetCertSourceSpecForService(log, service) 44 | if err != nil { 45 | return nil, err 46 | } 47 | if len(inputMap) > 1 { 48 | return nil, fmt.Errorf("expected one certificate source, found %d", len(inputMap)) 49 | } 50 | return inputMap, nil 51 | } 52 | -------------------------------------------------------------------------------- /pkg/certman2/controller/source/service/service_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package service_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestLandscape(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "Service Source Controller Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/core/const.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | const ( 10 | // ACMEType is the type name for ACME. 11 | ACMEType = "acme" 12 | // CAType is the type name for CA. 13 | CAType = "ca" 14 | ) 15 | 16 | const ( 17 | // AnnotationClass is the annotation to set the cert class. 18 | AnnotationClass = "cert.gardener.cloud/class" 19 | ) 20 | -------------------------------------------------------------------------------- /pkg/certman2/core/issuerDNSSelections.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | "sync" 11 | 12 | "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 13 | ) 14 | 15 | // NewIssuerDNSSelections creates an IssuerDNSSelections 16 | func NewIssuerDNSSelections() *IssuerDNSSelections { 17 | return &IssuerDNSSelections{ 18 | selections: map[IssuerKey]*v1alpha1.DNSSelection{}, 19 | } 20 | } 21 | 22 | // IssuerDNSSelections stores last known DNS selection for an issuer 23 | type IssuerDNSSelections struct { 24 | lock sync.Mutex 25 | selections map[IssuerKey]*v1alpha1.DNSSelection 26 | } 27 | 28 | // Add adds a DNS selection 29 | func (s *IssuerDNSSelections) Add(key IssuerKey, sel *v1alpha1.DNSSelection) { 30 | s.lock.Lock() 31 | defer s.lock.Unlock() 32 | 33 | s.selections[key] = sel 34 | } 35 | 36 | // Remove removes a DNS selection 37 | func (s *IssuerDNSSelections) Remove(key IssuerKey) { 38 | s.lock.Lock() 39 | defer s.lock.Unlock() 40 | 41 | delete(s.selections, key) 42 | } 43 | 44 | // GetSelection returns the selection for the given key. 45 | func (s *IssuerDNSSelections) GetSelection(key IssuerKey) *v1alpha1.DNSSelection { 46 | s.lock.Lock() 47 | defer s.lock.Unlock() 48 | 49 | return s.selections[key] 50 | } 51 | 52 | // Issuers returns all issuer keys. 53 | func (s *IssuerDNSSelections) Issuers() []IssuerKey { 54 | s.lock.Lock() 55 | defer s.lock.Unlock() 56 | 57 | keys := []IssuerKey{} 58 | for key := range s.selections { 59 | keys = append(keys, key) 60 | } 61 | return keys 62 | } 63 | 64 | // GetAll returns a map with all selections 65 | func (s *IssuerDNSSelections) GetAll() map[IssuerKey]*v1alpha1.DNSSelection { 66 | s.lock.Lock() 67 | defer s.lock.Unlock() 68 | 69 | result := map[IssuerKey]*v1alpha1.DNSSelection{} 70 | for k, v := range s.selections { 71 | result[k] = v 72 | } 73 | return result 74 | } 75 | -------------------------------------------------------------------------------- /pkg/certman2/core/keys.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | corev1 "k8s.io/api/core/v1" 11 | "sigs.k8s.io/controller-runtime/pkg/client" 12 | 13 | "github.com/gardener/cert-management/pkg/shared" 14 | ) 15 | 16 | // IssuerKey provides object key and cluster of an issuer. 17 | type IssuerKey struct { 18 | client.ObjectKey 19 | secondary bool 20 | } 21 | 22 | // Name provides issuer name. 23 | func (k IssuerKey) Name() string { 24 | return k.ObjectKey.Name 25 | } 26 | 27 | // Namespace provides issuer namespace. 28 | func (k IssuerKey) Namespace() string { 29 | return k.ObjectKey.Namespace 30 | } 31 | 32 | // Cluster provides cluster (from CML). 33 | func (k IssuerKey) Cluster() shared.Cluster { 34 | if k.secondary { 35 | return shared.ClusterDefault 36 | } 37 | return shared.ClusterTarget 38 | } 39 | 40 | // Secondary returns true if issuer is from secondary cluster. 41 | func (k IssuerKey) Secondary() bool { 42 | return k.secondary 43 | } 44 | 45 | func (k IssuerKey) String() string { 46 | if k.secondary { 47 | return k.ObjectKey.Name 48 | } 49 | return "target:" + k.ObjectKey.String() 50 | } 51 | 52 | var _ shared.IssuerKeyItf = IssuerKey{} 53 | 54 | // NewIssuerKey creates key for an issuer. 55 | func NewIssuerKey(key client.ObjectKey, secondary bool) IssuerKey { 56 | return IssuerKey{ObjectKey: key, secondary: secondary} 57 | } 58 | 59 | // SecretKey provides object key and cluster of a secret 60 | type SecretKey struct { 61 | client.ObjectKey 62 | secondary bool 63 | } 64 | 65 | // NewSecretKey creates key for a secret. 66 | func NewSecretKey(key client.ObjectKey, secondary bool) SecretKey { 67 | return SecretKey{ObjectKey: key, secondary: secondary} 68 | } 69 | 70 | // IsFromSecondaryCluster returns true if secret is from secondary cluster. 71 | func (k SecretKey) IsFromSecondaryCluster() bool { 72 | return k.secondary 73 | } 74 | 75 | // ObjectKeyFromSecretReference returns an ObjectKey for a secret reference. 76 | func ObjectKeyFromSecretReference(secretRef *corev1.SecretReference) client.ObjectKey { 77 | return client.ObjectKey{Namespace: secretRef.Namespace, Name: secretRef.Name} 78 | } 79 | -------------------------------------------------------------------------------- /pkg/certman2/core/objectnameset.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | "sort" 11 | "strings" 12 | "sync" 13 | 14 | "k8s.io/apimachinery/pkg/util/sets" 15 | "sigs.k8s.io/controller-runtime/pkg/client" 16 | ) 17 | 18 | // newObjectKeySet creates a synced Set[client.ObjectKey] 19 | func newObjectKeySet() *objectKeySet { 20 | return &objectKeySet{ 21 | set: sets.Set[client.ObjectKey]{}, 22 | } 23 | } 24 | 25 | // objectKeySet is a synced ObjectNameSet. 26 | type objectKeySet struct { 27 | lock sync.Mutex 28 | set sets.Set[client.ObjectKey] 29 | } 30 | 31 | // Add a name. 32 | func (s *objectKeySet) Add(key client.ObjectKey) bool { 33 | s.lock.Lock() 34 | defer s.lock.Unlock() 35 | 36 | old := len(s.set) 37 | s.set.Insert(key) 38 | return len(s.set) != old 39 | } 40 | 41 | // Remove a name. 42 | func (s *objectKeySet) Remove(key client.ObjectKey) bool { 43 | s.lock.Lock() 44 | defer s.lock.Unlock() 45 | 46 | oldLen := len(s.set) 47 | return len(s.set.Delete(key)) != oldLen 48 | } 49 | 50 | // UnsortedList returns copy of members as array 51 | func (s *objectKeySet) UnsortedList() []client.ObjectKey { 52 | s.lock.Lock() 53 | defer s.lock.Unlock() 54 | 55 | return s.set.UnsortedList() 56 | } 57 | 58 | func (s *objectKeySet) String() string { 59 | s.lock.Lock() 60 | defer s.lock.Unlock() 61 | 62 | array := s.UnsortedList() 63 | sort.Slice(array, func(i, j int) bool { 64 | if array[i].Namespace < array[j].Namespace { 65 | return true 66 | } else if array[i].Namespace > array[j].Namespace { 67 | return false 68 | } else { 69 | return array[i].Name < array[j].Name 70 | } 71 | }) 72 | strs := make([]string, len(array)) 73 | for i, obj := range array { 74 | strs[i] = obj.String() 75 | } 76 | return strings.Join(strs, ",") 77 | } 78 | 79 | func (s *objectKeySet) Size() int { 80 | s.lock.Lock() 81 | defer s.lock.Unlock() 82 | 83 | return len(s.set) 84 | } 85 | -------------------------------------------------------------------------------- /pkg/certman2/core/quotas.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | "sync" 11 | 12 | "k8s.io/client-go/util/flowcontrol" 13 | ) 14 | 15 | // NewQuotas create a Quotas 16 | func NewQuotas() *Quotas { 17 | return &Quotas{ 18 | issuerToQuotas: map[IssuerKey]quotas{}, 19 | } 20 | } 21 | 22 | type quotas struct { 23 | rateLimiter flowcontrol.RateLimiter 24 | requestsPerDay int 25 | } 26 | 27 | // Quotas stores references issuer quotas. 28 | type Quotas struct { 29 | lock sync.Mutex 30 | issuerToQuotas map[IssuerKey]quotas 31 | } 32 | 33 | // RememberQuotas stores the requests per days quota and creates a new ratelimiter if the quota changed. 34 | func (q *Quotas) RememberQuotas(issuerKey IssuerKey, requestsPerDay int) { 35 | q.lock.Lock() 36 | defer q.lock.Unlock() 37 | 38 | if quotas, ok := q.issuerToQuotas[issuerKey]; ok { 39 | if quotas.requestsPerDay == requestsPerDay { 40 | return 41 | } 42 | } 43 | 44 | qps := float32(requestsPerDay) / 86400 45 | burst := requestsPerDay / 4 46 | if burst < 1 { 47 | burst = 1 48 | } 49 | 50 | q.issuerToQuotas[issuerKey] = quotas{ 51 | rateLimiter: flowcontrol.NewTokenBucketRateLimiter(qps, burst), 52 | requestsPerDay: requestsPerDay, 53 | } 54 | } 55 | 56 | // TryAccept tries to accept a certificate request according to the quotas. 57 | // Returns true if accepted and the requests per days quota value 58 | func (q *Quotas) TryAccept(issuerKey IssuerKey) (bool, int) { 59 | q.lock.Lock() 60 | defer q.lock.Unlock() 61 | 62 | if quotas, ok := q.issuerToQuotas[issuerKey]; ok { 63 | return quotas.rateLimiter.TryAccept(), quotas.requestsPerDay 64 | } 65 | return false, 0 66 | } 67 | 68 | // RemoveIssuer removes all secretRefs for an issuer. 69 | func (q *Quotas) RemoveIssuer(issuerKey IssuerKey) { 70 | q.lock.Lock() 71 | defer q.lock.Unlock() 72 | 73 | delete(q.issuerToQuotas, issuerKey) 74 | } 75 | 76 | // RequestsPerDay gets the request per day quota 77 | func (q *Quotas) RequestsPerDay(issuerName IssuerKey) int { 78 | q.lock.Lock() 79 | defer q.lock.Unlock() 80 | 81 | quotas, ok := q.issuerToQuotas[issuerName] 82 | if !ok { 83 | return 0 84 | } 85 | return quotas.requestsPerDay 86 | } 87 | -------------------------------------------------------------------------------- /pkg/certman2/core/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import "strings" 10 | 11 | // DomainsString creates a comma separated string. 12 | func DomainsString(domains []string) string { 13 | if domains == nil { 14 | return "" 15 | } 16 | return strings.Join(domains, ",") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/certman2/core/wrappedregistration.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | "encoding/json" 11 | 12 | "github.com/go-acme/lego/v4/registration" 13 | "k8s.io/apimachinery/pkg/runtime" 14 | ) 15 | 16 | type wrappedRegistration struct { 17 | registration.Resource `json:",inline"` 18 | SecretHash *string `json:"secretHash,omitempty"` 19 | } 20 | 21 | // WrapRegistration wraps registration. 22 | func WrapRegistration(raw []byte, secretHash string) ([]byte, error) { 23 | reg := &wrappedRegistration{} 24 | err := json.Unmarshal(raw, reg) 25 | if err != nil { 26 | return nil, err 27 | } 28 | reg.SecretHash = &secretHash 29 | return json.Marshal(®) 30 | } 31 | 32 | // WrapRegistrationFromResource unmarshalls a wrapped registration. 33 | func WrapRegistrationFromResource(raw []byte) (*wrappedRegistration, error) { 34 | reg := &wrappedRegistration{} 35 | err := json.Unmarshal(raw, reg) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return reg, nil 40 | } 41 | 42 | // IsSameExistingRegistration returns true if status ACME has same secret hash. 43 | func IsSameExistingRegistration(raw *runtime.RawExtension, realSecretHash string) bool { 44 | if raw == nil || raw.Raw == nil { 45 | return false 46 | } 47 | reg := &wrappedRegistration{} 48 | if err := json.Unmarshal(raw.Raw, reg); err == nil && reg.SecretHash != nil { 49 | return *reg.SecretHash == realSecretHash 50 | } 51 | return false 52 | } 53 | -------------------------------------------------------------------------------- /pkg/certman2/testutils/assert_events.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package testutils 8 | 9 | import ( 10 | "fmt" 11 | "strings" 12 | "time" 13 | 14 | . "github.com/onsi/ginkgo/v2" 15 | . "github.com/onsi/gomega" 16 | ) 17 | 18 | // AssertEvents checks for expected events (in the given order). 19 | func AssertEvents(actual <-chan string, expectedList ...string) { 20 | c := time.After(1 * time.Second) 21 | for _, e := range expectedList { 22 | select { 23 | case a := <-actual: 24 | if !strings.HasPrefix(a, e) { 25 | Expect(a).To(ContainSubstring(e)) 26 | return 27 | } 28 | case <-c: 29 | Fail(fmt.Sprintf("Expected event %q, got nothing", e)) 30 | // continue iterating to print all expected events 31 | } 32 | } 33 | for { 34 | select { 35 | case a := <-actual: 36 | Fail(fmt.Sprintf("Unexpected event: %q", a)) 37 | default: 38 | return // No more events, as expected. 39 | } 40 | } 41 | } 42 | 43 | // AssertUnorderedEvents checks for expected events (any order). 44 | func AssertUnorderedEvents(actual <-chan string, expectedList ...string) { 45 | c := time.After(1 * time.Second) 46 | var actualList []string 47 | for _, e := range expectedList { 48 | select { 49 | case a := <-actual: 50 | actualList = append(actualList, a) 51 | case <-c: 52 | Fail(fmt.Sprintf("Expected event %q, got nothing", e)) 53 | // continue iterating to print all expected events 54 | } 55 | } 56 | outer: 57 | for { 58 | select { 59 | case a := <-actual: 60 | actualList = append(actualList, a) 61 | default: 62 | break outer // No more events, as expected. 63 | } 64 | } 65 | 66 | missing := []string{} 67 | outer2: 68 | for _, e := range expectedList { 69 | for i, a := range actualList { 70 | if strings.HasPrefix(a, e) { 71 | actualList = append(actualList[:i], actualList[i+1:]...) 72 | continue outer2 73 | } 74 | } 75 | missing = append(missing, e) 76 | } 77 | // show mismatches 78 | Expect(missing).To(Equal(actualList)) 79 | } 80 | -------------------------------------------------------------------------------- /pkg/client/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - '' 5 | forbiddenPrefixes: 6 | - github.com/gardener/cert-management/pkg/certman2 7 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | // This package has the automatically generated fake clientset. 8 | package fake 9 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | package fake 8 | 9 | import ( 10 | certv1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 11 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | runtime "k8s.io/apimachinery/pkg/runtime" 13 | schema "k8s.io/apimachinery/pkg/runtime/schema" 14 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 15 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 16 | ) 17 | 18 | var scheme = runtime.NewScheme() 19 | var codecs = serializer.NewCodecFactory(scheme) 20 | 21 | var localSchemeBuilder = runtime.SchemeBuilder{ 22 | certv1alpha1.AddToScheme, 23 | } 24 | 25 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 26 | // of clientsets, like in: 27 | // 28 | // import ( 29 | // "k8s.io/client-go/kubernetes" 30 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 31 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 32 | // ) 33 | // 34 | // kclientset, _ := kubernetes.NewForConfig(c) 35 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 36 | // 37 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 38 | // correctly. 39 | var AddToScheme = localSchemeBuilder.AddToScheme 40 | 41 | func init() { 42 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 43 | utilruntime.Must(AddToScheme(scheme)) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | // This package contains the scheme of the automatically generated clientset. 8 | package scheme 9 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | package scheme 8 | 9 | import ( 10 | certv1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 11 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | runtime "k8s.io/apimachinery/pkg/runtime" 13 | schema "k8s.io/apimachinery/pkg/runtime/schema" 14 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 15 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 16 | ) 17 | 18 | var Scheme = runtime.NewScheme() 19 | var Codecs = serializer.NewCodecFactory(Scheme) 20 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 21 | var localSchemeBuilder = runtime.SchemeBuilder{ 22 | certv1alpha1.AddToScheme, 23 | } 24 | 25 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 26 | // of clientsets, like in: 27 | // 28 | // import ( 29 | // "k8s.io/client-go/kubernetes" 30 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 31 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 32 | // ) 33 | // 34 | // kclientset, _ := kubernetes.NewForConfig(c) 35 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 36 | // 37 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 38 | // correctly. 39 | var AddToScheme = localSchemeBuilder.AddToScheme 40 | 41 | func init() { 42 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 43 | utilruntime.Must(AddToScheme(Scheme)) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/typed/cert/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | // This package has the automatically generated typed clients. 8 | package v1alpha1 9 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/typed/cert/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | // Package fake has the automatically generated clients. 8 | package fake 9 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/typed/cert/v1alpha1/fake/fake_cert_client.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | package fake 8 | 9 | import ( 10 | v1alpha1 "github.com/gardener/cert-management/pkg/client/cert/clientset/versioned/typed/cert/v1alpha1" 11 | rest "k8s.io/client-go/rest" 12 | testing "k8s.io/client-go/testing" 13 | ) 14 | 15 | type FakeCertV1alpha1 struct { 16 | *testing.Fake 17 | } 18 | 19 | func (c *FakeCertV1alpha1) Certificates(namespace string) v1alpha1.CertificateInterface { 20 | return newFakeCertificates(c, namespace) 21 | } 22 | 23 | func (c *FakeCertV1alpha1) CertificateRevocations(namespace string) v1alpha1.CertificateRevocationInterface { 24 | return newFakeCertificateRevocations(c, namespace) 25 | } 26 | 27 | func (c *FakeCertV1alpha1) Issuers(namespace string) v1alpha1.IssuerInterface { 28 | return newFakeIssuers(c, namespace) 29 | } 30 | 31 | // RESTClient returns a RESTClient that is used to communicate 32 | // with API server by this client implementation. 33 | func (c *FakeCertV1alpha1) RESTClient() rest.Interface { 34 | var ret *rest.RESTClient 35 | return ret 36 | } 37 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/typed/cert/v1alpha1/fake/fake_certificate.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | package fake 8 | 9 | import ( 10 | v1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 11 | certv1alpha1 "github.com/gardener/cert-management/pkg/client/cert/clientset/versioned/typed/cert/v1alpha1" 12 | gentype "k8s.io/client-go/gentype" 13 | ) 14 | 15 | // fakeCertificates implements CertificateInterface 16 | type fakeCertificates struct { 17 | *gentype.FakeClientWithList[*v1alpha1.Certificate, *v1alpha1.CertificateList] 18 | Fake *FakeCertV1alpha1 19 | } 20 | 21 | func newFakeCertificates(fake *FakeCertV1alpha1, namespace string) certv1alpha1.CertificateInterface { 22 | return &fakeCertificates{ 23 | gentype.NewFakeClientWithList[*v1alpha1.Certificate, *v1alpha1.CertificateList]( 24 | fake.Fake, 25 | namespace, 26 | v1alpha1.SchemeGroupVersion.WithResource("certificates"), 27 | v1alpha1.SchemeGroupVersion.WithKind("Certificate"), 28 | func() *v1alpha1.Certificate { return &v1alpha1.Certificate{} }, 29 | func() *v1alpha1.CertificateList { return &v1alpha1.CertificateList{} }, 30 | func(dst, src *v1alpha1.CertificateList) { dst.ListMeta = src.ListMeta }, 31 | func(list *v1alpha1.CertificateList) []*v1alpha1.Certificate { 32 | return gentype.ToPointerSlice(list.Items) 33 | }, 34 | func(list *v1alpha1.CertificateList, items []*v1alpha1.Certificate) { 35 | list.Items = gentype.FromPointerSlice(items) 36 | }, 37 | ), 38 | fake, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/typed/cert/v1alpha1/fake/fake_certificaterevocation.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | package fake 8 | 9 | import ( 10 | v1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 11 | certv1alpha1 "github.com/gardener/cert-management/pkg/client/cert/clientset/versioned/typed/cert/v1alpha1" 12 | gentype "k8s.io/client-go/gentype" 13 | ) 14 | 15 | // fakeCertificateRevocations implements CertificateRevocationInterface 16 | type fakeCertificateRevocations struct { 17 | *gentype.FakeClientWithList[*v1alpha1.CertificateRevocation, *v1alpha1.CertificateRevocationList] 18 | Fake *FakeCertV1alpha1 19 | } 20 | 21 | func newFakeCertificateRevocations(fake *FakeCertV1alpha1, namespace string) certv1alpha1.CertificateRevocationInterface { 22 | return &fakeCertificateRevocations{ 23 | gentype.NewFakeClientWithList[*v1alpha1.CertificateRevocation, *v1alpha1.CertificateRevocationList]( 24 | fake.Fake, 25 | namespace, 26 | v1alpha1.SchemeGroupVersion.WithResource("certificaterevocations"), 27 | v1alpha1.SchemeGroupVersion.WithKind("CertificateRevocation"), 28 | func() *v1alpha1.CertificateRevocation { return &v1alpha1.CertificateRevocation{} }, 29 | func() *v1alpha1.CertificateRevocationList { return &v1alpha1.CertificateRevocationList{} }, 30 | func(dst, src *v1alpha1.CertificateRevocationList) { dst.ListMeta = src.ListMeta }, 31 | func(list *v1alpha1.CertificateRevocationList) []*v1alpha1.CertificateRevocation { 32 | return gentype.ToPointerSlice(list.Items) 33 | }, 34 | func(list *v1alpha1.CertificateRevocationList, items []*v1alpha1.CertificateRevocation) { 35 | list.Items = gentype.FromPointerSlice(items) 36 | }, 37 | ), 38 | fake, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/typed/cert/v1alpha1/fake/fake_issuer.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | package fake 8 | 9 | import ( 10 | v1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 11 | certv1alpha1 "github.com/gardener/cert-management/pkg/client/cert/clientset/versioned/typed/cert/v1alpha1" 12 | gentype "k8s.io/client-go/gentype" 13 | ) 14 | 15 | // fakeIssuers implements IssuerInterface 16 | type fakeIssuers struct { 17 | *gentype.FakeClientWithList[*v1alpha1.Issuer, *v1alpha1.IssuerList] 18 | Fake *FakeCertV1alpha1 19 | } 20 | 21 | func newFakeIssuers(fake *FakeCertV1alpha1, namespace string) certv1alpha1.IssuerInterface { 22 | return &fakeIssuers{ 23 | gentype.NewFakeClientWithList[*v1alpha1.Issuer, *v1alpha1.IssuerList]( 24 | fake.Fake, 25 | namespace, 26 | v1alpha1.SchemeGroupVersion.WithResource("issuers"), 27 | v1alpha1.SchemeGroupVersion.WithKind("Issuer"), 28 | func() *v1alpha1.Issuer { return &v1alpha1.Issuer{} }, 29 | func() *v1alpha1.IssuerList { return &v1alpha1.IssuerList{} }, 30 | func(dst, src *v1alpha1.IssuerList) { dst.ListMeta = src.ListMeta }, 31 | func(list *v1alpha1.IssuerList) []*v1alpha1.Issuer { return gentype.ToPointerSlice(list.Items) }, 32 | func(list *v1alpha1.IssuerList, items []*v1alpha1.Issuer) { 33 | list.Items = gentype.FromPointerSlice(items) 34 | }, 35 | ), 36 | fake, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pkg/client/cert/clientset/versioned/typed/cert/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by client-gen. DO NOT EDIT. 6 | 7 | package v1alpha1 8 | 9 | type CertificateExpansion interface{} 10 | 11 | type CertificateRevocationExpansion interface{} 12 | 13 | type IssuerExpansion interface{} 14 | -------------------------------------------------------------------------------- /pkg/client/cert/informers/externalversions/cert/interface.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by informer-gen. DO NOT EDIT. 6 | 7 | package cert 8 | 9 | import ( 10 | v1alpha1 "github.com/gardener/cert-management/pkg/client/cert/informers/externalversions/cert/v1alpha1" 11 | internalinterfaces "github.com/gardener/cert-management/pkg/client/cert/informers/externalversions/internalinterfaces" 12 | ) 13 | 14 | // Interface provides access to each of this group's versions. 15 | type Interface interface { 16 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 17 | V1alpha1() v1alpha1.Interface 18 | } 19 | 20 | type group struct { 21 | factory internalinterfaces.SharedInformerFactory 22 | namespace string 23 | tweakListOptions internalinterfaces.TweakListOptionsFunc 24 | } 25 | 26 | // New returns a new Interface. 27 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 28 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 29 | } 30 | 31 | // V1alpha1 returns a new v1alpha1.Interface. 32 | func (g *group) V1alpha1() v1alpha1.Interface { 33 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/client/cert/informers/externalversions/cert/v1alpha1/interface.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by informer-gen. DO NOT EDIT. 6 | 7 | package v1alpha1 8 | 9 | import ( 10 | internalinterfaces "github.com/gardener/cert-management/pkg/client/cert/informers/externalversions/internalinterfaces" 11 | ) 12 | 13 | // Interface provides access to all the informers in this group version. 14 | type Interface interface { 15 | // Certificates returns a CertificateInformer. 16 | Certificates() CertificateInformer 17 | // CertificateRevocations returns a CertificateRevocationInformer. 18 | CertificateRevocations() CertificateRevocationInformer 19 | // Issuers returns a IssuerInformer. 20 | Issuers() IssuerInformer 21 | } 22 | 23 | type version struct { 24 | factory internalinterfaces.SharedInformerFactory 25 | namespace string 26 | tweakListOptions internalinterfaces.TweakListOptionsFunc 27 | } 28 | 29 | // New returns a new Interface. 30 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 31 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 32 | } 33 | 34 | // Certificates returns a CertificateInformer. 35 | func (v *version) Certificates() CertificateInformer { 36 | return &certificateInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 37 | } 38 | 39 | // CertificateRevocations returns a CertificateRevocationInformer. 40 | func (v *version) CertificateRevocations() CertificateRevocationInformer { 41 | return &certificateRevocationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 42 | } 43 | 44 | // Issuers returns a IssuerInformer. 45 | func (v *version) Issuers() IssuerInformer { 46 | return &issuerInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 47 | } 48 | -------------------------------------------------------------------------------- /pkg/client/cert/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by informer-gen. DO NOT EDIT. 6 | 7 | package externalversions 8 | 9 | import ( 10 | fmt "fmt" 11 | 12 | v1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 13 | schema "k8s.io/apimachinery/pkg/runtime/schema" 14 | cache "k8s.io/client-go/tools/cache" 15 | ) 16 | 17 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 18 | // sharedInformers based on type 19 | type GenericInformer interface { 20 | Informer() cache.SharedIndexInformer 21 | Lister() cache.GenericLister 22 | } 23 | 24 | type genericInformer struct { 25 | informer cache.SharedIndexInformer 26 | resource schema.GroupResource 27 | } 28 | 29 | // Informer returns the SharedIndexInformer. 30 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 31 | return f.informer 32 | } 33 | 34 | // Lister returns the GenericLister. 35 | func (f *genericInformer) Lister() cache.GenericLister { 36 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 37 | } 38 | 39 | // ForResource gives generic access to a shared informer of the matching type 40 | // TODO extend this to unknown resources with a client pool 41 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 42 | switch resource { 43 | // Group=cert.gardener.cloud, Version=v1alpha1 44 | case v1alpha1.SchemeGroupVersion.WithResource("certificates"): 45 | return &genericInformer{resource: resource.GroupResource(), informer: f.Cert().V1alpha1().Certificates().Informer()}, nil 46 | case v1alpha1.SchemeGroupVersion.WithResource("certificaterevocations"): 47 | return &genericInformer{resource: resource.GroupResource(), informer: f.Cert().V1alpha1().CertificateRevocations().Informer()}, nil 48 | case v1alpha1.SchemeGroupVersion.WithResource("issuers"): 49 | return &genericInformer{resource: resource.GroupResource(), informer: f.Cert().V1alpha1().Issuers().Informer()}, nil 50 | 51 | } 52 | 53 | return nil, fmt.Errorf("no informer found for %v", resource) 54 | } 55 | -------------------------------------------------------------------------------- /pkg/client/cert/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by informer-gen. DO NOT EDIT. 6 | 7 | package internalinterfaces 8 | 9 | import ( 10 | time "time" 11 | 12 | versioned "github.com/gardener/cert-management/pkg/client/cert/clientset/versioned" 13 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | runtime "k8s.io/apimachinery/pkg/runtime" 15 | cache "k8s.io/client-go/tools/cache" 16 | ) 17 | 18 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 19 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 20 | 21 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 22 | type SharedInformerFactory interface { 23 | Start(stopCh <-chan struct{}) 24 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 25 | } 26 | 27 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 28 | type TweakListOptionsFunc func(*v1.ListOptions) 29 | -------------------------------------------------------------------------------- /pkg/client/cert/listers/cert/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by lister-gen. DO NOT EDIT. 6 | 7 | package v1alpha1 8 | 9 | // CertificateListerExpansion allows custom methods to be added to 10 | // CertificateLister. 11 | type CertificateListerExpansion interface{} 12 | 13 | // CertificateNamespaceListerExpansion allows custom methods to be added to 14 | // CertificateNamespaceLister. 15 | type CertificateNamespaceListerExpansion interface{} 16 | 17 | // CertificateRevocationListerExpansion allows custom methods to be added to 18 | // CertificateRevocationLister. 19 | type CertificateRevocationListerExpansion interface{} 20 | 21 | // CertificateRevocationNamespaceListerExpansion allows custom methods to be added to 22 | // CertificateRevocationNamespaceLister. 23 | type CertificateRevocationNamespaceListerExpansion interface{} 24 | 25 | // IssuerListerExpansion allows custom methods to be added to 26 | // IssuerLister. 27 | type IssuerListerExpansion interface{} 28 | 29 | // IssuerNamespaceListerExpansion allows custom methods to be added to 30 | // IssuerNamespaceLister. 31 | type IssuerNamespaceListerExpansion interface{} 32 | -------------------------------------------------------------------------------- /pkg/client/cert/listers/cert/v1alpha1/issuer.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Code generated by lister-gen. DO NOT EDIT. 6 | 7 | package v1alpha1 8 | 9 | import ( 10 | certv1alpha1 "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 11 | labels "k8s.io/apimachinery/pkg/labels" 12 | listers "k8s.io/client-go/listers" 13 | cache "k8s.io/client-go/tools/cache" 14 | ) 15 | 16 | // IssuerLister helps list Issuers. 17 | // All objects returned here must be treated as read-only. 18 | type IssuerLister interface { 19 | // List lists all Issuers in the indexer. 20 | // Objects returned here must be treated as read-only. 21 | List(selector labels.Selector) (ret []*certv1alpha1.Issuer, err error) 22 | // Issuers returns an object that can list and get Issuers. 23 | Issuers(namespace string) IssuerNamespaceLister 24 | IssuerListerExpansion 25 | } 26 | 27 | // issuerLister implements the IssuerLister interface. 28 | type issuerLister struct { 29 | listers.ResourceIndexer[*certv1alpha1.Issuer] 30 | } 31 | 32 | // NewIssuerLister returns a new IssuerLister. 33 | func NewIssuerLister(indexer cache.Indexer) IssuerLister { 34 | return &issuerLister{listers.New[*certv1alpha1.Issuer](indexer, certv1alpha1.Resource("issuer"))} 35 | } 36 | 37 | // Issuers returns an object that can list and get Issuers. 38 | func (s *issuerLister) Issuers(namespace string) IssuerNamespaceLister { 39 | return issuerNamespaceLister{listers.NewNamespaced[*certv1alpha1.Issuer](s.ResourceIndexer, namespace)} 40 | } 41 | 42 | // IssuerNamespaceLister helps list and get Issuers. 43 | // All objects returned here must be treated as read-only. 44 | type IssuerNamespaceLister interface { 45 | // List lists all Issuers in the indexer for a given namespace. 46 | // Objects returned here must be treated as read-only. 47 | List(selector labels.Selector) (ret []*certv1alpha1.Issuer, err error) 48 | // Get retrieves the Issuer from the indexer for a given namespace and name. 49 | // Objects returned here must be treated as read-only. 50 | Get(name string) (*certv1alpha1.Issuer, error) 51 | IssuerNamespaceListerExpansion 52 | } 53 | 54 | // issuerNamespaceLister implements the IssuerNamespaceLister 55 | // interface. 56 | type issuerNamespaceLister struct { 57 | listers.ResourceIndexer[*certv1alpha1.Issuer] 58 | } 59 | -------------------------------------------------------------------------------- /pkg/controller/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - '' 5 | forbiddenPrefixes: 6 | - github.com/gardener/cert-management/pkg/certman2 7 | -------------------------------------------------------------------------------- /pkg/controller/common.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package controller 8 | 9 | import ( 10 | "github.com/gardener/controller-manager-library/pkg/controllermanager/cluster" 11 | ) 12 | 13 | const ( 14 | // ControllerGroupCert is the controller group for certificates and issuers 15 | ControllerGroupCert = "certcontrollers" 16 | // ControllerGroupSource is the controller group for sources (ingress and services) 17 | ControllerGroupSource = "certsources" 18 | // DNSCluster is the name of the DNS cluster 19 | DNSCluster = "dns" 20 | // SourceCluster is the name of the source cluster 21 | SourceCluster = "source" 22 | // TargetCluster is the name of the target cluster 23 | TargetCluster = "target" 24 | // DefaultCluster is the name of the default cluster 25 | DefaultCluster = cluster.DEFAULT 26 | ) 27 | -------------------------------------------------------------------------------- /pkg/controller/issuer/certificate/certificate_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package certificate_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestCertificate(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "Certificate Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/controller/issuer/certificate/utils_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package certificate 6 | 7 | import ( 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | 11 | api "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 12 | ) 13 | 14 | var _ = Describe("Utils", func() { 15 | Context("#hasMultipleIssuerTypes", func() { 16 | var issuer *api.Issuer 17 | 18 | BeforeEach(func() { 19 | issuer = &api.Issuer{} 20 | }) 21 | 22 | It("should return false if no issuer type is specified", func() { 23 | Expect(hasMultipleIssuerTypes(issuer)).To(BeFalse()) 24 | }) 25 | 26 | It("should return false if only one issuer type is specified", func() { 27 | issuer.Spec.ACME = &api.ACMESpec{} 28 | Expect(hasMultipleIssuerTypes(issuer)).To(BeFalse()) 29 | }) 30 | 31 | It("should return true if multiple issuer types are specified", func() { 32 | issuer.Spec.ACME = &api.ACMESpec{} 33 | issuer.Spec.SelfSigned = &api.SelfSignedSpec{} 34 | Expect(hasMultipleIssuerTypes(issuer)).To(BeTrue()) 35 | }) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /pkg/controller/issuer/core/core_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core_test 8 | 9 | import ( 10 | "testing" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | ) 15 | 16 | func TestCore(t *testing.T) { 17 | RegisterFailHandler(Fail) 18 | RunSpecs(t, "Issuer Core Suite") 19 | } 20 | -------------------------------------------------------------------------------- /pkg/controller/issuer/core/issuerDNSSelections.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | "sync" 11 | 12 | "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 13 | "github.com/gardener/cert-management/pkg/cert/utils" 14 | ) 15 | 16 | // NewIssuerDNSSelections creates an IssuerDNSSelections 17 | func NewIssuerDNSSelections() *IssuerDNSSelections { 18 | return &IssuerDNSSelections{ 19 | selections: map[utils.IssuerKey]*v1alpha1.DNSSelection{}, 20 | } 21 | } 22 | 23 | // IssuerDNSSelections stores last known DNS selection for an issuer 24 | type IssuerDNSSelections struct { 25 | lock sync.Mutex 26 | selections map[utils.IssuerKey]*v1alpha1.DNSSelection 27 | } 28 | 29 | // Add adds a DNS selection 30 | func (s *IssuerDNSSelections) Add(key utils.IssuerKey, sel *v1alpha1.DNSSelection) { 31 | s.lock.Lock() 32 | defer s.lock.Unlock() 33 | 34 | s.selections[key] = sel 35 | } 36 | 37 | // Remove removes a DNS selection 38 | func (s *IssuerDNSSelections) Remove(key utils.IssuerKey) { 39 | s.lock.Lock() 40 | defer s.lock.Unlock() 41 | 42 | delete(s.selections, key) 43 | } 44 | 45 | // GetSelection returns the selection for the given key. 46 | func (s *IssuerDNSSelections) GetSelection(key utils.IssuerKey) *v1alpha1.DNSSelection { 47 | s.lock.Lock() 48 | defer s.lock.Unlock() 49 | 50 | return s.selections[key] 51 | } 52 | 53 | // Issuers returns all issuer keys. 54 | func (s *IssuerDNSSelections) Issuers() []utils.IssuerKey { 55 | s.lock.Lock() 56 | defer s.lock.Unlock() 57 | 58 | keys := []utils.IssuerKey{} 59 | for key := range s.selections { 60 | keys = append(keys, key) 61 | } 62 | return keys 63 | } 64 | 65 | // GetAll returns a map with all selections 66 | func (s *IssuerDNSSelections) GetAll() map[utils.IssuerKey]*v1alpha1.DNSSelection { 67 | s.lock.Lock() 68 | defer s.lock.Unlock() 69 | 70 | result := map[utils.IssuerKey]*v1alpha1.DNSSelection{} 71 | for k, v := range s.selections { 72 | result[k] = v 73 | } 74 | return result 75 | } 76 | -------------------------------------------------------------------------------- /pkg/controller/issuer/core/objectnameset.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | "sync" 11 | 12 | "github.com/gardener/controller-manager-library/pkg/resources" 13 | ) 14 | 15 | // newObjectNameSet creates a synced ObjectNameSet 16 | func newObjectNameSet() *objectNameSet { 17 | return &objectNameSet{ 18 | set: resources.ObjectNameSet{}, 19 | } 20 | } 21 | 22 | // objectNameSet is a synced ObjectNameSet. 23 | type objectNameSet struct { 24 | lock sync.Mutex 25 | set resources.ObjectNameSet 26 | } 27 | 28 | // Add a name. 29 | func (s *objectNameSet) Add(name resources.ObjectName) bool { 30 | s.lock.Lock() 31 | defer s.lock.Unlock() 32 | 33 | old := len(s.set) 34 | s.set.Add(name) 35 | return len(s.set) != old 36 | } 37 | 38 | // Remove a name. 39 | func (s *objectNameSet) Remove(name resources.ObjectName) bool { 40 | s.lock.Lock() 41 | defer s.lock.Unlock() 42 | 43 | old := len(s.set) 44 | s.set.Remove(name) 45 | return len(s.set) != old 46 | } 47 | 48 | // AsArray returns copy of members as array 49 | func (s *objectNameSet) AsArray() []resources.ObjectName { 50 | s.lock.Lock() 51 | defer s.lock.Unlock() 52 | 53 | return s.set.AsArray() 54 | } 55 | 56 | func (s *objectNameSet) String() string { 57 | s.lock.Lock() 58 | defer s.lock.Unlock() 59 | 60 | return s.set.String() 61 | } 62 | 63 | func (s *objectNameSet) Size() int { 64 | s.lock.Lock() 65 | defer s.lock.Unlock() 66 | 67 | return len(s.set) 68 | } 69 | -------------------------------------------------------------------------------- /pkg/controller/issuer/core/quotas.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | "sync" 11 | 12 | "k8s.io/client-go/util/flowcontrol" 13 | 14 | "github.com/gardener/cert-management/pkg/cert/utils" 15 | ) 16 | 17 | // NewQuotas create a Quotas 18 | func NewQuotas() *Quotas { 19 | return &Quotas{ 20 | issuerToQuotas: map[utils.IssuerKey]quotas{}, 21 | } 22 | } 23 | 24 | type quotas struct { 25 | rateLimiter flowcontrol.RateLimiter 26 | requestsPerDay int 27 | } 28 | 29 | // Quotas stores references issuer quotas. 30 | type Quotas struct { 31 | lock sync.Mutex 32 | issuerToQuotas map[utils.IssuerKey]quotas 33 | } 34 | 35 | // RememberQuotas stores the requests per days quota and creates a new ratelimiter if the quota changed. 36 | func (q *Quotas) RememberQuotas(issuerKey utils.IssuerKey, requestsPerDay int) { 37 | q.lock.Lock() 38 | defer q.lock.Unlock() 39 | 40 | if quotas, ok := q.issuerToQuotas[issuerKey]; ok { 41 | if quotas.requestsPerDay == requestsPerDay { 42 | return 43 | } 44 | } 45 | 46 | qps := float32(requestsPerDay) / 86400 47 | burst := requestsPerDay / 4 48 | if burst < 1 { 49 | burst = 1 50 | } 51 | 52 | q.issuerToQuotas[issuerKey] = quotas{ 53 | rateLimiter: flowcontrol.NewTokenBucketRateLimiter(qps, burst), 54 | requestsPerDay: requestsPerDay, 55 | } 56 | } 57 | 58 | // TryAccept tries to accept a certificate request according to the quotas. 59 | // Returns true if accepted and the requests per days quota value 60 | func (q *Quotas) TryAccept(issuerKey utils.IssuerKey) (bool, int) { 61 | q.lock.Lock() 62 | defer q.lock.Unlock() 63 | 64 | if quotas, ok := q.issuerToQuotas[issuerKey]; ok { 65 | return quotas.rateLimiter.TryAccept(), quotas.requestsPerDay 66 | } 67 | return false, 0 68 | } 69 | 70 | // RemoveIssuer removes all secretRefs for an issuer. 71 | func (q *Quotas) RemoveIssuer(issuerKey utils.IssuerKey) { 72 | q.lock.Lock() 73 | defer q.lock.Unlock() 74 | 75 | delete(q.issuerToQuotas, issuerKey) 76 | } 77 | 78 | // RequestsPerDay gets the request per day quota 79 | func (q *Quotas) RequestsPerDay(issuerName utils.IssuerKey) int { 80 | q.lock.Lock() 81 | defer q.lock.Unlock() 82 | 83 | quotas, ok := q.issuerToQuotas[issuerName] 84 | if !ok { 85 | return 0 86 | } 87 | return quotas.requestsPerDay 88 | } 89 | -------------------------------------------------------------------------------- /pkg/controller/issuer/core/wrappedregistration.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package core 8 | 9 | import ( 10 | "encoding/json" 11 | 12 | "github.com/go-acme/lego/v4/registration" 13 | "k8s.io/apimachinery/pkg/runtime" 14 | ) 15 | 16 | type wrappedRegistration struct { 17 | registration.Resource `json:",inline"` 18 | SecretHash *string `json:"secretHash,omitempty"` 19 | } 20 | 21 | // WrapRegistration wraps registration 22 | func WrapRegistration(raw []byte, secretHash string) ([]byte, error) { 23 | reg := &wrappedRegistration{} 24 | err := json.Unmarshal(raw, reg) 25 | if err != nil { 26 | return nil, err 27 | } 28 | reg.SecretHash = &secretHash 29 | return json.Marshal(®) 30 | } 31 | 32 | // IsSameExistingRegistration returns true if status ACME has same secret hash 33 | // or if it has in the old format without secret hash (for migration) 34 | func IsSameExistingRegistration(raw *runtime.RawExtension, realSecretHash string) bool { 35 | if raw == nil || raw.Raw == nil { 36 | return false 37 | } 38 | reg := &wrappedRegistration{} 39 | if err := json.Unmarshal(raw.Raw, reg); err == nil && reg.SecretHash != nil { 40 | return *reg.SecretHash == realSecretHash 41 | } 42 | return true 43 | } 44 | -------------------------------------------------------------------------------- /pkg/controller/issuer/selfSigned/handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package selfSigned 8 | 9 | import ( 10 | "fmt" 11 | "math" 12 | 13 | "github.com/gardener/controller-manager-library/pkg/controllermanager/controller/reconcile" 14 | "github.com/gardener/controller-manager-library/pkg/logger" 15 | "github.com/gardener/controller-manager-library/pkg/resources" 16 | "k8s.io/utils/ptr" 17 | 18 | api "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 19 | "github.com/gardener/cert-management/pkg/controller/issuer/core" 20 | ) 21 | 22 | var selfSignedType = core.SelfSignedType 23 | 24 | // NewSelfSignedIssuerHandler creates an SelfSigned IssuerHandler. 25 | func NewSelfSignedIssuerHandler(support *core.Support) (core.IssuerHandler, error) { 26 | return &selfSignedIssuerHandler{ 27 | support: support, 28 | }, nil 29 | } 30 | 31 | type selfSignedIssuerHandler struct { 32 | support *core.Support 33 | } 34 | 35 | func (h *selfSignedIssuerHandler) Type() string { 36 | return core.SelfSignedType 37 | } 38 | 39 | func (h *selfSignedIssuerHandler) CanReconcile(issuer *api.Issuer) bool { 40 | return issuer != nil && issuer.Spec.SelfSigned != nil 41 | } 42 | 43 | func (h *selfSignedIssuerHandler) Reconcile(logger logger.LogContext, obj resources.Object, issuer *api.Issuer) reconcile.Status { 44 | logger.Infof("reconciling") 45 | 46 | selfSigned := issuer.Spec.SelfSigned 47 | if selfSigned == nil { 48 | return h.support.Failed(logger, obj, api.StateError, &selfSignedType, fmt.Errorf("missing selfSigned spec"), false) 49 | } 50 | issuer.Spec.RequestsPerDayQuota = ptr.To(math.MaxInt64) 51 | 52 | return h.support.SucceedSelfSignedIssuer(logger, obj, &selfSignedType) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/controller/issuer/selfSigned/handler_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package selfSigned_test 6 | 7 | import ( 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | 11 | api "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1" 12 | "github.com/gardener/cert-management/pkg/controller/issuer/selfSigned" 13 | ) 14 | 15 | var _ = Describe("Handler", func() { 16 | h, _ := selfSigned.NewSelfSignedIssuerHandler(nil) 17 | 18 | Context("#CanReconcile", func() { 19 | It("should return false if issuer is nil", func() { 20 | Expect(h.CanReconcile(nil)).To(BeFalse()) 21 | }) 22 | 23 | It("should return false if issuer type is unset", func() { 24 | issuer := &api.Issuer{} 25 | Expect(h.CanReconcile(issuer)).To(BeFalse()) 26 | }) 27 | 28 | It("should return false if issuer type is not self-signed", func() { 29 | issuer := &api.Issuer{Spec: api.IssuerSpec{ACME: &api.ACMESpec{}}} 30 | Expect(h.CanReconcile(issuer)).To(BeFalse()) 31 | }) 32 | 33 | It("should return true if issuer type is self-signed", func() { 34 | issuer := &api.Issuer{Spec: api.IssuerSpec{SelfSigned: &api.SelfSignedSpec{}}} 35 | Expect(h.CanReconcile(issuer)).To(BeTrue()) 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /pkg/controller/issuer/selfSigned/selfSigned_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package selfSigned_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestSelfSigned(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "SelfSigned Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/controller/source/gateways/gatewayapi/controller.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package gatewayapi 6 | 7 | import ( 8 | "strings" 9 | 10 | "github.com/gardener/controller-manager-library/pkg/resources" 11 | 12 | "github.com/gardener/cert-management/pkg/cert/source" 13 | ctrl "github.com/gardener/cert-management/pkg/controller" 14 | ) 15 | 16 | // Group is the group of the Gateway API. 17 | const Group = "gateway.networking.k8s.io" 18 | 19 | var ( 20 | // GroupKindGateway is the GroupKind for the Gateway resource. 21 | GroupKindGateway = resources.NewGroupKind(Group, "Gateway") 22 | // GroupKindHTTPRoute is the GroupKind for the HTTPRoute resource. 23 | GroupKindHTTPRoute = resources.NewGroupKind(Group, "HTTPRoute") 24 | ) 25 | 26 | func init() { 27 | source.CertSourceController(source.NewCertSourceTypeForCreator("k8s-gateways-dns", GroupKindGateway, NewGatewaySource), nil). 28 | FinalizerDomain("cert.gardener.cloud"). 29 | RequireLease(ctrl.SourceCluster). 30 | DeactivateOnCreationErrorCheck(deactivateOnMissingMainResource). 31 | Reconciler(httpRoutesReconciler, "httproutes"). 32 | WorkerPool("httproutes", 2, 0). 33 | ReconcilerWatchesByGK("httproutes", GroupKindHTTPRoute). 34 | MustRegister(ctrl.ControllerGroupSource) 35 | } 36 | 37 | func deactivateOnMissingMainResource(err error) bool { 38 | return strings.Contains(err.Error(), "gardener/cml/resources/UNKNOWN_RESOURCE") && 39 | (strings.Contains(err.Error(), GroupKindGateway.String()) || strings.Contains(err.Error(), GroupKindHTTPRoute.String())) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/controller/source/gateways/gatewayapi/gateway_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * 7 | */ 8 | 9 | package gatewayapi_test 10 | 11 | import ( 12 | "testing" 13 | 14 | ginkgov2 "github.com/onsi/ginkgo/v2" 15 | . "github.com/onsi/gomega" 16 | ) 17 | 18 | func TestUtilsSuite(t *testing.T) { 19 | RegisterFailHandler(ginkgov2.Fail) 20 | ginkgov2.RunSpecs(t, "Networking Kubernetes Gateway Suite") 21 | } 22 | -------------------------------------------------------------------------------- /pkg/controller/source/gateways/gatewayapi/state.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package gatewayapi 6 | 7 | import ( 8 | "sync" 9 | 10 | "github.com/gardener/controller-manager-library/pkg/controllermanager/controller" 11 | "github.com/gardener/controller-manager-library/pkg/ctxutil" 12 | "github.com/gardener/controller-manager-library/pkg/resources" 13 | ) 14 | 15 | var stateKey = ctxutil.SimpleKey("gatewayapi-gateways-state") 16 | 17 | type routesState struct { 18 | lock sync.Mutex 19 | 20 | routesToGateways map[resources.ObjectName][]resources.ObjectName 21 | } 22 | 23 | func newState() *routesState { 24 | return &routesState{ 25 | routesToGateways: map[resources.ObjectName][]resources.ObjectName{}, 26 | } 27 | } 28 | 29 | func getOrCreateSharedState(c controller.Interface) (*routesState, error) { 30 | state := c.GetEnvironment().GetOrCreateSharedValue(stateKey, func() interface{} { 31 | return newState() 32 | }).(*routesState) 33 | 34 | return state, nil 35 | } 36 | 37 | func (s *routesState) AddRoute(route resources.ObjectName, gateways resources.ObjectNameSet) []resources.ObjectName { 38 | s.lock.Lock() 39 | defer s.lock.Unlock() 40 | 41 | oldGateways := s.routesToGateways[route] 42 | if len(gateways) == 0 { 43 | delete(s.routesToGateways, route) 44 | return oldGateways 45 | } 46 | 47 | s.routesToGateways[route] = gateways.AsArray() 48 | return oldGateways 49 | } 50 | 51 | func (s *routesState) RemoveRoute(route resources.ObjectName) { 52 | s.lock.Lock() 53 | defer s.lock.Unlock() 54 | 55 | delete(s.routesToGateways, route) 56 | } 57 | 58 | func (s *routesState) MatchingGatewaysByRoute(route resources.ObjectName) []resources.ObjectName { 59 | s.lock.Lock() 60 | defer s.lock.Unlock() 61 | 62 | if array := s.routesToGateways[route]; array != nil { 63 | value := make([]resources.ObjectName, len(array)) 64 | copy(value, array) 65 | return value 66 | } 67 | return nil 68 | } 69 | 70 | func (s *routesState) RoutesCount() int { 71 | s.lock.Lock() 72 | defer s.lock.Unlock() 73 | return len(s.routesToGateways) 74 | } 75 | -------------------------------------------------------------------------------- /pkg/controller/source/gateways/istio/controller.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package istio 6 | 7 | import ( 8 | "strings" 9 | 10 | "github.com/gardener/controller-manager-library/pkg/resources" 11 | 12 | "github.com/gardener/cert-management/pkg/cert/source" 13 | ctrl "github.com/gardener/cert-management/pkg/controller" 14 | "github.com/gardener/cert-management/pkg/controller/source/ingress" 15 | "github.com/gardener/cert-management/pkg/controller/source/service" 16 | ) 17 | 18 | var ( 19 | // GroupKindGateway is the GroupKind for the Gateway resource. 20 | GroupKindGateway = resources.NewGroupKind("networking.istio.io", "Gateway") 21 | // GroupKindVirtualService is the GroupKind for the VirtualService resource. 22 | GroupKindVirtualService = resources.NewGroupKind("networking.istio.io", "VirtualService") 23 | ) 24 | 25 | func init() { 26 | source.CertSourceController(source.NewCertSourceTypeForCreator("istio-gateways-dns", GroupKindGateway, newGatewaySource), nil). 27 | FinalizerDomain("cert.gardener.cloud"). 28 | RequireLease(ctrl.SourceCluster). 29 | DeactivateOnCreationErrorCheck(deactivateOnMissingMainResource). 30 | Reconciler(newTargetSourcesReconciler, "targetsources"). 31 | Reconciler(newVirtualServicesReconciler, "virtualservices"). 32 | WorkerPool("targetsources", 2, 0). 33 | ReconcilerWatchesByGK("targetsources", service.MainResource, ingress.MainResource). 34 | WorkerPool("virtualservices", 2, 0). 35 | ReconcilerWatchesByGK("virtualservices", GroupKindVirtualService). 36 | MustRegister(ctrl.ControllerGroupSource) 37 | } 38 | 39 | func deactivateOnMissingMainResource(err error) bool { 40 | return strings.Contains(err.Error(), "gardener/cml/resources/UNKNOWN_RESOURCE") && 41 | (strings.Contains(err.Error(), GroupKindGateway.String()) || strings.Contains(err.Error(), GroupKindVirtualService.String())) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/controller/source/gateways/istio/istio_gateway_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * 7 | */ 8 | 9 | package istio 10 | 11 | import ( 12 | "testing" 13 | 14 | ginkgov2 "github.com/onsi/ginkgo/v2" 15 | . "github.com/onsi/gomega" 16 | ) 17 | 18 | func TestUtilsSuite(t *testing.T) { 19 | RegisterFailHandler(ginkgov2.Fail) 20 | ginkgov2.RunSpecs(t, "Istio Gateway Suite") 21 | } 22 | -------------------------------------------------------------------------------- /pkg/controller/source/gateways/istio/targetSourcesReconciler.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package istio 6 | 7 | import ( 8 | "github.com/gardener/controller-manager-library/pkg/controllermanager/controller" 9 | "github.com/gardener/controller-manager-library/pkg/controllermanager/controller/reconcile" 10 | "github.com/gardener/controller-manager-library/pkg/logger" 11 | "github.com/gardener/controller-manager-library/pkg/resources" 12 | ) 13 | 14 | func newTargetSourcesReconciler(c controller.Interface) (reconcile.Interface, error) { 15 | state, err := getOrCreateSharedState(c) 16 | if err != nil { 17 | return nil, err 18 | } 19 | return &targetSourcesReconciler{controller: c, state: state}, nil 20 | } 21 | 22 | var _ reconcile.Interface = &targetSourcesReconciler{} 23 | 24 | type targetSourcesReconciler struct { 25 | reconcile.DefaultReconciler 26 | controller controller.Interface 27 | state *resourcesState 28 | } 29 | 30 | func (r *targetSourcesReconciler) Reconcile(logger logger.LogContext, obj resources.Object) reconcile.Status { 31 | r.triggerGateways(obj.ClusterKey()) 32 | return reconcile.Succeeded(logger) 33 | } 34 | 35 | func (r *targetSourcesReconciler) Delete(logger logger.LogContext, obj resources.Object) reconcile.Status { 36 | r.triggerGateways(obj.ClusterKey()) 37 | return reconcile.Succeeded(logger) 38 | } 39 | 40 | func (r *targetSourcesReconciler) Deleted(logger logger.LogContext, key resources.ClusterObjectKey) reconcile.Status { 41 | r.triggerGateways(key) 42 | return reconcile.Succeeded(logger) 43 | } 44 | 45 | func (r *targetSourcesReconciler) triggerGateways(source resources.ClusterObjectKey) { 46 | gateways := r.state.MatchingGatewaysByTargetSource(source.ObjectKey()) 47 | for _, g := range gateways { 48 | _ = r.controller.EnqueueKey(resources.NewClusterKeyForObject(source.Cluster(), g.ForGroupKind(GroupKindGateway))) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/controller/source/ingress/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package ingress 8 | 9 | import ( 10 | "github.com/gardener/controller-manager-library/pkg/resources" 11 | 12 | "github.com/gardener/cert-management/pkg/cert/source" 13 | ctrl "github.com/gardener/cert-management/pkg/controller" 14 | ) 15 | 16 | // MainResource is the GroupKind for the ingress resource. 17 | var MainResource = resources.NewGroupKind("networking.k8s.io", "Ingress") 18 | 19 | func init() { 20 | source.CertSourceController(source.NewCertSourceTypeForCreator("ingress-cert", MainResource, NewIngressSource), nil). 21 | FinalizerDomain("cert.gardener.cloud"). 22 | RequireLease(ctrl.SourceCluster). 23 | MustRegister(ctrl.ControllerGroupSource) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/controller/source/ingress/handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package ingress 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/gardener/controller-manager-library/pkg/controllermanager/controller" 13 | "github.com/gardener/controller-manager-library/pkg/logger" 14 | "github.com/gardener/controller-manager-library/pkg/resources" 15 | networkingv1 "k8s.io/api/networking/v1" 16 | networkingv1beta1 "k8s.io/api/networking/v1beta1" 17 | 18 | "github.com/gardener/cert-management/pkg/cert/source" 19 | ctrlsource "github.com/gardener/cert-management/pkg/controller/source" 20 | ) 21 | 22 | // CIngressSource is the ingress CertSource 23 | type CIngressSource struct { 24 | source.DefaultCertSource 25 | } 26 | 27 | // NewIngressSource creates a CertSource 28 | func NewIngressSource(_ controller.Interface) (source.CertSource, error) { 29 | return &CIngressSource{DefaultCertSource: source.DefaultCertSource{Events: map[resources.ClusterObjectKey]map[string]string{}}}, nil 30 | } 31 | 32 | // GetCertsInfo returns CertsInfo for the given object 33 | func (s *CIngressSource) GetCertsInfo(logger logger.LogContext, objData resources.ObjectData) (*source.CertsInfo, error) { 34 | return ctrlsource.GetCertsInfoByCollector(logger, objData, func(data resources.ObjectData) ([]*ctrlsource.TLSData, error) { 35 | var array []*ctrlsource.TLSData 36 | switch data := data.(type) { 37 | case *networkingv1beta1.Ingress: 38 | if data.Spec.TLS == nil { 39 | return nil, nil 40 | } 41 | for _, item := range data.Spec.TLS { 42 | array = append(array, &ctrlsource.TLSData{ 43 | SecretName: item.SecretName, 44 | Hosts: item.Hosts, 45 | }) 46 | } 47 | return array, nil 48 | case *networkingv1.Ingress: 49 | if data.Spec.TLS == nil { 50 | return nil, nil 51 | } 52 | for _, item := range data.Spec.TLS { 53 | array = append(array, &ctrlsource.TLSData{ 54 | SecretName: item.SecretName, 55 | Hosts: item.Hosts, 56 | }) 57 | } 58 | return array, nil 59 | default: 60 | return nil, fmt.Errorf("unexpected ingress type: %#v", data) 61 | } 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /pkg/controller/source/service/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package service 8 | 9 | import ( 10 | "github.com/gardener/controller-manager-library/pkg/resources" 11 | 12 | "github.com/gardener/cert-management/pkg/cert/source" 13 | ctrl "github.com/gardener/cert-management/pkg/controller" 14 | ) 15 | 16 | // MainResource is the service resource. 17 | var MainResource = resources.NewGroupKind("core", "Service") 18 | 19 | func init() { 20 | source.CertSourceController(source.NewCertSourceTypeForExtractor("service-cert", MainResource, GetSecretName), nil). 21 | FinalizerDomain("cert.gardener.cloud"). 22 | RequireLease(ctrl.SourceCluster). 23 | MustRegister(ctrl.ControllerGroupSource) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/controller/source/service/handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package service 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/gardener/controller-manager-library/pkg/logger" 13 | "github.com/gardener/controller-manager-library/pkg/resources" 14 | api "k8s.io/api/core/v1" 15 | "k8s.io/apimachinery/pkg/types" 16 | 17 | "github.com/gardener/cert-management/pkg/cert/source" 18 | ) 19 | 20 | // GetSecretName finds the secret name from the object annotations 21 | func GetSecretName(_ logger.LogContext, objData resources.ObjectData) (types.NamespacedName, error) { 22 | var zero types.NamespacedName 23 | svc := objData.(*api.Service) 24 | if svc.Spec.Type != api.ServiceTypeLoadBalancer { 25 | return zero, fmt.Errorf("service is not of type LoadBalancer") 26 | } 27 | 28 | secretName, _ := resources.GetAnnotation(svc, source.AnnotSecretname) 29 | if secretName == "" { 30 | return zero, fmt.Errorf("missing annotation '%s'", source.AnnotSecretname) 31 | } 32 | secretNamespace, _ := resources.GetAnnotation(svc, source.AnnotSecretNamespace) 33 | if secretNamespace == "" { 34 | secretNamespace = svc.GetNamespace() 35 | } 36 | return types.NamespacedName{Namespace: secretNamespace, Name: secretName}, nil 37 | } 38 | -------------------------------------------------------------------------------- /pkg/shared/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - github.com/gardener/cert-management/pkg/apis 5 | - github.com/gardener/cert-management/pkg/shared 6 | - selectorRegexp: github[.]com/gardener/controller-manager-library 7 | forbiddenPrefixes: 8 | - '' 9 | -------------------------------------------------------------------------------- /pkg/shared/const.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package shared 8 | 9 | const ( 10 | // AnnotDNSClass is the annotation for the dns class 11 | AnnotDNSClass = "dns.gardener.cloud/class" 12 | // AnnotACMEDNSChallenge is the annotation for marking DNSEntries for DNS challenges 13 | AnnotACMEDNSChallenge = "cert.gardener.cloud/acme-dns-challenge" 14 | ) 15 | -------------------------------------------------------------------------------- /pkg/shared/dns_utils_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package shared_test 6 | 7 | import ( 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | 11 | "github.com/gardener/cert-management/pkg/shared" 12 | ) 13 | 14 | var _ = Describe("DnsUtils", func() { 15 | Describe("PreparePrecheckNameservers", func() { 16 | It("should return given nameservers if they are valid", func() { 17 | nameservers := []string{"1.1.1.1:53", "1.1.1.2:53"} 18 | Expect(shared.PreparePrecheckNameservers(nameservers)).To(Equal(nameservers)) 19 | }) 20 | 21 | It("should return given nameservers if they are valid and adds missing ports", func() { 22 | nameservers := []string{"1.1.1.1", "1.1.1.2"} 23 | nameserversExpected := []string{"1.1.1.1:53", "1.1.1.2:53"} 24 | Expect(shared.PreparePrecheckNameservers(nameservers)).To(Equal(nameserversExpected)) 25 | }) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /pkg/shared/issuerinfo.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package shared 8 | 9 | const ( 10 | // IssuerTypeACME is the issuer type ACME 11 | IssuerTypeACME = "acme" 12 | // IssuerTypeCA is the issuer type CA 13 | IssuerTypeCA = "ca" 14 | // IssuerTypeSelfSigned is the issuer type selfsigned 15 | IssuerTypeSelfSigned = "selfSigned" 16 | ) 17 | 18 | // IssuerInfo provides name and type of an issuer 19 | type IssuerInfo struct { 20 | key IssuerKeyItf 21 | issuertype string 22 | } 23 | 24 | // NewACMEIssuerInfo creates info for an ACME issuer 25 | func NewACMEIssuerInfo(key IssuerKeyItf) IssuerInfo { 26 | return IssuerInfo{key: key, issuertype: IssuerTypeACME} 27 | } 28 | 29 | // NewCAIssuerInfo creates info for an CA issuer 30 | func NewCAIssuerInfo(key IssuerKeyItf) IssuerInfo { 31 | return IssuerInfo{key: key, issuertype: IssuerTypeCA} 32 | } 33 | 34 | // NewSelfSignedIssuerInfo creates info for a selfSigned issuer. 35 | func NewSelfSignedIssuerInfo(key IssuerKeyItf) IssuerInfo { 36 | return IssuerInfo{key: key, issuertype: IssuerTypeSelfSigned} 37 | } 38 | 39 | // Key returns the issuer key 40 | func (i *IssuerInfo) Key() IssuerKeyItf { 41 | return i.key 42 | } 43 | 44 | // IssuerType returns the issuer type 45 | func (i *IssuerInfo) IssuerType() string { 46 | return i.issuertype 47 | } 48 | -------------------------------------------------------------------------------- /pkg/shared/issuerkeyitf.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package shared 8 | 9 | // Cluster is an enum for default and target cluster 10 | type Cluster int 11 | 12 | const ( 13 | // ClusterDefault is the default cluster (= secondary) 14 | ClusterDefault Cluster = iota 15 | // ClusterTarget is the target cluster (= primary) 16 | ClusterTarget 17 | ) 18 | 19 | // IssuerKeyItf abstracts IssuerKey to simplify code reuse. 20 | type IssuerKeyItf interface { 21 | Name() string 22 | Namespace() string 23 | Cluster() Cluster 24 | Secondary() bool 25 | String() string 26 | } 27 | -------------------------------------------------------------------------------- /pkg/shared/legobridge/delegatingprovider_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package legobridge 6 | 7 | import ( 8 | "errors" 9 | "time" 10 | 11 | . "github.com/onsi/ginkgo/v2" 12 | . "github.com/onsi/gomega" 13 | "k8s.io/apimachinery/pkg/util/wait" 14 | ) 15 | 16 | var _ = Describe("Delegating Provider", func() { 17 | Describe("retryOnUpdateEroor", func() { 18 | BeforeEach(func() { 19 | // Override the default backoff settings to speed up the tests 20 | backoff = wait.Backoff{ 21 | Steps: 4, 22 | Duration: 10 * time.Millisecond, 23 | Factor: 1.1, 24 | Jitter: 0.1, 25 | Cap: 100 * time.Millisecond, 26 | } 27 | }) 28 | 29 | It("should succeed without error", func() { 30 | err := retryOnUpdateError(func() error { 31 | return nil 32 | }) 33 | Expect(err).NotTo(HaveOccurred()) 34 | }) 35 | 36 | It("should succeed after a few retries if updateError occurs", func() { 37 | var i int 38 | err := retryOnUpdateError(func() error { 39 | i++ 40 | if i < 3 { 41 | return &updateError{"failed"} 42 | } 43 | return nil 44 | }) 45 | Expect(err).NotTo(HaveOccurred()) 46 | }) 47 | 48 | It("should fail if some other error occurs and return the error", func() { 49 | var i int 50 | err := retryOnUpdateError(func() error { 51 | i++ 52 | if i < 3 { 53 | return errors.New("failed") 54 | } 55 | return nil 56 | }) 57 | Expect(err).To(MatchError("failed")) 58 | }) 59 | 60 | It("should fail after timeout", func() { 61 | err := retryOnUpdateError(func() error { 62 | return &updateError{"failed"} 63 | }) 64 | Expect(err).To(HaveOccurred()) 65 | }) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /pkg/shared/legobridge/keystores_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package legobridge_test 6 | 7 | import ( 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | corev1 "k8s.io/api/core/v1" 11 | 12 | "github.com/gardener/cert-management/pkg/shared/legobridge" 13 | ) 14 | 15 | var _ = Describe("Pending", func() { 16 | Describe("RemoveKeystoresFromSecret", func() { 17 | It("should do nothing if secret is nil", func() { 18 | Expect(func() { 19 | legobridge.RemoveKeystoresFromSecret(nil) 20 | }).NotTo(Panic()) 21 | }) 22 | 23 | It("should do nothing if the data in the secret is nil", func() { 24 | secret := &corev1.Secret{} 25 | legobridge.RemoveKeystoresFromSecret(secret) 26 | Expect(secret.Data).To(BeNil()) 27 | }) 28 | 29 | It("should remove the keystore data from the secret", func() { 30 | secret := &corev1.Secret{ 31 | Data: map[string][]byte{ 32 | "Field1": []byte("Field1"), 33 | "Field2": []byte("Field2"), 34 | legobridge.PKCS12SecretKey: []byte(legobridge.PKCS12SecretKey), 35 | legobridge.PKCS12TruststoreKey: []byte(legobridge.PKCS12TruststoreKey), 36 | legobridge.JKSSecretKey: []byte(legobridge.JKSSecretKey), 37 | legobridge.JKSTruststoreKey: []byte(legobridge.JKSTruststoreKey), 38 | }, 39 | } 40 | legobridge.RemoveKeystoresFromSecret(secret) 41 | Expect(secret.Data).To(Equal(map[string][]byte{ 42 | "Field1": []byte("Field1"), 43 | "Field2": []byte("Field2"), 44 | })) 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /pkg/shared/legobridge/legobridge_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package legobridge_test 8 | 9 | import ( 10 | "testing" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | ) 15 | 16 | func TestCore(t *testing.T) { 17 | RegisterFailHandler(Fail) 18 | RunSpecs(t, "Legobridge Suite") 19 | } 20 | -------------------------------------------------------------------------------- /pkg/shared/legobridge/pending.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package legobridge 8 | 9 | import ( 10 | "sync" 11 | "time" 12 | 13 | "sigs.k8s.io/controller-runtime/pkg/client" 14 | ) 15 | 16 | // PendingCertificateRequests contains the pending certificate requests. 17 | type PendingCertificateRequests struct { 18 | lock sync.Mutex 19 | requests map[client.ObjectKey]time.Time 20 | } 21 | 22 | // NewPendingRequests creates a new PendingCertificateRequests 23 | func NewPendingRequests() *PendingCertificateRequests { 24 | return &PendingCertificateRequests{requests: map[client.ObjectKey]time.Time{}} 25 | } 26 | 27 | // Add adds a certificate object name. 28 | func (pr *PendingCertificateRequests) Add(name client.ObjectKey) { 29 | pr.lock.Lock() 30 | defer pr.lock.Unlock() 31 | 32 | pr.requests[name] = time.Now() 33 | } 34 | 35 | // Contains check if a certificate object name is pending. 36 | func (pr *PendingCertificateRequests) Contains(name client.ObjectKey) bool { 37 | pr.lock.Lock() 38 | defer pr.lock.Unlock() 39 | 40 | t, ok := pr.requests[name] 41 | return ok && !t.Add(5*time.Minute).Before(time.Now()) 42 | } 43 | 44 | // Remove removes a certificate object name from the pending list. 45 | func (pr *PendingCertificateRequests) Remove(name client.ObjectKey) { 46 | pr.lock.Lock() 47 | defer pr.lock.Unlock() 48 | 49 | delete(pr.requests, name) 50 | } 51 | 52 | // PendingResults caches the ObtainOutput results. 53 | type PendingResults struct { 54 | lock sync.Mutex 55 | results map[client.ObjectKey]*ObtainOutput 56 | } 57 | 58 | // NewPendingResults creates a new PendingResults. 59 | func NewPendingResults() *PendingResults { 60 | return &PendingResults{results: map[client.ObjectKey]*ObtainOutput{}} 61 | } 62 | 63 | // Add adds a object name / ObtainOutput pair. 64 | func (pr *PendingResults) Add(name client.ObjectKey, result *ObtainOutput) { 65 | pr.lock.Lock() 66 | defer pr.lock.Unlock() 67 | 68 | pr.results[name] = result 69 | } 70 | 71 | // Peek fetches a pending result by object name. 72 | func (pr *PendingResults) Peek(name client.ObjectKey) *ObtainOutput { 73 | pr.lock.Lock() 74 | defer pr.lock.Unlock() 75 | 76 | return pr.results[name] 77 | } 78 | 79 | // Remove removes a pending result by object name. 80 | func (pr *PendingResults) Remove(name client.ObjectKey) { 81 | pr.lock.Lock() 82 | defer pr.lock.Unlock() 83 | 84 | delete(pr.results, name) 85 | } 86 | -------------------------------------------------------------------------------- /pkg/shared/legobridge/pending_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package legobridge_test 6 | 7 | import ( 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | "sigs.k8s.io/controller-runtime/pkg/client" 11 | 12 | "github.com/gardener/cert-management/pkg/shared/legobridge" 13 | ) 14 | 15 | var _ = Describe("Pending", func() { 16 | It("should add an object to PendingCertificateRequests and remove it afterwards", func() { 17 | name := client.ObjectKey{Namespace: "test", Name: "test-cert"} 18 | pendingRequests := legobridge.NewPendingRequests() 19 | By("Adding the Object") 20 | pendingRequests.Add(name) 21 | Expect(pendingRequests.Contains(name)).To(BeTrue()) 22 | 23 | By("Removing the Object") 24 | pendingRequests.Remove(name) 25 | Expect(pendingRequests.Contains(name)).To(BeFalse()) 26 | }) 27 | 28 | It("should add an object to PendingResults and remove it afterwards", func() { 29 | name := client.ObjectKey{Namespace: "test", Name: "test-cert"} 30 | pendingResults := legobridge.NewPendingResults() 31 | result := &legobridge.ObtainOutput{} 32 | 33 | By("Adding the Object") 34 | pendingResults.Add(name, result) 35 | Expect(pendingResults.Peek(name)).To(Equal(result)) 36 | 37 | By("Removing the Object") 38 | pendingResults.Remove(name) 39 | Expect(pendingResults.Peek(name)).To(BeNil()) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /pkg/shared/shared_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package shared_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestUtils(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | RunSpecs(t, "Shared Suite") 17 | } 18 | -------------------------------------------------------------------------------- /pkg/shared/utils_certificate.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package shared 8 | 9 | import ( 10 | "crypto/x509" 11 | "encoding/pem" 12 | "fmt" 13 | ) 14 | 15 | // ExtractCommonNameAnDNSNames extracts values from a CSR (Certificate Signing Request). 16 | func ExtractCommonNameAnDNSNames(csr []byte) (cn *string, san []string, err error) { 17 | certificateRequest, err := extractCertificateRequest(csr) 18 | if err != nil { 19 | err = fmt.Errorf("parsing CSR failed: %w", err) 20 | return 21 | } 22 | cnvalue := certificateRequest.Subject.CommonName 23 | if cnvalue != "" { 24 | cn = &cnvalue 25 | } 26 | san = certificateRequest.DNSNames[:] 27 | for _, ip := range certificateRequest.IPAddresses { 28 | san = append(san, ip.String()) 29 | } 30 | return 31 | } 32 | 33 | func extractCertificateRequest(csr []byte) (*x509.CertificateRequest, error) { 34 | block, _ := pem.Decode(csr) 35 | if block == nil { 36 | return nil, fmt.Errorf("decoding CSR failed") 37 | } 38 | return x509.ParseCertificateRequest(block.Bytes) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/shared/utils_certificate_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package shared_test 6 | 7 | import ( 8 | "crypto/rand" 9 | "crypto/rsa" 10 | "crypto/x509" 11 | "crypto/x509/pkix" 12 | "encoding/pem" 13 | "net" 14 | 15 | . "github.com/onsi/ginkgo/v2" 16 | . "github.com/onsi/gomega" 17 | 18 | "github.com/gardener/cert-management/pkg/shared" 19 | ) 20 | 21 | var _ = Describe("ExtractCommonNameAnDNSNames", func() { 22 | var ( 23 | exampleCn string 24 | exampleSan []string 25 | exampleIPs []net.IP 26 | ) 27 | 28 | BeforeEach(func() { 29 | exampleCn = "example.com" 30 | exampleSan = []string{"www.example.com", "example.org"} 31 | exampleIPs = []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("10.0.0.1")} 32 | }) 33 | 34 | It("should extract cn, san, and IP addresses if Common Name (cn), Subject Alternative Name (san), and IP Addresses are set", func() { 35 | csr := _createCSR(exampleCn, exampleSan, exampleIPs) 36 | cn, san, err := shared.ExtractCommonNameAnDNSNames(csr) 37 | Expect(err).ToNot(HaveOccurred()) 38 | Expect(*cn).To(Equal(exampleCn)) 39 | Expect(san).To(ContainElements(append(exampleSan, "192.168.1.1", "10.0.0.1"))) 40 | }) 41 | 42 | It("should only return the san and IP addresses if Common Name is not set", func() { 43 | csr := _createCSR("", exampleSan, exampleIPs) 44 | cn, san, err := shared.ExtractCommonNameAnDNSNames(csr) 45 | Expect(err).ToNot(HaveOccurred()) 46 | Expect(cn).To(BeNil()) 47 | Expect(san).To(ContainElements(append(exampleSan, "192.168.1.1", "10.0.0.1"))) 48 | }) 49 | 50 | It("should fail with an error if CSR is not parseable", func() { 51 | csr := []byte("invalid csr") 52 | cn, san, err := shared.ExtractCommonNameAnDNSNames(csr) 53 | Expect(err).To(HaveOccurred()) 54 | Expect(err).To(MatchError("parsing CSR failed: decoding CSR failed")) 55 | Expect(cn).To(BeNil()) 56 | Expect(san).To(BeEmpty()) 57 | }) 58 | }) 59 | 60 | func _createCSR(cn string, san []string, ips []net.IP) []byte { 61 | key, _ := rsa.GenerateKey(rand.Reader, 2048) 62 | template := &x509.CertificateRequest{ 63 | Subject: pkix.Name{ 64 | CommonName: cn, 65 | }, 66 | DNSNames: san, 67 | IPAddresses: ips, 68 | } 69 | csr, _ := x509.CreateCertificateRequest(rand.Reader, template, key) 70 | return pem.EncodeToMemory(&pem.Block{ 71 | Type: "CERTIFICATE REQUEST", 72 | Bytes: csr, 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v4beta9 2 | kind: Config 3 | metadata: 4 | name: cert-management 5 | build: 6 | local: 7 | useBuildkit: true 8 | push: false 9 | artifacts: 10 | - image: local-skaffold/cert-controller-manager 11 | docker: 12 | dockerfile: Dockerfile 13 | cacheFrom: 14 | - local-skaffold/cert-controller-manager 15 | hooks: 16 | after: 17 | - command: [ "sh", "-c", "hack/kind/skaffold-after-hock.sh" ] 18 | manifests: 19 | rawYaml: 20 | - dev/manifests.yaml 21 | deploy: 22 | kubectl: {} 23 | profiles: 24 | - name: dnsrecords 25 | patches: 26 | - op: replace 27 | path: /manifests/rawYaml/0 28 | value: dev/manifests-dnsrecords.yaml 29 | - op: add 30 | path: /manifests/rawYaml/- 31 | value: test/functional/resources/10-crd-extensions.gardener.cloud_dnsrecords.yaml 32 | -------------------------------------------------------------------------------- /test/certman2/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - github.com/gardener/cert-management/pkg/apis 5 | - github.com/gardener/cert-management/pkg/certman2 6 | - github.com/gardener/cert-management/pkg/shared 7 | - selectorRegexp: github[.]com/gardener/controller-manager-library 8 | forbiddenPrefixes: 9 | - '' 10 | -------------------------------------------------------------------------------- /test/certman2/integration/controller/issuer/issuer_suite_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package source_test 6 | 7 | import ( 8 | "context" 9 | "path/filepath" 10 | "testing" 11 | "time" 12 | 13 | "github.com/gardener/gardener/pkg/logger" 14 | "github.com/go-logr/logr" 15 | . "github.com/onsi/ginkgo/v2" 16 | . "github.com/onsi/gomega" 17 | "k8s.io/apimachinery/pkg/runtime" 18 | "k8s.io/client-go/rest" 19 | "sigs.k8s.io/controller-runtime/pkg/client" 20 | "sigs.k8s.io/controller-runtime/pkg/envtest" 21 | logf "sigs.k8s.io/controller-runtime/pkg/log" 22 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 23 | 24 | certmanclient "github.com/gardener/cert-management/pkg/certman2/client" 25 | ) 26 | 27 | func TestIssuerController(t *testing.T) { 28 | RegisterFailHandler(Fail) 29 | RunSpecs(t, "Integrationtest suite for issuers") 30 | } 31 | 32 | const testID = "issuer-controller-test" 33 | 34 | var ( 35 | ctx = context.Background() 36 | log logr.Logger 37 | 38 | restConfig *rest.Config 39 | testEnv *envtest.Environment 40 | testClient client.Client 41 | 42 | scheme *runtime.Scheme 43 | ) 44 | 45 | var _ = BeforeSuite(func() { 46 | // a lot of CPU-intensive stuff is happening in this test, so to 47 | // prevent flakes we have to increase the timeout here manually 48 | SetDefaultEventuallyTimeout(5 * time.Second) 49 | 50 | logf.SetLogger(logger.MustNewZapLogger(logger.DebugLevel, logger.FormatJSON, zap.WriteTo(GinkgoWriter))) 51 | log = logf.Log.WithName(testID) 52 | 53 | By("Start test environment") 54 | testEnv = &envtest.Environment{ 55 | CRDInstallOptions: envtest.CRDInstallOptions{ 56 | Paths: []string{ 57 | filepath.Join("..", "..", "..", "..", "..", "pkg", "certman2", "apis", "cert", "crd-cert.gardener.cloud_issuers.yaml"), 58 | }, 59 | }, 60 | ErrorIfCRDPathMissing: true, 61 | } 62 | 63 | var err error 64 | restConfig, err = testEnv.Start() 65 | Expect(err).NotTo(HaveOccurred()) 66 | Expect(restConfig).NotTo(BeNil()) 67 | 68 | DeferCleanup(func() { 69 | By("Stop test environment") 70 | Expect(testEnv.Stop()).To(Succeed()) 71 | }) 72 | 73 | By("Create test client") 74 | scheme = certmanclient.ClusterScheme 75 | 76 | testClient, err = client.New(restConfig, client.Options{Scheme: scheme}) 77 | Expect(err).NotTo(HaveOccurred()) 78 | }) 79 | -------------------------------------------------------------------------------- /test/functional/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - '' 5 | forbiddenPrefixes: 6 | - github.com/gardener/cert-management/pkg/certman2 7 | -------------------------------------------------------------------------------- /test/functional/functest-config-kind.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | issuers: 6 | - name: functest-kind 7 | type: acme 8 | autoRegistration: true 9 | server: https://acme.certman-support.svc.cluster.local/dir 10 | email: some.user@mydomain.com 11 | precheckNameservers: 12 | - 10.96.0.10:53 -------------------------------------------------------------------------------- /test/functional/functest-config.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | issuers: 6 | - name: functest-staging 7 | type: acme 8 | autoRegistration: true 9 | server: https://acme-staging-v02.api.letsencrypt.org/directory 10 | email: some.user@mydomain.com 11 | -------------------------------------------------------------------------------- /test/functional/suite.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package functional 8 | 9 | import ( 10 | "sync" 11 | 12 | "github.com/gardener/cert-management/test/functional/config" 13 | ) 14 | 15 | var _config *config.Config 16 | var lock sync.Mutex 17 | 18 | func addIssuerTests(testFactory issuerTestFactory) { 19 | lock.Lock() 20 | defer lock.Unlock() 21 | 22 | if _config == nil { 23 | _config = config.InitConfig() 24 | } 25 | 26 | for _, issuer := range _config.Issuers { 27 | testFactory(_config, issuer) 28 | } 29 | } 30 | 31 | type issuerTestFactory func(config *config.Config, issuer *config.IssuerConfig) 32 | -------------------------------------------------------------------------------- /test/functional/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019 SAP SE or an SAP affiliate company and Gardener contributors 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package functional 8 | 9 | import ( 10 | "testing" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | ) 15 | 16 | var _ = BeforeSuite(func() { 17 | }) 18 | 19 | func TestFunctionalTests(t *testing.T) { 20 | RegisterFailHandler(Fail) 21 | RunSpecs(t, "Functional Test Suite for Cert Controller Manager") 22 | } 23 | 24 | var _ = AfterSuite(func() { 25 | }) 26 | -------------------------------------------------------------------------------- /test/integration/.import-restrictions: -------------------------------------------------------------------------------- 1 | rules: 2 | - selectorRegexp: github[.]com/gardener/cert-management 3 | allowedPrefixes: 4 | - '' 5 | forbiddenPrefixes: 6 | - github.com/gardener/cert-management/pkg/certman2 7 | -------------------------------------------------------------------------------- /test/utils/logbridge.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package testutils 6 | 7 | import ( 8 | "log" 9 | "strings" 10 | 11 | "github.com/go-logr/logr" 12 | ) 13 | 14 | type logBridge struct { 15 | logr logr.Logger 16 | } 17 | 18 | func (logBridge *logBridge) Write(p []byte) (n int, err error) { 19 | message := strings.TrimSpace(string(p)) 20 | 21 | logBridge.logr.Info(message) 22 | 23 | return len(p), nil 24 | } 25 | 26 | // NewLogBridge creates a new log.Logger that forwards all log messages to the given logr.Logger. 27 | func NewLogBridge(logr logr.Logger) *log.Logger { 28 | writer := &logBridge{logr} 29 | 30 | return log.New(writer, "", 0) 31 | } 32 | --------------------------------------------------------------------------------