From 13cc354a23404ddf91958d0cf262dee78abcd97d Mon Sep 17 00:00:00 2001 From: Jonathan Grahl Date: Wed, 13 Mar 2024 23:30:22 +0100 Subject: [PATCH] feat: modernization with cname follow support --- .github/workflows/main.yml | 34 +++++++-------- .github/workflows/pr.yml | 43 +++++++++++++++++++ Makefile | 11 +++-- README.md | 12 ++++-- deploy/cert-manager-webhook-gandi/Chart.yaml | 2 +- deploy/cert-manager-webhook-gandi/values.yaml | 8 ++-- .../certif-example-com-clusterissuer.yaml | 2 +- .../letsencrypt-staging-clusterissuer.yaml | 18 ++++---- .../issuers/letsencrypt-staging-issuer.yaml | 18 ++++---- gandiclient.go | 4 +- main.go | 16 +++++++ 11 files changed, 119 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/pr.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ba12d42..2478a02 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,8 +3,8 @@ name: CI on: push: tags: - - 'v*.*.*' - - '!v0.1.*' + - "v*.*.*" + - "!v0.1.*" jobs: base: @@ -67,10 +67,11 @@ jobs: uses: actions/checkout@v2 - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_PASSWORD }} - name: Set up Docker buildx uses: docker/setup-buildx-action@v1 @@ -84,15 +85,14 @@ jobs: ${{ runner.os }}-buildx- - name: Build and push - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: - context: ./ - file: ./Dockerfile + context: . platforms: linux/amd64 target: image push: true build-args: GO_VERSION=${{ needs.base.outputs.go_version }} - tags: bwolf/cert-manager-webhook-gandi:latest,bwolf/cert-manager-webhook-gandi:${{ needs.base.outputs.build_version }} + tags: quay.io/molnett/cert-manager-webhook-gandi:latest,quay.io/molnett/cert-manager-webhook-gandi:${{ needs.base.outputs.build_version }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max @@ -108,10 +108,10 @@ jobs: id: update_image uses: fjogeleit/yaml-update-action@master with: - valueFile: 'deploy/cert-manager-webhook-gandi/values.yaml' - propertyPath: 'image.tag' + valueFile: "deploy/cert-manager-webhook-gandi/values.yaml" + propertyPath: "image.tag" value: ${{ needs.base.outputs.build_version }} - message: 'Update image tag to ${{ needs.base.outputs.build_version }}' + message: "Update image tag to ${{ needs.base.outputs.build_version }}" token: ${{ secrets.GITHUB_TOKEN }} release: @@ -151,10 +151,10 @@ jobs: - name: Update Helm chart version uses: fjogeleit/yaml-update-action@master with: - valueFile: 'deploy/cert-manager-webhook-gandi/Chart.yaml' - propertyPath: 'version' + valueFile: "deploy/cert-manager-webhook-gandi/Chart.yaml" + propertyPath: "version" value: ${{ needs.base.outputs.chart_version }} - message: 'Update chart version to ${{ needs.base.outputs.chart_version }}' + message: "Update chart version to ${{ needs.base.outputs.chart_version }}" token: ${{ secrets.GITHUB_TOKEN }} updateFile: true @@ -168,5 +168,5 @@ jobs: with: charts_dir: deploy env: - CR_RELEASE_NAME_TEMPLATE: '{{ .Version }}' - CR_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + CR_RELEASE_NAME_TEMPLATE: "{{ .Version }}" + CR_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..344866e --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,43 @@ +name: "PR" + +on: + pull_request: + types: + - opened + - edited + - synchronize + +jobs: + pr-title-check: + name: Validate PR title + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: marocchino/sticky-pull-request-comment@v2 + # When the previous steps fails, the workflow would stop. By adding this + # condition you can continue the execution with the populated error message. + if: always() && (steps.pr-title-lint.outputs.error_message != null) + id: pr-title-lint + with: + header: pr-title-lint-error + message: | + Hey there and thank you for opening this pull request! 👋🏼 + + We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted. + + Details: + + ``` + ${{ steps.pr-title-lint.outputs.error_message }} + ``` + + # Delete a previous comment when the issue has been resolved + - if: ${{ steps.pr-title-lint.outputs.error_message == null }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-title-lint-error + delete: true diff --git a/Makefile b/Makefile index 75ad470..4d7ca67 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,8 @@ endif GO_VERSION ?= $(shell go mod edit -json | grep -${GREP_PREGEX_FLAG}o '"Go":\s+"([0-9.]+)"' | sed -E 's/.+"([0-9.]+)"/\1/') -IMAGE_NAME := bwolf/cert-manager-webhook-gandi -IMAGE_TAG := 0.2.0 +IMAGE_NAME := molnett/cert-manager-webhook-gandi +IMAGE_TAG := 0.2.1 OUT := $(shell pwd)/_out @@ -40,13 +40,16 @@ clean-kubebuilder: build: docker buildx build --target=image --platform=linux/amd64 --output=type=docker,name=${IMAGE_NAME}:${IMAGE_TAG} --tag=${IMAGE_NAME}:latest --build-arg=GO_VERSION=${GO_VERSION} . +build-arm: + docker buildx build --target=image --platform=linux/arm64 --output=type=docker,name=${IMAGE_NAME}:${IMAGE_TAG} --tag=${IMAGE_NAME}:latest --build-arg=GO_VERSION=${GO_VERSION} . + package: helm package deploy/cert-manager-webhook-gandi -d charts/ - helm repo index charts/ --url https://bwolf.github.io/cert-manager-webhook-gandi + helm repo index charts/ --url https://molnett.github.io/cert-manager-webhook-gandi .PHONY: rendered-manifest.yaml rendered-manifest.yaml: helm template \ --set image.repository=${IMAGE_NAME} \ --set image.tag=${IMAGE_TAG} \ - deploy/cert-manager-webhook-gandi > "${OUT}/rendered-manifest.yaml" \ No newline at end of file + deploy/cert-manager-webhook-gandi > "${OUT}/rendered-manifest.yaml" diff --git a/README.md b/README.md index a42135f..db21f1e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # ACME webhook for Gandi (cert-manager-webhook-gandi) -### This is a fork of the original project found here: [https://github.com/bwolf/cert-manager-webhook-gandi](https://github.com/bwolf/cert-manager-webhook-gandi) +## This is a fork of the original project found here: [https://github.com/bwolf/cert-manager-webhook-gandi](https://github.com/bwolf/cert-manager-webhook-gandi) + +It was forked due to adding functionality specific to Molnett which might not be interesting for a larger audience. Thank you to the original author BWolf for the great work! + +## Introduction `cert-manager-webhook-gandi` is an ACME webhook for [cert-manager]. It provides an ACME (read: Let's Encrypt) webhook for [cert-manager], which allows to use a `DNS-01` challenge with [Gandi]. This allows to provide Let's Encrypt certificates to [Kubernetes] for service protocols other than HTTP and furthermore to request wildcard certificates. Internally it uses the [Gandi LiveDNS API] to communicate with Gandi. @@ -39,7 +43,7 @@ This webhook has been tested with [cert-manager] v1.14.4 and Kubernetes v1.22.2 helm repo add jetstack https://charts.jetstack.io - helm install cert-manager cert-manager/cert-manager \ + helm install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --set installCRDs=true \ @@ -94,7 +98,7 @@ This webhook has been tested with [cert-manager] v1.14.4 and Kubernetes v1.22.2 To deploy using the Helm repository (for example using the `v0.2.0` version): helm install cert-manager-webhook-gandi \ - --repo https://bwolf.github.io/cert-manager-webhook-gandi \ + --repo https://molnett.github.io/cert-manager-webhook-gandi \ --version v0.2.0 \ --namespace cert-manager \ --set features.apiPriorityAndFairness=true \ @@ -176,7 +180,7 @@ This webhook has been tested with [cert-manager] v1.14.4 and Kubernetes v1.22.2 **Note**: All changes to the Go code or Helm chart must go with a version tag `vX.X.X` to trigger the GitHub workflow -**Note**: Any Helm chart release results in the creation of a [GitHub release](https://github.com/bwolf/cert-manager-webhook-gandi/releases) +**Note**: Any Helm chart release results in the creation of a [GitHub release](https://github.com/molnett/cert-manager-webhook-gandi/releases) ## Conformance test diff --git a/deploy/cert-manager-webhook-gandi/Chart.yaml b/deploy/cert-manager-webhook-gandi/Chart.yaml index 3c313c9..98ff67c 100644 --- a/deploy/cert-manager-webhook-gandi/Chart.yaml +++ b/deploy/cert-manager-webhook-gandi/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v2 description: A Helm chart for cert-manager-webhook-gandi name: cert-manager-webhook-gandi -version: v0.2.0 +version: v0.2.1 diff --git a/deploy/cert-manager-webhook-gandi/values.yaml b/deploy/cert-manager-webhook-gandi/values.yaml index ab255cc..17d8d01 100644 --- a/deploy/cert-manager-webhook-gandi/values.yaml +++ b/deploy/cert-manager-webhook-gandi/values.yaml @@ -4,11 +4,11 @@ certManager: namespace: cert-manager serviceAccountName: cert-manager image: - repository: bwolf/cert-manager-webhook-gandi - tag: 0.2.0 + repository: quay.io/molnett/cert-manager-webhook-gandi + tag: 0.2.1 pullPolicy: IfNotPresent -nameOverride: '' -fullnameOverride: '' +nameOverride: "" +fullnameOverride: "" service: type: ClusterIP port: 443 diff --git a/examples/certificates/certif-example-com-clusterissuer.yaml b/examples/certificates/certif-example-com-clusterissuer.yaml index eb39c24..9c93bd3 100644 --- a/examples/certificates/certif-example-com-clusterissuer.yaml +++ b/examples/certificates/certif-example-com-clusterissuer.yaml @@ -4,7 +4,7 @@ metadata: name: example-com spec: dnsNames: - - example.com + - example.com issuerRef: name: letsencrypt-staging kind: ClusterIssuer diff --git a/examples/issuers/letsencrypt-staging-clusterissuer.yaml b/examples/issuers/letsencrypt-staging-clusterissuer.yaml index d801c9c..9cf266e 100644 --- a/examples/issuers/letsencrypt-staging-clusterissuer.yaml +++ b/examples/issuers/letsencrypt-staging-clusterissuer.yaml @@ -12,11 +12,13 @@ spec: privateKeySecretRef: name: letsencrypt-staging solvers: - - dns01: - webhook: - groupName: acme.bwolf.me - solverName: gandi - config: - apiKeySecretRef: - key: api-token - name: gandi-credentials \ No newline at end of file + - dns01: + cnameStrategy: Follow + webhook: + groupName: acme.molnett.net + solverName: gandi + config: + rootDomain: "" + apiKeySecretRef: + key: api-token + name: gandi-credentials diff --git a/examples/issuers/letsencrypt-staging-issuer.yaml b/examples/issuers/letsencrypt-staging-issuer.yaml index 4871657..60846c6 100644 --- a/examples/issuers/letsencrypt-staging-issuer.yaml +++ b/examples/issuers/letsencrypt-staging-issuer.yaml @@ -13,11 +13,13 @@ spec: privateKeySecretRef: name: letsencrypt-staging solvers: - - dns01: - webhook: - groupName: acme.bwolf.me - solverName: gandi - config: - apiKeySecretRef: - key: api-token - name: gandi-credentials \ No newline at end of file + - dns01: + cnameStrategy: Follow + webhook: + groupName: acme.molnett.net + solverName: gandi + config: + rootDomain: "" + apiKeySecretRef: + key: api-token + name: gandi-credentials diff --git a/gandiclient.go b/gandiclient.go index 0af0c65..93ab636 100644 --- a/gandiclient.go +++ b/gandiclient.go @@ -48,7 +48,7 @@ func (c *GandiClient) doRequest(req *http.Request, readResponseBody bool) (int, fmt.Printf("Request: %q\n", dump) } - req.Header.Set("Authorization", fmt.Sprintf("Apikey %s", c.apiKey)) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey)) client := http.Client{ Timeout: 30 * time.Second, } @@ -95,7 +95,7 @@ func (c *GandiClient) HasTxtRecord(domain *string, name *string) (bool, error) { // Maybe parse response body here to really ensure that the record is present return true, nil } else { - return false, fmt.Errorf("unexpected HTTP status: %d", status) + return false, fmt.Errorf("unexpected HTTP status: %d, err: %w", status, err) } } diff --git a/main.go b/main.go index 94b8e6c..e8b362a 100644 --- a/main.go +++ b/main.go @@ -64,6 +64,7 @@ type gandiDNSProviderConfig struct { // These fields will be set by users in the // `issuer.spec.acme.dns01.providers.webhook.config` field. APIKeySecretRef cmmeta.SecretKeySelector `json:"apiKeySecretRef"` + RootDomain string `json:"rootDomain"` } // Name is used as the name for this DNS solver when referencing it on the ACME @@ -100,6 +101,13 @@ func (c *gandiDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error { gandiClient := NewGandiClient(*apiKey) entry, domain := c.getDomainAndEntry(ch) + + if cfg.RootDomain != "" { + entry = strings.TrimPrefix(ch.ResolvedFQDN, "_acme-challenge.") + entry = strings.TrimSuffix(entry, ".") + domain = cfg.RootDomain + } + klog.V(6).Infof("present for entry=%s, domain=%s", entry, domain) present, err := gandiClient.HasTxtRecord(&domain, &entry) @@ -146,8 +154,16 @@ func (c *gandiDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error { entry, domain := c.getDomainAndEntry(ch) + if cfg.RootDomain != "" { + entry = ch.ResolvedZone + domain = cfg.RootDomain + } + + klog.V(6).Infof("cleanup for entry=%s, domain=%s", entry, domain) + present, err := gandiClient.HasTxtRecord(&domain, &entry) if err != nil { + klog.V(6).ErrorS(err, "hastxtrecord failed", "entry", entry, "domain", domain) return fmt.Errorf("unable to check TXT record: %v", err) }