├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug.yaml │ └── feature-request.yaml ├── scripts │ └── changelog.sh └── workflows │ ├── release.yaml │ └── stale.yaml ├── .gitignore ├── .markdownlint.json ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── artifacthub-repo.yml ├── assets └── images │ ├── cert-manager-webhook-ovh.png │ └── cert-manager-webhook-ovh.svg ├── charts └── cert-manager-webhook-ovh │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── apiservice.yaml │ ├── deployment.yaml │ ├── issuer.yaml │ ├── pki.yaml │ ├── rbac.yaml │ ├── secret.yaml │ ├── service.yaml │ └── version.yaml │ └── values.yaml ├── go.mod ├── go.sum ├── main.go ├── main_test.go ├── scripts └── fetch-test-binaries.sh └── testdata └── ovh ├── README.md ├── config.json.sample └── ovh-credentials.yaml.sample /.dockerignore: -------------------------------------------------------------------------------- 1 | /.git 2 | /_out 3 | /cert-manager-webhook-ovh 4 | /testdata 5 | /.github 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report something that's not working correctly 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this bug report! Please fill the form below. 9 | - type: textarea 10 | id: what-happened 11 | attributes: 12 | label: What happened? 13 | description: Provide a general description of the issue, including what you're trying to accomplish. Add any relevant logs and configuration files. 14 | validations: 15 | required: true 16 | - type: textarea 17 | id: expected 18 | attributes: 19 | label: Expected Behavior 20 | description: What did you expect to happen? 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: reproducible 25 | attributes: 26 | label: Steps to reproduce 27 | description: | 28 | Provide a link to a live example or a clear set of steps to reproduce this bug. 29 | Include any relevant code or configuration used. 30 | validations: 31 | required: true 32 | - type: textarea 33 | id: versions 34 | attributes: 35 | label: Versions in use 36 | description: Provide the relevant version information for this Chart, Cert Manager and your Kubernetes version. 37 | validations: 38 | required: true 39 | - type: textarea 40 | id: ctx 41 | attributes: 42 | label: Additional context 43 | description: Anything else you would like to add? 44 | validations: 45 | required: false 46 | - type: textarea 47 | id: voting 48 | attributes: 49 | label: Contributing 50 | value: | 51 | Vote on this issue by adding a 👍 reaction. 52 | To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already). 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: A request for a new feature or an update to an existing feature 3 | labels: ["enhancement"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this feature request! Please fill the form below. 9 | - type: textarea 10 | id: what-would-you-like 11 | attributes: 12 | label: What would you like? 13 | description: Provide a general description for this enhancement request, including what you would like to accomplish. Enhancement requests are most helpful when they describe the problem you're having as well as articulating the potential solution you'd like to see built. 14 | validations: 15 | required: true 16 | - type: textarea 17 | id: voting 18 | attributes: 19 | label: Contributing 20 | value: | 21 | Vote on this issue by adding a 👍 reaction. 22 | To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already). 23 | -------------------------------------------------------------------------------- /.github/scripts/changelog.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | CHART_DIR="charts/cert-manager-webhook-ovh" 6 | CONFIG_VERSION="0.0.1" 7 | 8 | COMMITS_TO_PUSH="$(git log --oneline -- "origin..HEAD" | awk 'END { print NR }')" 9 | 10 | if [ "$COMMITS_TO_PUSH" -ne "0" ]; then 11 | echo "All local commits should be pushed. (COMMITS_TO_PUSH='$COMMITS_TO_PUSH')" 12 | exit 1 13 | fi 14 | 15 | if [ "$#" -ne 1 ]; then 16 | echo "Missing release version" >&2 17 | echo "$0 " >&2 18 | exit 1 19 | fi 20 | 21 | if [ -z "$1" ]; then 22 | echo "Unable to determine current version. (CURRENT_VERSION='$CURRENT_VERSION')" >&2 23 | exit 1 24 | fi 25 | 26 | if [ -d "$CHART_DIR" ]; then 27 | echo -n "Linting Chart $CHART_DIR: " 28 | # check for helm syntax problems 29 | helm lint "$CHART_DIR" --set "configVersion=${CONFIG_VERSION}" >/dev/null 2>&1 || exit 1 30 | # check for any error returned by the chart itself 31 | helm lint "$CHART_DIR" --set "configVersion=${CONFIG_VERSION}" 2>&1 >/dev/null | grep -i 'error' >/dev/null && exit 1 32 | echo "passed." 33 | else 34 | echo "Missing '$CHART_DIR'" 35 | exit 1 36 | fi 37 | 38 | CURRENT_VERSION="$1" 39 | PREVIOUS_VERSION="$(git for-each-ref --format="%(refname)" --sort=-creatordate --count=1 refs/tags | awk -F '/' '{print $3}')" 40 | 41 | CHART_VERSION="$(cat "$CHART_DIR/Chart.yaml" | sed '/^version:/!d; s/^version: \(.*\)/\1/g; s/"//g;')" 42 | CHART_APPVERSION="$(cat "$CHART_DIR/Chart.yaml" | sed '/^appVersion:/!d; s/^appVersion: \(.*\)/\1/g; s/"//g;')" 43 | 44 | if [ "$CURRENT_VERSION" != "$CHART_VERSION" -o "$CURRENT_VERSION" != "$CHART_APPVERSION" ]; then 45 | echo "Version mismatch. The following values should match." 46 | echo "$CHART_DIR/Chart.yaml: appVersion: $CHART_APPVERSION" 47 | echo "$CHART_DIR/Chart.yaml: version: $CHART_VERSION" 48 | echo "Command: $CURRENT_VERSION" 49 | exit 1 50 | fi 51 | 52 | if git rev-parse "$CURRENT_VERSION" >/dev/null 2>&1; then 53 | echo "Release '$CURRENT_VERSION' already exists" >&2 54 | exit 1 55 | fi 56 | 57 | if [ -z "$PREVIOUS_VERSION" ]; then 58 | echo "Unable to determine previous version. (PREVIOUS_VERSION='$PREVIOUS_VERSION')" >&2 59 | exit 1 60 | fi 61 | 62 | if [ -z "$(cat CHANGELOG.md | grep "^## $CURRENT_VERSION")" ]; then 63 | echo "Cannot find '$CURRENT_VERSION' in CHANGELOG.md" >&2 64 | exit 1 65 | fi 66 | 67 | if [ -z "$(cat CHANGELOG.md | grep "^## $PREVIOUS_VERSION")" ]; then 68 | echo "Cannot find '$PREVIOUS_VERSION' in CHANGELOG.md" >&2 69 | exit 1 70 | fi 71 | 72 | RELEASE_CHANGELOG="$(mktemp)" 73 | 74 | trap "rm -f $RELEASE_CHANGELOG" EXIT 75 | 76 | echo -e "## Changes for cert-manager-webhook-ovh $CURRENT_VERSION\n" > "$RELEASE_CHANGELOG" 77 | 78 | cat CHANGELOG.md | sed -n "/## $CURRENT_VERSION/,/## $PREVIOUS_VERSION/p;" | sed 'N;$!P;$!D;$d' | awk 'NR>2' | sed '/^$/d' >> "$RELEASE_CHANGELOG" 79 | echo -n "- Released on " >> "$RELEASE_CHANGELOG" 80 | TZ=UTC date >> "$RELEASE_CHANGELOG" 81 | 82 | # we want to detect is a release name wasn't released but is now pulled into 83 | # the release notes. If so, we should stop and request that unpublished release 84 | # to be merge with the one we want to actually publish 85 | cat "$RELEASE_CHANGELOG" | grep '^## ' | sed 's/^## //g;' | sed '1d' | while read R; do 86 | if [ -z "$(git tag -l | grep "$R")" ]; then 87 | echo "The release '$R' doesn't seem to have a tag and is showing up in CHANGELOG.md." 88 | echo "Most likely this release wasn't published and the changelog entries should be merged into '$CURRENT_VERSION'" 89 | exit 1 90 | fi 91 | done 92 | 93 | GH_OPTS="" 94 | if [ ! -z "$(echo $CURRENT_VERSION | sed '/-\(alpha\|beta\|rc\)/!d')" ]; then 95 | GH_OPTS="--prerelease" 96 | fi 97 | 98 | gh release create "$CURRENT_VERSION" --notes-file "$RELEASE_CHANGELOG" $GH_OPTS 99 | 100 | sleep 2 101 | 102 | git fetch -a 103 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | # GitHub recommends pinning actions to a commit SHA. 7 | # To get a newer version, you will need to update the SHA. 8 | # You can also reference a tag or branch, but the action may change without warning. 9 | 10 | name: cert-manager-webhook-ovh actions 11 | 12 | on: 13 | push: 14 | tags: 15 | - 0* 16 | 17 | env: 18 | REGISTRY: ghcr.io 19 | IMAGE_NAME: ${{ github.repository }} 20 | 21 | jobs: 22 | build-and-push-image: 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: read 26 | packages: write 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v4 31 | 32 | - 33 | name: Set up QEMU 34 | uses: docker/setup-qemu-action@v3 35 | - 36 | name: Set up Docker Buildx 37 | uses: docker/setup-buildx-action@v3 38 | 39 | - name: Log in to the Container registry 40 | uses: docker/login-action@v3 41 | with: 42 | registry: ${{ env.REGISTRY }} 43 | username: ${{ github.actor }} 44 | password: ${{ secrets.GITHUB_TOKEN }} 45 | 46 | - name: Extract metadata (tags, labels) for Docker 47 | id: meta 48 | uses: docker/metadata-action@v5 49 | with: 50 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 51 | tags: | 52 | type=semver,pattern={{raw}} 53 | 54 | - name: Build and push Docker image 55 | uses: docker/build-push-action@v5 56 | with: 57 | context: . 58 | push: true 59 | platforms: linux/arm64,linux/arm/v7,linux/amd64 60 | tags: ${{ steps.meta.outputs.tags }} 61 | labels: ${{ steps.meta.outputs.labels }} 62 | 63 | chart-release: 64 | needs: 65 | - build-and-push-image 66 | runs-on: ubuntu-latest 67 | permissions: 68 | contents: write 69 | 70 | steps: 71 | - name: tag-checkout-code 72 | uses: actions/checkout@v4 73 | with: 74 | # # Fetch entire history. Required for chart-releaser; see https://github.com/helm/chart-releaser-action/issues/13#issuecomment-602063896 75 | fetch-depth: 0 76 | 77 | - name: tag-package-chart 78 | env: 79 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 80 | run: | 81 | cp LICENSE charts/cert-manager-webhook-ovh/ 82 | cp README.md charts/cert-manager-webhook-ovh/ 83 | mkdir -p tmp/packages 84 | helm package --destination tmp/packages/ charts/cert-manager-webhook-ovh/ 85 | cp charts/cert-manager-webhook-ovh/Chart.yaml tmp/ 86 | cp artifacthub-repo.yml tmp/ 87 | 88 | - name: tag-upload-artifacts 89 | uses: actions/upload-artifact@v4 90 | with: 91 | name: chart-archive 92 | path: tmp/ 93 | 94 | - name: gh-page-checkout-code 95 | uses: actions/checkout@v4 96 | with: 97 | ref: 'gh-pages' 98 | # Fetch entire history. Required for chart-releaser; see https://github.com/helm/chart-releaser-action/issues/13#issuecomment-602063896 99 | fetch-depth: 0 100 | 101 | - name: gh-pages-download-artifacts 102 | uses: actions/download-artifact@v4 103 | with: 104 | name: chart-archive 105 | path: tmp 106 | 107 | - name: gh-page-configure-git 108 | shell: bash 109 | run: | 110 | git config user.name "$GITHUB_ACTOR" 111 | git config user.email "$GITHUB_ACTOR@users.noreply.github.com" 112 | 113 | - name: gh-page-index-chart-repo 114 | env: 115 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 116 | run: | 117 | test -d packages || mkdir packages 118 | gh release upload "$GITHUB_REF_NAME" tmp/packages/* 119 | mv tmp/packages/* packages/ 120 | mv tmp/Chart.yaml ./ 121 | mv tmp/artifacthub-repo.yml ./ 122 | helm repo index --url "https://$GITHUB_ACTOR.github.io/$(echo "$GITHUB_REPOSITORY" | cut --delimiter / --fields=2)" . 123 | git add artifacthub-repo.yml index.yaml packages/ 124 | git status 125 | git commit -m "Publish $GITHUB_REF_NAME" 126 | git push origin gh-pages 127 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PR' 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | permissions: 7 | issues: write 8 | 9 | jobs: 10 | stale: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/stale@v7 14 | with: 15 | stale-issue-message: 'This issue is stale because it has been open for 14 days with no activity. Remove stale label or comment or this will be closed in 7 days.' 16 | close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.' 17 | days-before-stale: -1 18 | days-before-close: -1 19 | days-before-issue-stale: 14 20 | days-before-issue-close: 7 21 | stale-issue-label: stale 22 | only-issue-labels: question 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Ignore the built binary 15 | /cert-manager-webhook-ovh 16 | _out 17 | 18 | # Test data 19 | testdata/ovh/config.json 20 | testdata/ovh/ovh-credentials.yaml 21 | 22 | # dummy data 23 | tmp 24 | 25 | # IDE files 26 | .idea 27 | .vscode 28 | 29 | # aider 30 | .aider* -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD013": { 4 | "code_blocks": false, 5 | "line_length": 300, 6 | "tables": false 7 | }, 8 | "MD024": false, 9 | "MD040": false, 10 | "MD044": { 11 | "code_blocks": false, 12 | "names": [ 13 | "JavaScript", 14 | "TypeScript", 15 | "Pulumi", 16 | "Policy Pack", 17 | "Premium Policy", 18 | "Premium Policies" 19 | ] 20 | }, 21 | "MD048": { 22 | "style": "backtick" 23 | }, 24 | "MD049": { 25 | "style": "underscore" 26 | }, 27 | "MD050": { 28 | "style": "asterisk" 29 | } 30 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.7.4 4 | 5 | ### Noteworthy changes 6 | 7 | - 🧹 maintenance release, updated dependenies only. 8 | - 🐛 fix minor casing issue in `Dockerfile` 9 | - 🙈 ignore .aider* files 10 | 11 | ### Dependencies 12 | 13 | - ⏩ upgrade github.com/cert-manager/cert-manager to v1.17.2 14 | - ⏩ upgrade github.com/ovh/go-ovh to v1.7.0 15 | - ⏩ upgrade k8s.io/api to v0.32.5 16 | - ⏩ upgrade k8s.io/apiextensions-apiserver to v0.32.5 17 | - ⏩ upgrade k8s.io/apimachinery to v0.32.5 18 | - ⏩ upgrade k8s.io/client-go to v0.32.5 19 | - ⏩ upgrade golang to 1.24 20 | - ⏩ upgrade alpine to 3.21 21 | 22 | ## 0.7.3 23 | 24 | ### Noteworthy changes 25 | 26 | - 🧹 maintenance release, only updated dependenies. 27 | 28 | ### Dependencies 29 | 30 | - ⏩ update golang.org/x/net v0.33.0 to address [CVE-2024-45338](https://github.com/advisories/GHSA-w32m-9786-jp63) 31 | 32 | ## 0.7.2 33 | 34 | ### Noteworthy changes 35 | 36 | - 🧹 maintenance release, only updated dependenies. 37 | 38 | ### Dependencies 39 | 40 | - ⏩ update golang.org/x/crypto v0.31.0 41 | 42 | ## 0.7.1 43 | 44 | ### Noteworthy changes 45 | 46 | - 🧹 maintenance release, only updated dependenies. 47 | 48 | ### Dependencies 49 | 50 | - ⏩ update go 1.23.3 51 | - ⏩ update github.com/cert-manager/cert-manager v1.16.2 52 | - ⏩ update k8s.io/api v0.31.3 53 | - ⏩ update k8s.io/apiextensions-apiserver v0.31.3 54 | - ⏩ update k8s.io/apimachinery v0.31.3 55 | - ⏩ update k8s.io/client-go v0.31.3 56 | 57 | ## 0.7.0 58 | 59 | ### Noteworthy changes 60 | 61 | - ✨ Add new `configVersion` to assist with breaking change 62 | - 🌿 Prefix Helm Chart error messages with 'Error:' 63 | - 🐛 Address minor typography issues in documentation. 64 | - 🌿 support adding customer labels to pod 65 | - 📄 slightly improve documentation in values.yaml 66 | 67 | ### Dependencies 68 | 69 | - ⏩ Use Alpine to 3.20 and Golang 1.23 as build image 70 | - ⏩ Use Alpine to 3.20 as base image 71 | - ⏩ Use Go 1.23.0 to build webhook 72 | - ⏩ Bump github.com/cert-manager/cert-manager 1.14.1 to 1.15.3 73 | - ⏩ Bump github.com/ovh/go-ovh from 1.4.3 to 1.6.0 74 | - ⏩ Bump k8s.io/api from 0.29.1 to 0.30.1 75 | - ⏩ Bump k8s.io/apiextensions-apiserver from 0.29.1 to 0.30.1 76 | - ⏩ Bump golang.org/x/net from 0.20.0 to 0.23.0 77 | 78 | ## 0.7.0-alpha.3 79 | 80 | ### Noteworthy changes 81 | 82 | - 🌿 rename schemaVersion to configVersion 83 | - 🐛 fix error when handling commented configVersion 84 | - 🌿 improve version check 85 | 86 | ## 0.7.0-alpha.2 87 | 88 | ### Noteworthy changes 89 | 90 | - 🌿 support adding customer labels to pod 91 | - 📄 slightly improve documentation in values.yaml 92 | 93 | ## 0.7.0-alpha.1 94 | 95 | ### Noteworthy changes 96 | 97 | - ✨ Add new `schemaVersion` to assist with breaking change 98 | - 🌿 Prefix error messages with 'Error:' 99 | - 🐛 Address minor typography issues in documentation. 100 | 101 | ### Dependencies 102 | 103 | - ⏩ Use Alpine to 3.20 and Golang 1.23 as build image 104 | - ⏩ Use Alpine to 3.20 as base image 105 | - ⏩ Use Go 1.23.0 to build webhook 106 | - ⏩ Bump github.com/cert-manager/cert-manager 1.14.1 to 1.15.3 107 | - ⏩ Bump github.com/ovh/go-ovh from 1.4.3 to 1.6.0 108 | - ⏩ Bump k8s.io/api from 0.29.1 to 0.30.1 109 | - ⏩ Bump k8s.io/apiextensions-apiserver from 0.29.1 to 0.30.1 110 | - ⏩ Bump golang.org/x/net from 0.20.0 to 0.23.0 111 | 112 | ## 0.6.0 113 | 114 | ### Noteworthy changes 115 | 116 | - ⚠️ Separate `securityContext` for both `container` and `pod`. See `values.yaml` for more details. See [#32](https://github.com/aureq/cert-manager-webhook-ovh/pull/32). Authored by [Mathieu Sensei](https://github.com/hyu9a). 117 | - ✨ Support `podAnnotations`. See [#32](https://github.com/aureq/cert-manager-webhook-ovh/pull/32). Authored by [Mathieu Sensei](https://github.com/hyu9a). 118 | - 🌿 Comment out `image.tag` as it's not needed unless someone wants to override the container image version 119 | 120 | ### Dependencies 121 | 122 | - ⏩ Use Alpine to 3.19.1 as base image 123 | - ⏩ Use Go 1.21.6 to build webhook 124 | - ⏩ Bump github.com/cert-manager/cert-manager 1.13.0 to 1.14.1 125 | - ⏩ Bump github.com/ovh/go-ovh from 1.4.2 to 1.4.3 126 | - ⏩ Bump golang.org/x/crypto from 0.14.0 to 0.18.0 127 | - ⏩ Bump golang.org/x/net from 0.17.0 to 0.20.0 128 | - ⏩ Bump k8s.io/api from 0.29.0 to 0.29.1 129 | - ⏩ Bump k8s.io/apiextensions-apiserver from 0.29.0 to 0.29.1 130 | 131 | ## 0.5.2 132 | 133 | ### Dependencies 134 | 135 | - ⏩ Bump google.golang.org/grpc from 1.58.2 to 1.58.3. See [Dependabot](https://github.com/aureq/cert-manager-webhook-ovh/pull/34) 136 | - ⏩ Bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc from 0.35.0 to 0.46.0. See [Dependabot](https://github.com/aureq/cert-manager-webhook-ovh/pull/35) 137 | - ⏩ Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace from 1.19.0 to 1.20.0 138 | - ⏩ Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc from 1.19.0 to 1.20.0 139 | - ⏩ Bump go.opentelemetry.io/otel/sdk from 1.19.0 to 1.20.0 140 | - ⏩ Bump golang.org/x/sys from 0.13.0 to 0.14.0 141 | 142 | ### Known issues 143 | 144 | - 🔥 Alpine 3.18.4 is vulnerable to the following CVEs. Should be fixed in [3.18.5 release](https://gitlab.alpinelinux.org/groups/alpine/-/milestones/5#tab-issues). 145 | - [CVE-2023-5363](https://avd.aquasec.com/nvd/cve-2023-5363) 146 | - [CVE-2023-5678](https://avd.aquasec.com/nvd/cve-2023-5678) 147 | - [CVE-2023-5363](https://avd.aquasec.com/nvd/cve-2023-5363) 148 | - [CVE-2023-5678](https://avd.aquasec.com/nvd/cve-2023-5678) 149 | 150 | ## 0.5.1 151 | 152 | ### Dependencies 153 | 154 | - ⏩ bump go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp to v0.45.0 to address CVE-2023-45142. See [Dependabot](https://github.com/aureq/cert-manager-webhook-ovh/security/dependabot/6). 155 | - ⏩ bump golang.org/x/net from 0.15.0 to 0.17.0. See [Dependabot PR](https://github.com/aureq/cert-manager-webhook-ovh/pull/33). 156 | 157 | ## 0.5.0 158 | 159 | ### Noteworthy changes 160 | 161 | - ✨ add support for `readOnlyRootFilesystem` on the deployment (thanks @Benzhaomin) 162 | - ✨ add Deployment annotation support (thanks @Benzhaomin) 163 | - ✨ add ref link about `nodeSelector`, `tolerations`, `affinity` and `annotations` 164 | - ✨ choose rbac role type (default `Role`) (thanks @Alissia01) 165 | - 📄 document 3 more configuration entries in `values.yaml` 166 | - 🌿 make this chart compatible with helm 3 by settings `apiVersion` to `v2` 167 | - 🌿 drop `v` in `appVersion` and `version` fields, set `"0.5.0"` 168 | - 🌿 udpate `image.tag` value to use SemVer 2.0 and set its values to `"0.5.0"` 169 | - 🐛 typo fix 170 | - ⏩ update k8s.io/apiserver to v0.28.2 due to security (dependabot) 171 | 172 | ### Dependencies 173 | 174 | - ⏩ upgrade github.com/cert-manager/cert-manager to v1.13.0 175 | - ⏩ build with go 1.20 176 | - ⏩ upgrade k8s dependencies to 0.28.1 177 | - ⏩ use alpine 3.18 as base image 178 | - ⏩ update dependency for github.com/ovh/go-ovh to v1.4.2 179 | - ⏩ Bump google.golang.org/grpc from 1.51.0 to 1.53.0 180 | 181 | ## 0.5.0-alpha.2 182 | 183 | ### Noteworthy changes 184 | 185 | - ✨ add support for `readOnlyRootFilesystem` on the deployment (thanks @Benzhaomin) 186 | - 🐛 typo fix 187 | - ✨ add annotation support (thanks @Benzhaomin) 188 | - ✨ add ref link about `nodeSelector`, `tolerations`, `affinity` and `annotations` 189 | - ✨ choose rbac role type (default `Role`) 190 | - ⏩ build with go 1.20 191 | - ⏩ upgrade k8s dependencies to 0.28.1 192 | - ⏩ upgrade github.com/cert-manager/cert-manager to v1.13.0 193 | - ⏩ use alpine 3.18 as base image 194 | 195 | ## 0.5.0-alpha.1 196 | 197 | ### Noteworthy changes 198 | 199 | - ⏩ Bump google.golang.org/grpc from 1.51.0 to 1.53.0 200 | - 📄 document 3 more configuration entries in `values.yaml` 201 | - 🌿 make this chart compatible with helm 3 by settings `apiVersion` to `v2` 202 | - 🌿 drop `v` in `appVersion` and `version` fields, set `"0.5.0"` 203 | - 🌿 udpate `image.tag` value to use SemVer 2.0 and set its values to `"0.5.0"` 204 | - ⏩ update dependency for github.com/ovh/go-ovh to v1.4.2 205 | 206 | ## v0.4.2 207 | 208 | ### Noteworthy changes 209 | 210 | - ✨ build images for amd64, arm64 and armv7 architectures 211 | - 🐙 add issue templates for bugs and feature requests 212 | - 🤖 configure dependabot to get alerts on vulnerabilities 213 | - 📄 add disclaimer about support and code of conduct 214 | - ✨ integration with [artifacthub.io](https://artifacthub.io/packages/helm/cert-manager-webhook-ovh/cert-manager-webhook-ovh) 215 | - 📄 minor inconsistency fix in README.md 216 | - 📄 add steps to make a release 217 | - ⏩ update cert-manager dependency to v1.11.0 218 | - ⏩ update k8s dependency to v0.26.0 219 | - ⏩ build image using Go 1.19.7 220 | - ⏩ upgrade alpine to 3.17 221 | - ⏩ update Chart.yaml and `values.yaml` to use latest container image 222 | 223 | ## v0.4.2-alpha.1 224 | 225 | ### Noteworthy changes 226 | 227 | - 📄 minor consistency fix in README.md 228 | - ✨ start work to integrade with artifacthub.io 229 | 230 | ## v0.4.2-alpha.0 231 | 232 | ### Noteworthy changes 233 | 234 | - ⏩ update cert-manager dependency to v1.11.0 235 | - ⏩ update k8s dependency to v0.26.0 236 | - ✨ build image using Go 1.19.5 237 | - ✨ initial work to build arm64 and armv7 images 238 | 239 | ## v0.4.1 240 | 241 | ### Noteworthy changes 242 | 243 | - 🐛 include minutes and seconds in certificates duration fields. see [argoproj/argo-cd#6008](https://github.com/argoproj/argo-cd/issues/6008) for details. via [@aegaeonit](https://github.com/aegaeonit) 244 | - ✨ optimize Dockerfile for better builds 245 | - ✨ explicitly use Alpine 3.16 throughout the Dockerfile 246 | - ✨ run the webhook as `nobody`/`nogroup` 247 | - ✨ reduce container image from 107MB down to 56.2MB 248 | - ✨ add CNAME strategy to issuers in [#8](https://github.com/aureq/cert-manager-webhook-ovh/pull/8). Thanks ([@Zcool85](https://github.com/Zcool85)) 249 | - ✨ build image using Go 1.19.4 250 | 251 | ## v0.4.0 252 | 253 | ### Major features 254 | 255 | - ⚠️ breaking changes ahead if comming from previous version 256 | - 📄 documentation and helm chart hosted at [https://aureq.github.io/cert-manager-webhook-ovh/](https://aureq.github.io/cert-manager-webhook-ovh/) 257 | - ✨ deploy multiple `Issuer` (namespaced) and `ClusterIssuer` via chart 258 | - ✨ either specify your OVH credentials, or use an existing secret 259 | - ✨ OVH credential are all stored in a secret (ApplicationKey, ApplicaitonSecret, ConsumerKey) 260 | - ✨ deploy necessary permissions to access the OVH credentials 261 | - ✨ role based access control to access secrets across namespaces 262 | - 🚀 publish container image on GitHub Container Registry 263 | - 🚀 publish Helm Chart on GitHub pages 264 | - ⬆️ upgrade dependencies to reduce warnings 265 | - ✨ drop root privileges 266 | - ✨ add support for HTTP/HTTPS proxy 267 | 268 | ### Noteworthy changes 269 | 270 | - 🚀 use kubernetes recommended labels 271 | - ✨ move some helm logic in _helpers.tpl 272 | - ✨ completely rework `values.yaml` to support creating issuers and ovh credentials 273 | - ✨ create role and bind it so the webhook can access necessary secrets 274 | - ⬆️ upgrade dependencies to reduce warnings 275 | - cert-manager `v1.5.3` to `v1.9.1` 276 | - go-ovh `v1.1.0` to `v1.3.0` 277 | - client-go `v0.22.1` to `v0.24.2` 278 | - build webhook using golang `1.18` 279 | - ✨ add image pull secrets to helm chart by Julian Stiller) 280 | - 🐛 fix base64 encoded secrets by [@julienkosinski](https://github.com/julienkosinski) 281 | - 🔥 drop root privilges (missing attribution) 282 | - 🐛 fix how security context is checked 283 | - ✨ add RBAC (missing attribution) 284 | - ⬆️ upgrade to Alpine Linux 3.16 container image 285 | - 🐛 fix `Makefile` references and enable HTTP proxy to local build environment 286 | - ✨ set `CAP_NET_BIND_SERVICE` to binary to bind on privileged ports without root privileges (missing attribution) 287 | - 🐛 add `libpcap` to container image 288 | - ✨ create logo based on cert-manager logo and [icons8](https://icons8.com/icon/92/link) 289 | - ✨ more fields populated in `Chart.yaml` 290 | - 🌱 some ground work to automate the release process via GitHub Actions and GitHub packages 291 | 292 | ## v0.4.0-alpha.1 293 | 294 | ### Major features 295 | 296 | - ⚠️ breaking changes ahead 297 | - ✨ major helm chart improvements 298 | - ✨ deploy multiple `Issuer` (namespaced) and `ClusterIssuer` via chart 299 | - ✨ either specify your OVH credentials, or use an existing secret 300 | - ✨ OVH credential are all stored in a secret (ApplicationKey, ApplicaitonSecret, ConsumerKey) 301 | - ✨ deploy necessary permissions to access the OVH credentials 302 | - ✨ role based access control to access secrets across namespaces 303 | 304 | ### Note worthy changes 305 | 306 | - ✨ move some helm logic in _helpers.tpl 307 | - ✨ completely rework `values.yaml` to support creating issuers and ovh credentials 308 | - ✨ create role and bind it so the webhook can access necessary secrets 309 | - 📄 documentation and helm chart hosted at [https://aureq.github.io/cert-manager-webhook-ovh/](https://aureq.github.io/cert-manager-webhook-ovh/) 310 | 311 | ## v0.4.0-alpha.0 312 | 313 | ### Major features 314 | 315 | - 🚀 publish container image on GitHub Container Registry 316 | - 🚀 publish Helm Chart on GitHub pages 317 | - ⬆️ upgrade dependencies to reduce warnings 318 | - ✨ drop root privileges 319 | - 🌱 some ground work to automate the release process via GitHub Actions 320 | 321 | ### Noteworthy changes 322 | 323 | - ✨ add support for HTTP proxy 324 | - ⬆️ upgrade dependencies to reduce warnings 325 | - cert-manager `v1.5.3` to `v1.9.1` 326 | - go-ovh `v1.1.0` to `v1.3.0` 327 | - client-go `v0.22.1` to `v0.24.2` 328 | - build webhook using golang `1.18` 329 | - ✨ add image pull secrets to helm chart by Julian Stiller) 330 | - 🐛 fix base64 encoded secrets by [@julienkosinski](https://github.com/julienkosinski) 331 | - 🔥 drop root privilges (missing attribution) 332 | - 🐛 fix how security context is checked 333 | - ✨ add RBAC (missing attribution) 334 | - ⬆️ upgrade to Alpine Linux 3.16 container image 335 | - 🐛 fix `Makefile` references and enable HTTP proxy to local build environment 336 | - ✨ set `CAP_NET_BIND_SERVICE` to binary to bind on privileged ports without root privileges (missing attribution) 337 | - 🐛 add `libpcap` to container image 338 | - ✨ create logo based on cert-manager logo and [icons8](https://icons8.com/icon/92/link) 339 | - ✨ more fields populated in `Chart.yaml` 340 | - 🌱 some ground work to automate the release process via GitHub Actions and GitHub packages 341 | 342 | ## 0.3.0 343 | 344 | - Initial work by [@baarde](https://github.com/baarde) 345 | - [cert-manager-webhook-ovh](https://github.com/baarde/cert-manager-webhook-ovh/) 346 | - Commit [`ab4d192`](https://github.com/baarde/cert-manager-webhook-ovh/commit/ab4d192358ed7048091e1788e7256fc4fbf5e767) 347 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/golang:1.24-alpine3.21 AS build 2 | 3 | RUN apk update && \ 4 | apk upgrade && \ 5 | apk add --no-cache git libcap 6 | 7 | WORKDIR /go/src/app 8 | ENV GO111MODULE=on 9 | COPY . . 10 | 11 | RUN go mod download 12 | RUN CGO_ENABLED=0 go build -o /go/bin/app -ldflags '-s -w -extldflags "-static"' . 13 | 14 | FROM alpine:3.20 15 | 16 | COPY --from=build /go/bin/app / 17 | 18 | COPY --from=build /usr/sbin/setcap /usr/sbin/setcap 19 | COPY --from=build /usr/lib/libcap.so.2* /usr/lib/ 20 | 21 | RUN /usr/sbin/setcap cap_net_bind_service=+ep /app 22 | 23 | USER nobody:nogroup 24 | ENTRYPOINT ["/app"] 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IMAGE_NAME := "aureq/cert-manager-webhook-ovh" 2 | IMAGE_TAG := "latest" 3 | 4 | .PHONY: rendered-manifest.yaml test build 5 | 6 | OUT := $(shell pwd)/_out 7 | TEST_ASSET_ETCD := $(OUT)/kubebuilder/bin/etcd 8 | TEST_ASSET_KUBE_APISERVER := $(OUT)/kubebuilder/bin/kube-apiserver 9 | TEST_ASSET_KUBECTL := $(OUT)/kubebuilder/bin/kubectl 10 | 11 | test: 12 | @test -d "$(OUT)" || mkdir -p "$(OUT)" 13 | @sh ./scripts/fetch-test-binaries.sh 14 | TEST_ASSET_ETCD="$(TEST_ASSET_ETCD)" \ 15 | TEST_ASSET_KUBE_APISERVER="$(TEST_ASSET_KUBE_APISERVER)" \ 16 | TEST_ASSET_KUBECTL="$(TEST_ASSET_KUBECTL)" \ 17 | go test -v . 18 | 19 | build: 20 | @test -z "$$HTTP_PROXY" -a -z "$$HTTPS_PROXY" || docker buildx build \ 21 | --progress=plain \ 22 | --compress \ 23 | --output type=image,oci-mediatypes=true,compression=estargz,force-compression=true,push=false \ 24 | --build-arg "HTTP_PROXY=$$HTTP_PROXY" \ 25 | --build-arg "HTTPS_PROXY=$$HTTPS_PROXY" \ 26 | -t "$(IMAGE_NAME):$(IMAGE_TAG)" . 27 | @test ! -z "$$HTTP_PROXY" -o ! -z "$$HTTPS_PROXY" || docker build \ 28 | --progress=plain \ 29 | --compress \ 30 | --output type=image,oci-mediatypes=true,compression=estargz,force-compression=true,push=false \ 31 | -t "$(IMAGE_NAME):$(IMAGE_TAG)" . 32 | 33 | rendered-manifest.yaml: 34 | @test -d "$(OUT)" || mkdir -p "$(OUT)" 35 | @helm template \ 36 | cert-manager-webhook-ovh \ 37 | --set image.repository=$(IMAGE_NAME) \ 38 | --set image.tag=$(IMAGE_TAG) \ 39 | charts/cert-manager-webhook-ovh > "$(OUT)/rendered-manifest.yaml" 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OVH Webhook for Cert Manager 2 | 3 | ![OVH Webhook for Cert Manager](assets/images/cert-manager-webhook-ovh.svg "OVH Webhook for Cert Manager") 4 | 5 | This is a webhook solver for [OVH](http://www.ovh.com) DNS. In short, if your domain has its DNS servers hosted with OVH, you can solve DNS challenges using Cert Manager and OVH Webhook for Cert Manager. 6 | 7 | Please star this repository to help others find it. 8 | 9 | ## Features 10 | 11 | - Solve DNS01 challenges using OVH DNS servers 12 | - Supports Cert Manager `ClusterIssuer` and `Issuer` 13 | - Helm chart repository for ease and simplicity 14 | - Store OVH credentials in a secret per issuer, or use secret references 15 | - Role based access control, across namespace 16 | 17 | ## Documentation 18 | 19 | The documentation is available at [https://aureq.github.io/cert-manager-webhook-ovh/](https://aureq.github.io/cert-manager-webhook-ovh/) 20 | 21 | ## Artifact details 22 | 23 | [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/cert-manager-webhook-ovh)](https://artifacthub.io/packages/search?repo=cert-manager-webhook-ovh) 24 | 25 | ## Support 26 | 27 | We would like to inform our users that this repository is maintained by a volunteer, and as such, it is a best effort support, and not a commercial service. 28 | While we strive to provide the best possible support to our users, we cannot guarantee immediate or comprehensive responses to all queries. 29 | Therefore, we advise that users seeking professional support should contact the author directly. 30 | 31 | We also want to emphasize that any form of abuse or entitlement towards our volunteer maintainers will not be tolerated. 32 | Our volunteers work hard to provide support to the community, and we expect all users to treat them with respect and appreciation. 33 | We appreciate your understanding and cooperation in maintaining a positive and productive environment for everyone involved in this community-driven project. 34 | 35 | ## Release workflow 36 | 37 | - Update `charts/cert-manager-webhook-ovh/Chart.yaml` 38 | - Prepare `CHANGELOG.md` for `x.y.z` 39 | - Commit all changes 40 | - Push all commits 41 | - run `bash .github/scripts/changelog.sh x.y.z` 42 | 43 | ## Maintainers 44 | 45 | - [@aureq](https://github.com/aureq) 46 | 47 | ## Contributors 48 | 49 | - [@munnerz](https://github.com/munnerz) 50 | - [@Diaphteiros](https://github.com/Diaphteiros) 51 | - [@baarde](https://github.com/baarde) 52 | - Xaver Baun 53 | - lcavajani 54 | - Ricardo Pchevuzinske Katz 55 | - [@MattiasGees](https://github.com/MattiasGees) 56 | - Jean-Marc Andre 57 | - [@IDerr](https://github.com/IDerr) 58 | - Robin KERDILES 59 | - Julian Stiller 60 | - [@julienkosinski](https://github.com/julienkosinski) 61 | - [@aegaeonit](https://github.com/aegaeonit) 62 | - [@TartanLeGrand](https://github.com/TartanLeGrand) 63 | - [@Zcool85](https://github.com/Zcool85) 64 | - [@Yethal](https://github.com/Yethal) 65 | - [Benjamin Maisonnas](https://github.com/Benzhaomin) 66 | - [Kebree](https://github.com/Kebree) 67 | - [Alissia01](https://github.com/Alissia01) 68 | - [Mathieu Sensei](https://github.com/hyu9a) 69 | -------------------------------------------------------------------------------- /artifacthub-repo.yml: -------------------------------------------------------------------------------- 1 | # Artifact Hub repository metadata file 2 | # 3 | # Some settings like the verified publisher flag or the ignored packages won't 4 | # be applied until the next time the repository is processed. Please keep in 5 | # mind that the repository won't be processed if it has not changed since the 6 | # last time it was processed. Depending on the repository kind, this is checked 7 | # in a different way. For Helm http based repositories, we consider it has 8 | # changed if the `index.yaml` file changes. For git based repositories, it does 9 | # when the hash of the last commit in the branch you set up changes. This does 10 | # NOT apply to ownership claim operations, which are processed immediately. 11 | # 12 | repositoryID: f607d418-57a9-4371-a9c3-e2f05c331c3a 13 | owners: 14 | - name: Aurelien Requiem 15 | email: artifacthub@antispam.menfin.net 16 | -------------------------------------------------------------------------------- /assets/images/cert-manager-webhook-ovh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureq/cert-manager-webhook-ovh/03c0770490e0c4674d4c02f61da29efdddc5cce4/assets/images/cert-manager-webhook-ovh.png -------------------------------------------------------------------------------- /assets/images/cert-manager-webhook-ovh.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 37 | 42 | 47 | 49 | 51 | 57 | 58 | 59 | 64 | 69 | 70 | -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/.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 | -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: "0.7.4" 3 | deprecated: false 4 | description: OVH DNS cert-manager ACME webhook 5 | home: https://github.com/aureq/cert-manager-webhook-ovh 6 | icon: https://raw.githubusercontent.com/aureq/cert-manager-webhook-ovh/main/assets/images/cert-manager-webhook-ovh.svg 7 | keywords: 8 | - cert-manager 9 | - ovh 10 | - dns 11 | - lets-encrypt 12 | - Let's Encrypt 13 | - tls 14 | - certificate 15 | - ssl 16 | maintainers: 17 | - name: Aurélien Requiem 18 | url: https://github.com/aureq 19 | name: cert-manager-webhook-ovh 20 | sources: 21 | - https://github.com/aureq/cert-manager-webhook-ovh 22 | version: "0.7.4" -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/NOTES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureq/cert-manager-webhook-ovh/03c0770490e0c4674d4c02f61da29efdddc5cce4/charts/cert-manager-webhook-ovh/templates/NOTES.txt -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "cert-manager-webhook-ovh.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-manager-webhook-ovh.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-manager-webhook-ovh.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{- define "cert-manager-webhook-ovh.selfSignedIssuer" -}} 35 | {{ printf "%s-selfsign" (include "cert-manager-webhook-ovh.fullname" .) }} 36 | {{- end -}} 37 | 38 | {{- define "cert-manager-webhook-ovh.rootCAIssuer" -}} 39 | {{ printf "%s-ca" (include "cert-manager-webhook-ovh.fullname" .) }} 40 | {{- end -}} 41 | 42 | {{- define "cert-manager-webhook-ovh.rootCACertificate" -}} 43 | {{ printf "%s-ca" (include "cert-manager-webhook-ovh.fullname" .) }} 44 | {{- end -}} 45 | 46 | {{- define "cert-manager-webhook-ovh.servingCertificate" -}} 47 | {{ printf "%s-webhook-tls" (include "cert-manager-webhook-ovh.fullname" .) }} 48 | {{- end -}} 49 | 50 | {{/* 51 | Returns true if ovhAuthentication is correctly set. 52 | */}} 53 | {{- define "cert-manager-webhook-ovh.isOvhAuthenticationAvail" -}} 54 | {{- if . -}} 55 | {{- if and (.consumerKey) (.applicationKey) (.applicationSecret) -}} 56 | {{- eq "true" "true" -}} 57 | {{- end -}} 58 | {{- end -}} 59 | {{- end -}} 60 | 61 | {{/* 62 | Returns true if ovhAuthenticationRef is correctly set. 63 | */}} 64 | {{- define "cert-manager-webhook-ovh.isOvhAuthenticationRefAvail" -}} 65 | {{- if . -}} 66 | {{- if or (not .consumerKeyRef) (not .applicationKeyRef) (not .applicationSecretRef) }} 67 | {{- fail "Error: When 'ovhAuthenticationRef' is used, 'consumerKeyRef', 'applicationKeyRef' and 'applicationSecretRef' need to be provided." }} 68 | {{- end }} 69 | {{- if or (not .consumerKeyRef.name) (not .consumerKeyRef.key) }} 70 | {{ fail "Error: When 'ovhAuthenticationRef' is used, you need to provide 'ovhAuthenticationRef.consumerKeyRef.name' and 'ovhAuthenticationRef.consumerKeyRef.key'" }} 71 | {{- end }} 72 | {{- if or (not .applicationKeyRef.name) (not .applicationKeyRef.key) }} 73 | {{ fail "Error: When 'ovhAuthenticationRef' is used, you need to provide 'ovhAuthenticationRef.applicationKeyRef.name' and 'ovhAuthenticationRef.applicationKeyRef.key'" }} 74 | {{- end }} 75 | {{- if or (not .applicationSecretRef.name) (not .applicationSecretRef.key) }} 76 | {{ fail "Error: When 'ovhAuthenticationRef' is used, you need to provide 'ovhAuthenticationRef.applicationSecretRef.name' and 'ovhAuthenticationRef.applicationSecretRef.key'" }} 77 | {{- end }} 78 | {{- eq "true" "true" -}} 79 | {{- end -}} 80 | {{- end -}} 81 | 82 | {{/* 83 | Returns the number of Issuer/ClusterIssuer to create 84 | */}} 85 | {{- define "cert-manager-webhook-ovh.isIssuerToCreate" -}} 86 | {{- $issuerCount := 0 }} 87 | {{- range $.Values.issuers }} 88 | {{- if .create }} 89 | {{- $issuerCount = $issuerCount | add1 -}} 90 | {{- end }}{{/* end if .create */}} 91 | {{- end }}{{/* end range */}} 92 | {{- $issuerCount }} 93 | {{- end }}{{/* end define */}} 94 | 95 | {{/* 96 | Common/recommended labels: https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/ 97 | */}} 98 | {{- define "cert-manager-webhook-ovh.labels" -}} 99 | helm.sh/chart: {{ include "cert-manager-webhook-ovh.chart" . }} 100 | app.kubernetes.io/component: webhook 101 | app.kubernetes.io/managed-by: {{ .Release.Service }} 102 | app.kubernetes.io/part-of: cert-manager 103 | {{ include "cert-manager-webhook-ovh.selectorLabels" . }} 104 | {{- if or .Chart.AppVersion .Values.image.tag }} 105 | app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} 106 | {{- end }} 107 | {{- end -}} 108 | 109 | {{/* 110 | Selector labels 111 | */}} 112 | {{- define "cert-manager-webhook-ovh.selectorLabels" -}} 113 | app.kubernetes.io/name: {{ include "cert-manager-webhook-ovh.name" . }} 114 | app.kubernetes.io/instance: {{ .Release.Name }} 115 | {{- end -}} -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/apiservice.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiregistration.k8s.io/v1 2 | kind: APIService 3 | metadata: 4 | name: v1alpha1.{{ .Values.groupName }} 5 | labels: 6 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 7 | annotations: 8 | cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "cert-manager-webhook-ovh.servingCertificate" . }}" 9 | spec: 10 | group: {{ .Values.groupName }} 11 | groupPriorityMinimum: 1000 12 | versionPriority: 15 13 | service: 14 | name: {{ include "cert-manager-webhook-ovh.fullname" . }} 15 | namespace: {{ .Release.Namespace }} 16 | version: v1alpha1 17 | -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "cert-manager-webhook-ovh.fullname" . }} 5 | namespace: {{ .Release.Namespace | quote }} 6 | labels: 7 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 8 | {{- with .Values.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | replicas: {{ .Values.replicas }} 14 | selector: 15 | matchLabels: 16 | {{- include "cert-manager-webhook-ovh.selectorLabels" $ | nindent 6 }} 17 | template: 18 | metadata: 19 | labels: 20 | {{- range $key, $value := .Values.podLabels }} 21 | {{ $key }}: {{ $value | quote }} 22 | {{- end }} 23 | {{- include "cert-manager-webhook-ovh.selectorLabels" $ | nindent 8 }} 24 | {{- with .Values.podAnnotations }} 25 | annotations: 26 | {{- toYaml . | nindent 8 }} 27 | {{- end }} 28 | spec: 29 | imagePullSecrets: 30 | {{ toYaml .Values.image.pullSecrets | indent 8 }} 31 | serviceAccountName: {{ include "cert-manager-webhook-ovh.fullname" . }} 32 | {{- if .Values.securityContext.enabled }} 33 | {{- with .Values.securityContext.pod }} 34 | securityContext: 35 | {{- toYaml . | nindent 8 }} 36 | {{- end }} 37 | {{- end }} 38 | {{- if .Values.hostNetwork }} 39 | hostNetwork: true 40 | {{- end }} 41 | containers: 42 | - name: {{ .Chart.Name }} 43 | image: "{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }}" 44 | imagePullPolicy: {{ .Values.image.pullPolicy }} 45 | args: 46 | - --secure-port={{ .Values.port | default 8443 }} 47 | - --tls-cert-file=/tls/tls.crt 48 | - --tls-private-key-file=/tls/tls.key 49 | env: 50 | - name: GROUP_NAME 51 | value: {{ .Values.groupName | quote }} 52 | {{- range $key, $val := .Values.environment }} 53 | - name: {{ $key }} 54 | value: {{ $val | quote }} 55 | {{- end}} 56 | ports: 57 | - name: https 58 | containerPort: {{ .Values.port | default 8443 }} 59 | protocol: TCP 60 | livenessProbe: 61 | httpGet: 62 | scheme: HTTPS 63 | path: /healthz 64 | port: https 65 | readinessProbe: 66 | httpGet: 67 | scheme: HTTPS 68 | path: /healthz 69 | port: https 70 | volumeMounts: 71 | - name: certs 72 | mountPath: /tls 73 | readOnly: true 74 | resources: 75 | {{ toYaml .Values.resources | indent 12 }} 76 | {{- if .Values.securityContext.enabled }} 77 | {{- with .Values.securityContext.container }} 78 | securityContext: 79 | {{- toYaml . | nindent 12 }} 80 | {{- end }} 81 | {{- end }} 82 | volumes: 83 | - name: certs 84 | secret: 85 | secretName: {{ include "cert-manager-webhook-ovh.servingCertificate" . }} 86 | {{- with .Values.nodeSelector }} 87 | nodeSelector: 88 | {{ toYaml . | indent 8 }} 89 | {{- end }} 90 | {{- with .Values.affinity }} 91 | affinity: 92 | {{ toYaml . | indent 8 }} 93 | {{- end }} 94 | {{- with .Values.tolerations }} 95 | tolerations: 96 | {{ toYaml . | indent 8 }} 97 | {{- end }} 98 | -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/issuer.yaml: -------------------------------------------------------------------------------- 1 | {{- range $.Values.issuers }} 2 | {{- if .create }} 3 | {{- if or (and (.ovhAuthentication) (.ovhAuthenticationRef)) (and (not .ovhAuthentication) (not .ovhAuthenticationRef)) }} 4 | {{ fail "Error: For each issuer you wish to create, you need to define either 'ovhAuthentication' or 'ovhAuthenticationRef'" }} 5 | {{- end }} 6 | apiVersion: cert-manager.io/v1 7 | kind: {{ .kind }} 8 | metadata: 9 | labels: 10 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 11 | name: {{ .name | quote }} 12 | {{- if eq .kind "Issuer" }} 13 | namespace: {{ .namespace | quote }} 14 | {{- end }} 15 | spec: 16 | acme: 17 | server: {{ .acmeServerUrl | default "https://acme-v02.api.letsencrypt.org/directory" | quote }} 18 | {{- if .email }} 19 | email: {{ .email | quote }} 20 | {{- end}} 21 | privateKeySecretRef: 22 | name: {{ printf "%s-account-key" .name | quote }} 23 | solvers: 24 | - dns01: 25 | cnameStrategy: {{ .cnameStrategy | default "None" | quote }} 26 | webhook: 27 | solverName: ovh 28 | groupName: {{ $.Values.groupName | quote }} 29 | config: 30 | endpoint: {{ .ovhEndpointName | quote }} 31 | {{- if eq (include "cert-manager-webhook-ovh.isOvhAuthenticationAvail" .ovhAuthentication) "true" }} 32 | applicationKeyRef: 33 | name: {{ printf "%s-ovh-credentials" .name }} 34 | key: "applicationKey" 35 | applicationSecretRef: 36 | name: {{ printf "%s-ovh-credentials" .name }} 37 | key: "applicationSecret" 38 | consumerKeyRef: 39 | name: {{ printf "%s-ovh-credentials" .name }} 40 | key: "consumerKey" 41 | {{- end }} 42 | {{- if eq (include "cert-manager-webhook-ovh.isOvhAuthenticationRefAvail" .ovhAuthenticationRef) "true" }} 43 | applicationKeyRef: 44 | name: {{ .ovhAuthenticationRef.applicationKeyRef.name }} 45 | key: {{ .ovhAuthenticationRef.applicationKeyRef.key }} 46 | applicationSecretRef: 47 | name: {{ .ovhAuthenticationRef.applicationSecretRef.name }} 48 | key: {{ .ovhAuthenticationRef.applicationSecretRef.key }} 49 | consumerKeyRef: 50 | name: {{ .ovhAuthenticationRef.consumerKeyRef.name }} 51 | key: {{ .ovhAuthenticationRef.consumerKeyRef.key }} 52 | {{- end }} 53 | --- 54 | {{- end }}{{/* end if .create */}} 55 | {{- end }}{{/* end range */}} 56 | -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/pki.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Create a selfsigned Issuer, in order to create a root CA certificate for 3 | # signing webhook serving certificates 4 | apiVersion: cert-manager.io/v1 5 | kind: Issuer 6 | metadata: 7 | name: {{ include "cert-manager-webhook-ovh.selfSignedIssuer" . }} 8 | namespace: {{ .Release.Namespace | quote }} 9 | labels: 10 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 11 | spec: 12 | selfSigned: {} 13 | 14 | --- 15 | 16 | # Generate a CA Certificate used to sign certificates for the webhook 17 | apiVersion: cert-manager.io/v1 18 | kind: Certificate 19 | metadata: 20 | name: {{ include "cert-manager-webhook-ovh.rootCACertificate" . }} 21 | namespace: {{ .Release.Namespace | quote }} 22 | labels: 23 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 24 | spec: 25 | secretName: {{ include "cert-manager-webhook-ovh.rootCACertificate" . }} 26 | duration: 43800h0m0s # 5y 27 | issuerRef: 28 | name: {{ include "cert-manager-webhook-ovh.selfSignedIssuer" . }} 29 | commonName: "ca.cert-manager-webhook-ovh.cert-manager" 30 | isCA: true 31 | 32 | --- 33 | 34 | # Create an Issuer that uses the above generated CA certificate to issue certs 35 | apiVersion: cert-manager.io/v1 36 | kind: Issuer 37 | metadata: 38 | name: {{ include "cert-manager-webhook-ovh.rootCAIssuer" . }} 39 | namespace: {{ .Release.Namespace | quote }} 40 | labels: 41 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 42 | spec: 43 | ca: 44 | secretName: {{ include "cert-manager-webhook-ovh.rootCACertificate" . }} 45 | 46 | --- 47 | 48 | # Finally, generate a serving certificate for the webhook to use 49 | apiVersion: cert-manager.io/v1 50 | kind: Certificate 51 | metadata: 52 | name: {{ include "cert-manager-webhook-ovh.servingCertificate" . }} 53 | namespace: {{ .Release.Namespace | quote }} 54 | labels: 55 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 56 | spec: 57 | secretName: {{ include "cert-manager-webhook-ovh.servingCertificate" . }} 58 | duration: 8760h0m0s # 1y 59 | issuerRef: 60 | name: {{ include "cert-manager-webhook-ovh.rootCAIssuer" . }} 61 | dnsNames: 62 | - {{ include "cert-manager-webhook-ovh.fullname" . }} 63 | - {{ include "cert-manager-webhook-ovh.fullname" . }}.{{ .Release.Namespace }} 64 | - {{ include "cert-manager-webhook-ovh.fullname" . }}.{{ .Release.Namespace }}.svc 65 | -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ include "cert-manager-webhook-ovh.fullname" . }} 5 | namespace: {{ .Release.Namespace | quote }} 6 | labels: 7 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 8 | --- 9 | # Grant the webhook permission to read the ConfigMap containing the Kubernetes 10 | # apiserver's requestheader-ca-certificate. 11 | # This ConfigMap is automatically created by the Kubernetes apiserver. 12 | apiVersion: rbac.authorization.k8s.io/v1 13 | kind: RoleBinding 14 | metadata: 15 | name: {{ include "cert-manager-webhook-ovh.fullname" . }}:webhook-authentication-reader 16 | namespace: kube-system 17 | labels: 18 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 19 | roleRef: 20 | apiGroup: rbac.authorization.k8s.io 21 | kind: Role 22 | name: extension-apiserver-authentication-reader 23 | subjects: 24 | - apiGroup: "" 25 | kind: ServiceAccount 26 | name: {{ include "cert-manager-webhook-ovh.fullname" . }} 27 | namespace: {{ .Release.Namespace | quote }} 28 | --- 29 | # apiserver gets the auth-delegator role to delegate auth decisions to 30 | # the core apiserver 31 | apiVersion: rbac.authorization.k8s.io/v1 32 | kind: ClusterRoleBinding 33 | metadata: 34 | name: {{ include "cert-manager-webhook-ovh.fullname" . }}:auth-delegator 35 | labels: 36 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 37 | roleRef: 38 | apiGroup: rbac.authorization.k8s.io 39 | kind: ClusterRole 40 | name: system:auth-delegator 41 | subjects: 42 | - apiGroup: "" 43 | kind: ServiceAccount 44 | name: {{ include "cert-manager-webhook-ovh.fullname" . }} 45 | namespace: {{ .Release.Namespace | quote }} 46 | --- 47 | # Grant cert-manager permission to validate using our apiserver 48 | apiVersion: rbac.authorization.k8s.io/v1 49 | kind: ClusterRole 50 | metadata: 51 | name: {{ include "cert-manager-webhook-ovh.fullname" . }}:domain-solver 52 | labels: 53 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 54 | rules: 55 | - apiGroups: 56 | - {{ .Values.groupName }} 57 | resources: 58 | - '*' 59 | verbs: 60 | - 'create' 61 | --- 62 | apiVersion: rbac.authorization.k8s.io/v1 63 | kind: ClusterRoleBinding 64 | metadata: 65 | name: {{ include "cert-manager-webhook-ovh.fullname" . }}:domain-solver 66 | labels: 67 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 68 | roleRef: 69 | apiGroup: rbac.authorization.k8s.io 70 | kind: ClusterRole 71 | name: {{ include "cert-manager-webhook-ovh.fullname" . }}:domain-solver 72 | subjects: 73 | - apiGroup: "" 74 | kind: ServiceAccount 75 | name: {{ .Values.certManager.serviceAccountName }} 76 | namespace: {{ .Values.certManager.namespace | quote }} 77 | --- 78 | apiVersion: rbac.authorization.k8s.io/v1 79 | kind: ClusterRole 80 | metadata: 81 | name: {{ include "cert-manager-webhook-ovh.fullname" . }}:flowcontrol-solver 82 | labels: 83 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 84 | rules: 85 | - apiGroups: 86 | - "flowcontrol.apiserver.k8s.io" 87 | resources: 88 | - 'prioritylevelconfigurations' 89 | - 'flowschemas' 90 | verbs: 91 | - 'list' 92 | - 'watch' 93 | --- 94 | apiVersion: rbac.authorization.k8s.io/v1 95 | kind: ClusterRoleBinding 96 | metadata: 97 | name: {{ include "cert-manager-webhook-ovh.fullname" . }}:flowcontrol-solver 98 | labels: 99 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 100 | roleRef: 101 | apiGroup: rbac.authorization.k8s.io 102 | kind: ClusterRole 103 | name: {{ include "cert-manager-webhook-ovh.fullname" . }}:flowcontrol-solver 104 | subjects: 105 | - apiGroup: "" 106 | kind: ServiceAccount 107 | name: {{ include "cert-manager-webhook-ovh.fullname" . }} 108 | namespace: {{ .Release.Namespace | quote }} 109 | --- 110 | {{- if gt (include "cert-manager-webhook-ovh.isIssuerToCreate" $ | int ) 0 }} 111 | {{- $secretsList := list }} 112 | {{- range $.Values.issuers }} 113 | {{- if .create }} 114 | {{- if eq .kind "ClusterIssuer" }} 115 | {{- if eq (include "cert-manager-webhook-ovh.isOvhAuthenticationAvail" .ovhAuthentication) "true" }} 116 | {{- $secretsList = append $secretsList (printf "%s-ovh-credentials" .name) | uniq }} 117 | {{- end }} 118 | {{- if eq (include "cert-manager-webhook-ovh.isOvhAuthenticationRefAvail" .ovhAuthenticationRef) "true" }} 119 | {{- $secretsList = append $secretsList .ovhAuthenticationRef.applicationKeyRef.name }} 120 | {{- $secretsList = append $secretsList .ovhAuthenticationRef.applicationSecretRef.name }} 121 | {{- $secretsList = append $secretsList .ovhAuthenticationRef.consumerKeyRef.name }} 122 | {{- end }} 123 | {{- end }}{{/* end if eq .kind "ClusterIssuer" */}} 124 | {{- end }}{{/* end if .create */}} 125 | {{- end }}{{/* end range */}} 126 | {{- if len $secretsList }} 127 | apiVersion: rbac.authorization.k8s.io/v1 128 | kind: {{ $.Values.rbac.roleType | default "Role" }} 129 | metadata: 130 | name: {{ include "cert-manager-webhook-ovh.fullname" $ }}:secret-reader 131 | namespace: {{ $.Release.Namespace | quote }} 132 | labels: 133 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 134 | rules: 135 | - apiGroups: [""] 136 | resources: ["secrets"] 137 | verbs: ["get", "watch"] 138 | resourceNames: 139 | {{- $secretsList = $secretsList | uniq | sortAlpha }} 140 | {{- range $secretsList }} 141 | - {{ . | quote }} 142 | {{- end }} 143 | --- 144 | {{- end }} 145 | {{- if len $secretsList }} 146 | apiVersion: rbac.authorization.k8s.io/v1 147 | kind: RoleBinding 148 | metadata: 149 | name: {{ include "cert-manager-webhook-ovh.fullname" $ }}:secret-reader 150 | namespace: {{ $.Release.Namespace | quote }} 151 | labels: 152 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 153 | roleRef: 154 | apiGroup: rbac.authorization.k8s.io 155 | kind: Role 156 | name: {{ include "cert-manager-webhook-ovh.fullname" $ }}:secret-reader 157 | subjects: 158 | - apiGroup: "" 159 | kind: ServiceAccount 160 | name: {{ include "cert-manager-webhook-ovh.fullname" $ }} 161 | namespace: {{ .Release.Namespace | quote }} 162 | --- 163 | {{- end }} 164 | {{- end }} 165 | {{- if gt (include "cert-manager-webhook-ovh.isIssuerToCreate" $ | int ) 0 }} 166 | {{- $namespaceList := list }} 167 | {{- range $.Values.issuers }} 168 | {{- if .create }} 169 | {{- if eq .kind "Issuer" }} 170 | {{- $namespaceList = append $namespaceList .namespace }} 171 | {{- end }} 172 | {{- end }} 173 | {{- end }} 174 | {{- $namespaceList = $namespaceList | uniq | sortAlpha }} 175 | {{- range $namespaceList }} 176 | {{- $namespace := . }} 177 | {{- $secretsList := list }} 178 | {{- range $.Values.issuers }} 179 | {{- if .create }} 180 | {{- if eq .kind "Issuer" }} 181 | {{- if eq $namespace .namespace }} 182 | {{- if eq (include "cert-manager-webhook-ovh.isOvhAuthenticationAvail" .ovhAuthentication) "true" }} 183 | {{- $secretsList = append $secretsList (printf "%s-ovh-credentials" .name) }} 184 | {{- end }} 185 | {{- if eq (include "cert-manager-webhook-ovh.isOvhAuthenticationRefAvail" .ovhAuthenticationRef) "true" }} 186 | {{- $secretsList = append $secretsList .ovhAuthenticationRef.applicationKeyRef.name }} 187 | {{- $secretsList = append $secretsList .ovhAuthenticationRef.applicationSecretRef.name }} 188 | {{- $secretsList = append $secretsList .ovhAuthenticationRef.consumerKeyRef.name }} 189 | {{- end }} 190 | {{- end }} 191 | {{- end }}{{/* end if eq .kind "Issuer" */}} 192 | {{- end }}{{/* end if .create */}} 193 | {{- end }}{{/* end range $.Values.issuers */}} 194 | {{- if len $secretsList }} 195 | apiVersion: rbac.authorization.k8s.io/v1 196 | kind: Role 197 | metadata: 198 | name: {{ include "cert-manager-webhook-ovh.fullname" $ }}:secret-reader 199 | namespace: {{ $namespace | quote }} 200 | labels: 201 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 202 | rules: 203 | - apiGroups: [""] 204 | resources: ["secrets"] 205 | verbs: ["get", "watch"] 206 | resourceNames: 207 | {{- $secretsList = $secretsList | uniq | sortAlpha }} 208 | {{- range $secretsList }} 209 | - {{ . | quote }} 210 | {{- end }} 211 | {{- end }} 212 | --- 213 | apiVersion: rbac.authorization.k8s.io/v1 214 | kind: RoleBinding 215 | metadata: 216 | name: {{ include "cert-manager-webhook-ovh.fullname" $ }}:secret-reader 217 | namespace: {{ $namespace | quote }} 218 | labels: 219 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 220 | roleRef: 221 | apiGroup: rbac.authorization.k8s.io 222 | kind: Role 223 | name: {{ include "cert-manager-webhook-ovh.fullname" $ }}:secret-reader 224 | subjects: 225 | - apiGroup: "" 226 | kind: ServiceAccount 227 | name: {{ include "cert-manager-webhook-ovh.fullname" $ }} 228 | namespace: {{ $.Release.Namespace | quote }} 229 | --- 230 | {{- end }}{{/* end $namespaceList */}} 231 | {{- end }} -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | {{- range $.Values.issuers }} 2 | {{- if .create }} 3 | {{- if or (and (.ovhAuthentication) (.ovhAuthenticationRef)) (and (not .ovhAuthentication) (not .ovhAuthenticationRef)) }} 4 | {{ fail "Error: For each issuer you wish to create, you need to define either 'ovhAuthentication' or 'ovhAuthenticationRef'" }} 5 | {{- end }} 6 | {{- if eq (include "cert-manager-webhook-ovh.isOvhAuthenticationAvail" .ovhAuthentication) "true" }} 7 | apiVersion: v1 8 | kind: Secret 9 | type: Opaque 10 | metadata: 11 | name: {{ printf "%s-ovh-credentials" .name }} 12 | {{- if eq .kind "Issuer" }} 13 | namespace: {{ .namespace | quote }} 14 | {{- else }} 15 | namespace: {{ $.Release.Namespace | quote }} 16 | {{- end }} 17 | labels: 18 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 19 | data: 20 | applicationKey: {{ .ovhAuthentication.applicationKey | b64enc | quote }} 21 | applicationSecret: {{ .ovhAuthentication.applicationSecret | b64enc | quote }} 22 | consumerKey: {{ .ovhAuthentication.consumerKey | b64enc | quote }} 23 | --- 24 | {{- end }}{{/* end if eq (include "cert..." */}} 25 | {{- end }}{{/* end if .create */}} 26 | {{- end }}{{/* end range */}} -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "cert-manager-webhook-ovh.fullname" . }} 5 | namespace: {{ .Release.Namespace | quote }} 6 | labels: 7 | {{- include "cert-manager-webhook-ovh.labels" $ | nindent 4 }} 8 | spec: 9 | type: {{ .Values.service.type }} 10 | ports: 11 | - port: {{ .Values.service.port }} 12 | targetPort: https 13 | protocol: TCP 14 | name: https 15 | selector: 16 | {{- include "cert-manager-webhook-ovh.selectorLabels" $ | nindent 4 }} 17 | -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/templates/version.yaml: -------------------------------------------------------------------------------- 1 | {{- $expectedVersion := "0.0.1" -}} 2 | {{- if .Values.configVersion -}} 3 | {{- $configVersion := .Values.configVersion | trimAll " " -}} 4 | {{- if $configVersion -}} 5 | {{- if (semverCompare (printf "!= %s" $expectedVersion) $configVersion) -}} 6 | {{- fail (printf "Error: Incorrect configVersion.\nExpected: '%s'\nProvided: '%s'\nPlease upgrade your 'values.yaml' file accordingly." $expectedVersion $configVersion) -}} 7 | {{- end -}} 8 | {{- else -}} 9 | {{ fail (printf "Error: The configVersion cannot be empty in your `values.yaml`. Please provide a valid configVersion.\nRetrieve the latest `values.yaml` using:\n\thelm show values cert-manager-webhook-ovh-charts/cert-manager-webhook-ovh") }} 10 | {{- end -}} 11 | {{- else -}} 12 | {{ fail (printf "Error: The configVersion cannot be empty in your `values.yaml`. Please provide a valid configVersion.\nRetrieve the latest `values.yaml` using:\n\thelm show values cert-manager-webhook-ovh-charts/cert-manager-webhook-ovh") }} 13 | {{- end -}} 14 | -------------------------------------------------------------------------------- /charts/cert-manager-webhook-ovh/values.yaml: -------------------------------------------------------------------------------- 1 | # The `configVersion` is used to assist with breaking changes before an upgrade 2 | # is applied. 3 | # Each time breaking change are introduced, the internal `configVersion` is 4 | # increased. If the user doesn't set the correct `configVersion` in their 5 | # `values.yaml` indicating they have performed the required configuration 6 | # changes, the new Chart version won't be installed preventing any outages. 7 | # The latest `values.yaml` contains the correct `configVersion` but the line is 8 | # commented. The latest `values.yaml` can be retrieved with the command below: 9 | # helm show values cert-manager-webhook-ovh-charts/cert-manager-webhook-ovh 10 | # configVersion: 0.0.1 11 | 12 | # The GroupName here is used to identify your company or business unit that 13 | # created this webhook. 14 | # For example, this may be "acme.mycompany.example". 15 | # This name will need to be referenced in each Issuer's `webhook` stanza to 16 | # inform cert-manager of where to send ChallengePayload resources in order to 17 | # solve the DNS01 challenge. 18 | # This group name should be **unique**, hence using your own company's domain 19 | # here is recommended. 20 | groupName: acme.mycompany.example 21 | 22 | # The values below should match your cert-manager deployment. 23 | # If not, you will get permissions errors. 24 | certManager: 25 | # namespace in which your cert-manager is deployed 26 | namespace: cert-manager 27 | # cert-manager serverAccount name (default: cert-manager) 28 | serviceAccountName: cert-manager 29 | 30 | issuers: 31 | # Name of this issuer 32 | - name: le-prod 33 | # When true this issuer is created. 34 | # This is disabled by default 35 | create: false 36 | # Type of issuer. Can either be ClusterIssuer or Issuer. 37 | # If issuer is specified, the namespace is required. 38 | # See for more information https://cert-manager.io/docs/concepts/issuer/ 39 | kind: ClusterIssuer 40 | # If kind is issuer, then indicate the namespace in which this 41 | # issuer should be deployed into. 42 | namespace: default 43 | # Follow CNAME records or not. 2 options: 44 | # - None (default): Don't follow CNAME records 45 | # - Follow: Follow CNAME records 46 | # See https://cert-manager.io/docs/configuration/acme/dns01/#delegated-domains-for-dns01 for more information 47 | cnameStrategy: None 48 | # The acme server url. For Let's encrypt, you have 2 options: 49 | # - for production (default): https://acme-v02.api.letsencrypt.org/directory 50 | # - for staging: https://acme-staging-v02.api.letsencrypt.org/directory 51 | acmeServerUrl: https://acme-v02.api.letsencrypt.org/directory 52 | # email to use when registrering your account with Let's encrypt. 53 | email: acme@example.net 54 | # The endpoint name of the OVH API. It must be one of the following: 55 | # ovh-eu, ovh-ca, kimsufi-eu, kimsufi-ca, soyoustart-eu, soyoustart-ca, runabove-ca 56 | # See https://docs.certifytheweb.com/docs/dns/providers/ovh/ for more information 57 | ovhEndpointName: ovh-eu 58 | # define how the webhook should authenticate with the OVH API. 59 | # Either set your credentials and the chart will create the necessary secret for you. 60 | # Or provide the details of a secret already containing the OVH credentials. 61 | # ovhAuthentication: 62 | # # the OVH application key. Leave emtpy if you are using an existing secret. 63 | # applicationKey: '' 64 | # # the OVH application secret. Leave emtpy if you are using an existing secret. 65 | # applicationSecret: '' 66 | # # Your OVH consumer key. Leave emtpy if you are using an existing secret. 67 | # consumerKey: '' 68 | 69 | # If 'kind' is a ClusterIssuer, then the secret needs to be present 70 | # in the same namespace as this deployment. If 'kind' is an Issuer, 71 | # then the secret needs to be present in the same namespace as the Issuer. 72 | # ovhAuthenticationRef: 73 | # # The secret reference to an existing OVH application key. 74 | # applicationKeyRef: 75 | # # Name of the Kubernetes secret containing the OVH Application Key 76 | # name: ovh-credentials 77 | # # The key name in the secret above that holds the actual OVH application key value 78 | # key: applicationKey 79 | # # The secret reference to an existing OVH application secret. 80 | # applicationSecretRef: 81 | # # Name of the Kubernetes secret containing the OVH Application Secret 82 | # name: ovh-credentials 83 | # # The key name in the secret above that holds the actual OVH application secret value 84 | # key: applicationSecret 85 | # # The secret reference to an existing OVH consumer key 86 | # consumerKeyRef: 87 | # # Name of the Kubernetes secret containing the OVH consumer Key 88 | # name: ovh-credentials 89 | # # The key name in the secret above that holds the actual OVH consumer key value 90 | # key: consumerKey 91 | 92 | # Name of this issuer 93 | - name: le-staging 94 | # When true this issuer is created. 95 | # This is disabled by default 96 | create: false 97 | # Type of issuer. Can either be ClusterIssuer or Issuer. 98 | # If issuer is specified, the the namespace is required. 99 | # See for more information https://cert-manager.io/docs/concepts/issuer/ 100 | kind: ClusterIssuer 101 | # If kind is issuer, then indicate the namespace in which this 102 | # issuer should be deployed into. 103 | namespace: default 104 | # Follow CNAME records or not. 2 options: 105 | # - None (default): Don't follow CNAME records 106 | # - Follow: Follow CNAME records 107 | # See https://cert-manager.io/docs/configuration/acme/dns01/#delegated-domains-for-dns01 for more information 108 | cnameStrategy: None 109 | # The acme server url. For Let's encrypt, you have 2 options: 110 | # - for production (default): https://acme-v02.api.letsencrypt.org/directory 111 | # - for staging: https://acme-staging-v02.api.letsencrypt.org/directory 112 | acmeServerUrl: https://acme-staging-v02.api.letsencrypt.org/directory 113 | # email to use when registering your account with Let's encrypt. 114 | email: acme@example.net 115 | # The endpoint name of the OVH API. It must be one of the following: 116 | # ovh-eu, ovh-ca, kimsufi-eu, kimsufi-ca, soyoustart-eu, soyoustart-ca, runabove-ca 117 | # See https://docs.certifytheweb.com/docs/dns/providers/ovh/ for more information 118 | ovhEndpointName: ovh-eu 119 | # define how the webhook should authenticate with the OVH API. 120 | # Either set your credentials and the chart will create the necessary secret for you. 121 | # Or provide the details of a secret already containing the OVH credentials. 122 | # ovhAuthentication: 123 | # # the OVH application key. Leave emtpy if you are using an existing secret. 124 | # applicationKey: '' 125 | # # the OVH application secret. Leave emtpy if you are using an existing secret. 126 | # applicationSecret: '' 127 | # # Your OVH consumer key. Leave emtpy if you are using an existing secret. 128 | # consumerKey: '' 129 | 130 | # If 'kind' is a ClusterIssuer, then the secret needs to be present 131 | # in the same namespace as this deployment. If 'kind' is an Issuer, 132 | # then the secret needs to be present in the same namespace as the Issuer. 133 | # ovhAuthenticationRef: 134 | # # The secret reference to an existing OVH application key. 135 | # applicationKeyRef: 136 | # # Name of the Kubernetes secret containing the OVH Application Key 137 | # name: ovh-credentials 138 | # # The key name in the secret above that holds the actual OVH application key value 139 | # key: applicationSecret 140 | # # The secret reference to an existing OVH application secret. 141 | # applicationSecretRef: 142 | # # Name of the Kubernetes secret containing the OVH Application Secret 143 | # name: ovh-credentials 144 | # # The key name in the secret above that holds the actual OVH application secret value 145 | # key: applicationSecret 146 | # # The secret reference to an existing OVH consumer key 147 | # consumerKeyRef: 148 | # # Name of the Kubernetes secret containing the OVH consumer Key 149 | # name: ovh-credentials 150 | # # The key name in the secret above that holds the actual OVH consumer key value 151 | # key: consumerKey 152 | 153 | rbac: 154 | # If the secret of the issuer is in a different namespace, change `Role` (default) 155 | # to `ClusterRole` so the webhook is able to access this secret. 156 | roleType: Role 157 | 158 | # If enabled (default) the webhook will run as the specified user and group. 159 | # Furthermore, the specified allowPrivilegeEscalation and readOnlyRootFilesystem are applied to the container. 160 | # pod securityContext and container securityContext will be inserted as-is in their respective places so that 161 | # it is possible to edit any supported field (e.g. capabilities, seccompProfile, privileged) 162 | securityContext: 163 | enabled: true 164 | pod: 165 | runAsUser: 1000 166 | runAsGroup: 1000 167 | container: 168 | allowPrivilegeEscalation: false 169 | readOnlyRootFilesystem: true 170 | 171 | image: 172 | repository: ghcr.io/aureq/cert-manager-webhook-ovh 173 | # tag: "" 174 | pullPolicy: IfNotPresent 175 | # Use this set to assign a list of default pullSecrets 176 | pullSecrets: [] 177 | # - name: docker-registry-secret 178 | # - name: internal-registry-secret 179 | 180 | nameOverride: "" 181 | fullnameOverride: "" 182 | 183 | # Use this field to add environment variables relevant to this webhook. 184 | # These fields will be passed on to the container when Chart is deployed. 185 | environment: 186 | # Use these variables to configure the HTTP_PROXY environment variables 187 | # HTTP_PROXY: "http://proxy:8080" 188 | # HTTPS_PROXY: "http://proxy:8080" 189 | # NO_PROXY: "127.0.0.1,localhost,10.0.0.0/8" 190 | 191 | # number of replicas in this deployment 192 | replicas: 1 193 | 194 | service: 195 | type: ClusterIP 196 | port: 443 197 | 198 | # Specifies if the webhook should be started in hostNetwork mode. 199 | # 200 | # Required for use in some managed kubernetes clusters (such as AWS EKS) with custom 201 | # CNI (such as calico), because control-plane managed by AWS cannot communicate 202 | # with pods' IP CIDR and admission webhooks are not working 203 | hostNetwork: false 204 | 205 | # The port used by the application to expose the service. 206 | # It may be useful to change it if the hostNetwork mode is activated to use an available port. 207 | port: 8443 208 | 209 | resources: {} 210 | # We usually recommend not to specify default resources and to leave this as a conscious 211 | # choice for the user. This also increases chances charts run on environments with little 212 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 213 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 214 | #requests: 215 | # cpu: 100m 216 | # memory: 64Mi 217 | #limits: 218 | # cpu: 200m 219 | # memory: 96Mi 220 | 221 | # see https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ 222 | nodeSelector: {} 223 | 224 | # see https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ 225 | tolerations: [] 226 | 227 | # see https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/ 228 | affinity: {} 229 | 230 | # see https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ 231 | # add annotations to the Deployment 232 | annotations: {} 233 | 234 | # see https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ 235 | # add annotations to the Pod 236 | podAnnotations: {} 237 | 238 | # see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ 239 | # add labels to the Pod 240 | podLabels: {} 241 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/aureq/cert-manager-webhook-ovh 2 | 3 | go 1.24.0 4 | 5 | toolchain go1.24.3 6 | 7 | require ( 8 | github.com/cert-manager/cert-manager v1.17.2 9 | github.com/ovh/go-ovh v1.7.0 10 | k8s.io/api v0.32.5 11 | k8s.io/apiextensions-apiserver v0.32.5 12 | k8s.io/apimachinery v0.32.5 13 | k8s.io/client-go v0.32.5 14 | ) 15 | 16 | require ( 17 | cel.dev/expr v0.24.0 // indirect 18 | github.com/NYTimes/gziphandler v1.1.1 // indirect 19 | github.com/antlr4-go/antlr/v4 v4.13.1 // indirect 20 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 21 | github.com/beorn7/perks v1.0.1 // indirect 22 | github.com/blang/semver/v4 v4.0.0 // indirect 23 | github.com/cenkalti/backoff/v5 v5.0.2 // indirect 24 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 25 | github.com/coreos/go-semver v0.3.1 // indirect 26 | github.com/coreos/go-systemd/v22 v22.5.0 // indirect 27 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 28 | github.com/emicklei/go-restful/v3 v3.12.2 // indirect 29 | github.com/evanphx/json-patch/v5 v5.9.11 // indirect 30 | github.com/felixge/httpsnoop v1.0.4 // indirect 31 | github.com/fsnotify/fsnotify v1.9.0 // indirect 32 | github.com/fxamacker/cbor/v2 v2.8.0 // indirect 33 | github.com/go-logr/logr v1.4.2 // indirect 34 | github.com/go-logr/stdr v1.2.2 // indirect 35 | github.com/go-logr/zapr v1.3.0 // indirect 36 | github.com/go-openapi/jsonpointer v0.21.1 // indirect 37 | github.com/go-openapi/jsonreference v0.21.0 // indirect 38 | github.com/go-openapi/swag v0.23.1 // indirect 39 | github.com/gogo/protobuf v1.3.2 // indirect 40 | github.com/golang/protobuf v1.5.4 // indirect 41 | github.com/google/btree v1.1.3 // indirect 42 | github.com/google/cel-go v0.22.1 // indirect 43 | github.com/google/gnostic-models v0.6.9 // indirect 44 | github.com/google/go-cmp v0.7.0 // indirect 45 | github.com/google/gofuzz v1.2.0 // indirect 46 | github.com/google/uuid v1.6.0 // indirect 47 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect 48 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect 49 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 50 | github.com/josharian/intern v1.0.0 // indirect 51 | github.com/json-iterator/go v1.1.12 // indirect 52 | github.com/kylelemons/godebug v1.1.0 // indirect 53 | github.com/mailru/easyjson v0.9.0 // indirect 54 | github.com/miekg/dns v1.1.66 // indirect 55 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 56 | github.com/modern-go/reflect2 v1.0.2 // indirect 57 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 58 | github.com/pkg/errors v0.9.1 // indirect 59 | github.com/prometheus/client_golang v1.22.0 // indirect 60 | github.com/prometheus/client_model v0.6.2 // indirect 61 | github.com/prometheus/common v0.64.0 // indirect 62 | github.com/prometheus/procfs v0.16.1 // indirect 63 | github.com/spf13/cobra v1.9.1 // indirect 64 | github.com/spf13/pflag v1.0.6 // indirect 65 | github.com/stoewer/go-strcase v1.3.0 // indirect 66 | github.com/x448/float16 v0.8.4 // indirect 67 | go.etcd.io/etcd/api/v3 v3.6.0 // indirect 68 | go.etcd.io/etcd/client/pkg/v3 v3.6.0 // indirect 69 | go.etcd.io/etcd/client/v3 v3.6.0 // indirect 70 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 71 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect 72 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect 73 | go.opentelemetry.io/otel v1.36.0 // indirect 74 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect 75 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect 76 | go.opentelemetry.io/otel/metric v1.36.0 // indirect 77 | go.opentelemetry.io/otel/sdk v1.36.0 // indirect 78 | go.opentelemetry.io/otel/trace v1.36.0 // indirect 79 | go.opentelemetry.io/proto/otlp v1.6.0 // indirect 80 | go.uber.org/multierr v1.11.0 // indirect 81 | go.uber.org/zap v1.27.0 // indirect 82 | golang.org/x/crypto v0.38.0 // indirect 83 | golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect 84 | golang.org/x/mod v0.24.0 // indirect 85 | golang.org/x/net v0.40.0 // indirect 86 | golang.org/x/oauth2 v0.30.0 // indirect 87 | golang.org/x/sync v0.14.0 // indirect 88 | golang.org/x/sys v0.33.0 // indirect 89 | golang.org/x/term v0.32.0 // indirect 90 | golang.org/x/text v0.25.0 // indirect 91 | golang.org/x/time v0.11.0 // indirect 92 | golang.org/x/tools v0.33.0 // indirect 93 | gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect 94 | google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect 95 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect 96 | google.golang.org/grpc v1.72.1 // indirect 97 | google.golang.org/protobuf v1.36.6 // indirect 98 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 99 | gopkg.in/inf.v0 v0.9.1 // indirect 100 | gopkg.in/ini.v1 v1.67.0 // indirect 101 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 102 | gopkg.in/yaml.v3 v3.0.1 // indirect 103 | k8s.io/apiserver v0.32.5 // indirect 104 | k8s.io/component-base v0.32.5 // indirect 105 | k8s.io/klog/v2 v2.130.1 // indirect 106 | k8s.io/kms v0.33.1 // indirect 107 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect 108 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect 109 | sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 // indirect 110 | sigs.k8s.io/controller-runtime v0.20.4 // indirect 111 | sigs.k8s.io/gateway-api v1.3.0 // indirect 112 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect 113 | sigs.k8s.io/randfill v1.0.0 // indirect 114 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect 115 | sigs.k8s.io/yaml v1.4.0 // indirect 116 | ) 117 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= 2 | cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= 3 | github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= 4 | github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= 5 | github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= 6 | github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= 7 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= 8 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 9 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 | github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= 12 | github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 13 | github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= 14 | github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= 15 | github.com/cert-manager/cert-manager v1.17.2 h1:QQYTEOsHf/Z3BFzKH2sIILHJwZA5Ut0LYZlHyNViupg= 16 | github.com/cert-manager/cert-manager v1.17.2/go.mod h1:2TmjsTQF8GZqc8fgLhXWCfbA6YwWCUHKxerJNbFh9eU= 17 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 18 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 19 | github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= 20 | github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= 21 | github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= 22 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 23 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 24 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 27 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 28 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 29 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 30 | github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= 31 | github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 32 | github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= 33 | github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 34 | github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= 35 | github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= 36 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 37 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 38 | github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= 39 | github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 40 | github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= 41 | github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= 42 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 43 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 44 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 45 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 46 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 47 | github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= 48 | github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= 49 | github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= 50 | github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= 51 | github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= 52 | github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= 53 | github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= 54 | github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= 55 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 56 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 57 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 58 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 59 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 60 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 61 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 62 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 63 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 64 | github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= 65 | github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= 66 | github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40= 67 | github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= 68 | github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= 69 | github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= 70 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 71 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 72 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 73 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 74 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 75 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 76 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= 77 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 78 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 79 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 80 | github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= 81 | github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 82 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= 83 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= 84 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= 85 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 86 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= 87 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 88 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= 89 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= 90 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 91 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 92 | github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= 93 | github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= 94 | github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= 95 | github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= 96 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 97 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 98 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 99 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 100 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 101 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 102 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 103 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 104 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 105 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 106 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 107 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 108 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 109 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 110 | github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= 111 | github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= 112 | github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= 113 | github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= 114 | github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= 115 | github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= 116 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 117 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 118 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 119 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 120 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 121 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 122 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 123 | github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= 124 | github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= 125 | github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= 126 | github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 127 | github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw= 128 | github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= 129 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 130 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 131 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 132 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 133 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 134 | github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= 135 | github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= 136 | github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= 137 | github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= 138 | github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= 139 | github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= 140 | github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= 141 | github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= 142 | github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 143 | github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 144 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 145 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 146 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 147 | github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= 148 | github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= 149 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 150 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 151 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 152 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 153 | github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= 154 | github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= 155 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 156 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 157 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 158 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 159 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 160 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 161 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 162 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 163 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 164 | github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= 165 | github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= 166 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 167 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 168 | github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= 169 | github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 170 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 171 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 172 | go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= 173 | go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= 174 | go.etcd.io/etcd/api/v3 v3.6.0 h1:vdbkcUBGLf1vfopoGE/uS3Nv0KPyIpUV/HM6w9yx2kM= 175 | go.etcd.io/etcd/api/v3 v3.6.0/go.mod h1:Wt5yZqEmxgTNJGHob7mTVBJDZNXiHPtXTcPab37iFOw= 176 | go.etcd.io/etcd/client/pkg/v3 v3.6.0 h1:nchnPqpuxvv3UuGGHaz0DQKYi5EIW5wOYsgUNRc365k= 177 | go.etcd.io/etcd/client/pkg/v3 v3.6.0/go.mod h1:Jv5SFWMnGvIBn8o3OaBq/PnT0jjsX8iNokAUessNjoA= 178 | go.etcd.io/etcd/client/v2 v2.305.16 h1:kQrn9o5czVNaukf2A2At43cE9ZtWauOtf9vRZuiKXow= 179 | go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE= 180 | go.etcd.io/etcd/client/v3 v3.6.0 h1:/yjKzD+HW5v/3DVj9tpwFxzNbu8hjcKID183ug9duWk= 181 | go.etcd.io/etcd/client/v3 v3.6.0/go.mod h1:Jzk/Knqe06pkOZPHXsQ0+vNDvMQrgIqJ0W8DwPdMJMg= 182 | go.etcd.io/etcd/pkg/v3 v3.5.16 h1:cnavs5WSPWeK4TYwPYfmcr3Joz9BH+TZ6qoUtz6/+mc= 183 | go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY= 184 | go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk= 185 | go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI= 186 | go.etcd.io/etcd/server/v3 v3.5.16 h1:d0/SAdJ3vVsZvF8IFVb1k8zqMZ+heGcNfft71ul9GWE= 187 | go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s= 188 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 189 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 190 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= 191 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= 192 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= 193 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= 194 | go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= 195 | go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= 196 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= 197 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= 198 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= 199 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= 200 | go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= 201 | go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= 202 | go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= 203 | go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= 204 | go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= 205 | go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= 206 | go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= 207 | go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= 208 | go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= 209 | go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= 210 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 211 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 212 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 213 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 214 | go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= 215 | go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= 216 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 217 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 218 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 219 | golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= 220 | golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= 221 | golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= 222 | golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= 223 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 224 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 225 | golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= 226 | golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= 227 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 228 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 229 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 230 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 231 | golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= 232 | golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= 233 | golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= 234 | golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= 235 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 236 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 237 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 238 | golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= 239 | golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 240 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 241 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 242 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 243 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 244 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 245 | golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= 246 | golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= 247 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 248 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 249 | golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= 250 | golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 251 | golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= 252 | golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 253 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 254 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 255 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 256 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 257 | golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= 258 | golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= 259 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 260 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 261 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 262 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 263 | gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= 264 | gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= 265 | google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= 266 | google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= 267 | google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= 268 | google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= 269 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= 270 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= 271 | google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= 272 | google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= 273 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 274 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 275 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 276 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 277 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 278 | gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= 279 | gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= 280 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 281 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 282 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 283 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 284 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 285 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 286 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 287 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 288 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 289 | k8s.io/api v0.32.5 h1:uqjjsYo1kTJr5NIcoIaP9F+TgXgADH7nKQx91FDAhtk= 290 | k8s.io/api v0.32.5/go.mod h1:bXXFU3fGCZ/eFMZvfHZC69PeGbXEL4zzjuPVzOxHF64= 291 | k8s.io/apiextensions-apiserver v0.32.5 h1:o0aKvmzIIs8Uk54pidk32pxET+Pg2ULnh9WI1PuKTwE= 292 | k8s.io/apiextensions-apiserver v0.32.5/go.mod h1:5fpedJa3HJJFBukAZ6ur91DEDye5gYuXISPbOiNLYpU= 293 | k8s.io/apimachinery v0.32.5 h1:6We3aJ6crC0ap8EhsEXcgX3LpI6SEjubpiOMXLROwPM= 294 | k8s.io/apimachinery v0.32.5/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= 295 | k8s.io/apiserver v0.32.5 h1:phmm2EOUVFI+cLiq8Grtuh166fTt/qgvkGPkpgzp5uY= 296 | k8s.io/apiserver v0.32.5/go.mod h1:5bfueS1tgARVWVXRJBMI5mHoCmev0jOvbxebai/kiqc= 297 | k8s.io/client-go v0.32.5 h1:huFmQMzgWu0z4kbWsuZci+Gt4Fo72I4CcrvhToZ/Qp0= 298 | k8s.io/client-go v0.32.5/go.mod h1:Qchw6f9WIVrur7DKojAHpRgGLcANT0RLIvF39Jz58xA= 299 | k8s.io/component-base v0.32.5 h1:2HiX+m3s9Iz5CMqdCVDH2V942UqzQvjuhcXb4W+KCsg= 300 | k8s.io/component-base v0.32.5/go.mod h1:jDsPNFFElv9m27TcYxlpEX7TZ3vdgx2g4PaqMUHpV/Y= 301 | k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= 302 | k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 303 | k8s.io/kms v0.33.1 h1:jJKrFhsbVofpyLF+G8k+drwOAF9CMQpxilHa5Uilb8Q= 304 | k8s.io/kms v0.33.1/go.mod h1:C1I8mjFFBNzfUZXYt9FZVJ8MJl7ynFbGgZFbBzkBJ3E= 305 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= 306 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= 307 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= 308 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 309 | sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 h1:XotDXzqvJ8Nx5eiZZueLpTuafJz8SiodgOemI+w87QU= 310 | sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= 311 | sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU= 312 | sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= 313 | sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M= 314 | sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk= 315 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= 316 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= 317 | sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= 318 | sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= 319 | sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= 320 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= 321 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= 322 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 323 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 324 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "os" 9 | "strconv" 10 | "strings" 11 | 12 | corev1 "k8s.io/api/core/v1" 13 | extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 14 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | "k8s.io/client-go/kubernetes" 16 | "k8s.io/client-go/rest" 17 | 18 | "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1" 19 | "github.com/cert-manager/cert-manager/pkg/acme/webhook/cmd" 20 | "github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/util" 21 | logf "github.com/cert-manager/cert-manager/pkg/logs" 22 | "github.com/ovh/go-ovh/ovh" 23 | ) 24 | 25 | var GroupName = os.Getenv("GROUP_NAME") 26 | 27 | func main() { 28 | logf.Log.Info("Webhook starting...") 29 | 30 | if GroupName == "" { 31 | panic("GROUP_NAME must be specified") 32 | } 33 | 34 | // This will register our ovh DNS provider with the webhook serving 35 | // library, making it available as an API under the provided GroupName. 36 | // You can register multiple DNS provider implementations with a single 37 | // webhook, where the Name() method will be used to disambiguate between 38 | // the different implementations. 39 | cmd.RunWebhookServer(GroupName, 40 | &ovhDNSProviderSolver{}, 41 | ) 42 | } 43 | 44 | // ovhDNSProviderSolver implements the provider-specific logic needed to 45 | // 'present' an ACME challenge TXT record for your own DNS provider. 46 | // To do so, it must implement the `github.com/jetstack/cert-manager/pkg/acme/webhook.Solver` 47 | // interface. 48 | type ovhDNSProviderSolver struct { 49 | client *kubernetes.Clientset 50 | } 51 | 52 | // ovhDNSProviderConfig is a structure that is used to decode into when 53 | // solving a DNS01 challenge. 54 | // This information is provided by cert-manager, and may be a reference to 55 | // additional configuration that's needed to solve the challenge for this 56 | // particular certificate or issuer. 57 | // This typically includes references to Secret resources containing DNS 58 | // provider credentials, in cases where a 'multi-tenant' DNS solver is being 59 | // created. 60 | // If you do *not* require per-issuer or per-certificate configuration to be 61 | // provided to your webhook, you can skip decoding altogether in favour of 62 | // using CLI flags or similar to provide configuration. 63 | // You should not include sensitive information here. If credentials need to 64 | // be used by your provider here, you should reference a Kubernetes Secret 65 | // resource and fetch these credentials using a Kubernetes clientset. 66 | type ovhDNSProviderConfig struct { 67 | Endpoint string `json:"endpoint"` 68 | ApplicationKeyRef corev1.SecretKeySelector `json:"applicationKeyRef"` 69 | ApplicationSecretRef corev1.SecretKeySelector `json:"applicationSecretRef"` 70 | ConsumerKeyRef corev1.SecretKeySelector `json:"consumerKeyRef"` 71 | } 72 | 73 | type ovhZoneStatus struct { 74 | IsDeployed bool `json:"isDeployed"` 75 | } 76 | 77 | type ovhZoneRecord struct { 78 | Id int64 `json:"id,omitempty"` 79 | FieldType string `json:"fieldType"` 80 | SubDomain string `json:"subDomain"` 81 | Target string `json:"target"` 82 | TTL int `json:"ttl,omitempty"` 83 | } 84 | 85 | // Name is used as the name for this DNS solver when referencing it on the ACME 86 | // Issuer resource. 87 | // This should be unique **within the group name**, i.e. you can have two 88 | // solvers configured with the same Name() **so long as they do not co-exist 89 | // within a single webhook deployment**. 90 | // For example, `cloudflare` may be used as the name of a solver. 91 | func (s *ovhDNSProviderSolver) Name() string { 92 | return "ovh" 93 | } 94 | 95 | func (s *ovhDNSProviderSolver) validate(cfg *ovhDNSProviderConfig, allowAmbientCredentials bool) error { 96 | logf.Log.Info("Validating provider config...") 97 | 98 | if allowAmbientCredentials { 99 | // When allowAmbientCredentials is true, OVH client can load missing config 100 | // values from the environment variables and the ovh.conf files. 101 | return nil 102 | } 103 | if cfg.Endpoint == "" { 104 | return errors.New("no endpoint provided in OVH config") 105 | } 106 | if cfg.ApplicationKeyRef.Name == "" { 107 | return errors.New("no application key provided in OVH config") 108 | } 109 | if cfg.ApplicationSecretRef.Name == "" { 110 | return errors.New("no application secret provided in OVH config") 111 | } 112 | if cfg.ConsumerKeyRef.Name == "" { 113 | return errors.New("no consumer key provided in OVH config") 114 | } 115 | logf.Log.Info("Provider config: passed.") 116 | return nil 117 | } 118 | 119 | func (s *ovhDNSProviderSolver) ovhClient(ch *v1alpha1.ChallengeRequest) (*ovh.Client, error) { 120 | logf.Log.Info("Starting challenge request...") 121 | cfg, err := loadConfig(ch.Config) 122 | if err != nil { 123 | return nil, err 124 | } 125 | 126 | logf.Log.Info(fmt.Sprintf("Resource namespace: %s", ch.ResourceNamespace)) 127 | 128 | err = s.validate(&cfg, ch.AllowAmbientCredentials) 129 | if err != nil { 130 | return nil, err 131 | } 132 | 133 | applicationKey, err := s.secret(cfg.ApplicationKeyRef, ch.ResourceNamespace) 134 | if err != nil { 135 | return nil, err 136 | } 137 | 138 | applicationSecret, err := s.secret(cfg.ApplicationSecretRef, ch.ResourceNamespace) 139 | if err != nil { 140 | return nil, err 141 | } 142 | 143 | consumerKey, err := s.secret(cfg.ConsumerKeyRef, ch.ResourceNamespace) 144 | if err != nil { 145 | return nil, err 146 | } 147 | 148 | return ovh.NewClient(cfg.Endpoint, applicationKey, applicationSecret, consumerKey) 149 | } 150 | 151 | func (s *ovhDNSProviderSolver) secret(ref corev1.SecretKeySelector, namespace string) (string, error) { 152 | if ref.Name == "" { 153 | return "", nil 154 | } 155 | 156 | secret, err := s.client.CoreV1().Secrets(namespace).Get(context.TODO(), ref.Name, metav1.GetOptions{}) 157 | if err != nil { 158 | return "", err 159 | } 160 | 161 | bytes, ok := secret.Data[ref.Key] 162 | if !ok { 163 | return "", fmt.Errorf("key not found %q in secret '%s/%s'", ref.Key, namespace, ref.Name) 164 | } 165 | return strings.TrimSuffix(string(bytes), "\n"), nil 166 | } 167 | 168 | // Present is responsible for actually presenting the DNS record with the 169 | // DNS provider. 170 | // This method should tolerate being called multiple times with the same value. 171 | // cert-manager itself will later perform a self check to ensure that the 172 | // solver has correctly configured the DNS provider. 173 | func (s *ovhDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error { 174 | ovhClient, err := s.ovhClient(ch) 175 | if err != nil { 176 | return err 177 | } 178 | domain := util.UnFqdn(ch.ResolvedZone) 179 | subDomain := getSubDomain(domain, ch.ResolvedFQDN) 180 | target := ch.Key 181 | return addTXTRecord(ovhClient, domain, subDomain, target) 182 | } 183 | 184 | // CleanUp should delete the relevant TXT record from the DNS provider console. 185 | // If multiple TXT records exist with the same record name (e.g. 186 | // _acme-challenge.example.com) then **only** the record with the same `key` 187 | // value provided on the ChallengeRequest should be cleaned up. 188 | // This is in order to facilitate multiple DNS validations for the same domain 189 | // concurrently. 190 | func (s *ovhDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error { 191 | ovhClient, err := s.ovhClient(ch) 192 | if err != nil { 193 | return err 194 | } 195 | domain := util.UnFqdn(ch.ResolvedZone) 196 | subDomain := getSubDomain(domain, ch.ResolvedFQDN) 197 | target := ch.Key 198 | return removeTXTRecord(ovhClient, domain, subDomain, target) 199 | } 200 | 201 | // Initialize will be called when the webhook first starts. 202 | // This method can be used to instantiate the webhook, i.e. initialising 203 | // connections or warming up caches. 204 | // Typically, the kubeClientConfig parameter is used to build a Kubernetes 205 | // client that can be used to fetch resources from the Kubernetes API, e.g. 206 | // Secret resources containing credentials used to authenticate with DNS 207 | // provider accounts. 208 | // The stopCh can be used to handle early termination of the webhook, in cases 209 | // where a SIGTERM or similar signal is sent to the webhook process. 210 | func (s *ovhDNSProviderSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error { 211 | client, err := kubernetes.NewForConfig(kubeClientConfig) 212 | if err != nil { 213 | return err 214 | } 215 | 216 | s.client = client 217 | return nil 218 | } 219 | 220 | // loadConfig is a small helper function that decodes JSON configuration into 221 | // the typed config struct. 222 | func loadConfig(cfgJSON *extapi.JSON) (ovhDNSProviderConfig, error) { 223 | cfg := ovhDNSProviderConfig{} 224 | // handle the 'base case' where no configuration has been provided 225 | if cfgJSON == nil { 226 | return cfg, nil 227 | } 228 | if err := json.Unmarshal(cfgJSON.Raw, &cfg); err != nil { 229 | return cfg, fmt.Errorf("error decoding OVH config: %v", err) 230 | } 231 | 232 | return cfg, nil 233 | } 234 | 235 | func getSubDomain(domain, fqdn string) string { 236 | if idx := strings.Index(fqdn, "."+domain); idx != -1 { 237 | return fqdn[:idx] 238 | } 239 | 240 | return util.UnFqdn(fqdn) 241 | } 242 | 243 | func addTXTRecord(ovhClient *ovh.Client, domain, subDomain, target string) error { 244 | err := validateZone(ovhClient, domain) 245 | if err != nil { 246 | return err 247 | } 248 | 249 | _, err = createRecord(ovhClient, domain, "TXT", subDomain, target) 250 | if err != nil { 251 | return err 252 | } 253 | return refreshRecords(ovhClient, domain) 254 | } 255 | 256 | func removeTXTRecord(ovhClient *ovh.Client, domain, subDomain, target string) error { 257 | ids, err := listRecords(ovhClient, domain, "TXT", subDomain) 258 | if err != nil { 259 | return err 260 | } 261 | 262 | for _, id := range ids { 263 | record, err := getRecord(ovhClient, domain, id) 264 | if err != nil { 265 | return err 266 | } 267 | if record.Target != target { 268 | continue 269 | } 270 | err = deleteRecord(ovhClient, domain, id) 271 | if err != nil { 272 | return err 273 | } 274 | } 275 | 276 | return refreshRecords(ovhClient, domain) 277 | } 278 | 279 | func validateZone(ovhClient *ovh.Client, domain string) error { 280 | url := "/domain/zone/" + domain + "/status" 281 | zoneStatus := ovhZoneStatus{} 282 | err := ovhClient.Get(url, &zoneStatus) 283 | if err != nil { 284 | return fmt.Errorf("OVH API call failed: GET %s - %v", url, err) 285 | } 286 | if !zoneStatus.IsDeployed { 287 | return fmt.Errorf("OVH zone not deployed for domain %s", domain) 288 | } 289 | 290 | return nil 291 | } 292 | 293 | func listRecords(ovhClient *ovh.Client, domain, fieldType, subDomain string) ([]int64, error) { 294 | url := "/domain/zone/" + domain + "/record?fieldType=" + fieldType + "&subDomain=" + subDomain 295 | ids := []int64{} 296 | err := ovhClient.Get(url, &ids) 297 | if err != nil { 298 | return nil, fmt.Errorf("OVH API call failed: GET %s - %v", url, err) 299 | } 300 | return ids, nil 301 | } 302 | 303 | func getRecord(ovhClient *ovh.Client, domain string, id int64) (*ovhZoneRecord, error) { 304 | url := "/domain/zone/" + domain + "/record/" + strconv.FormatInt(id, 10) 305 | record := ovhZoneRecord{} 306 | err := ovhClient.Get(url, &record) 307 | if err != nil { 308 | return nil, fmt.Errorf("OVH API call failed: GET %s - %v", url, err) 309 | } 310 | return &record, nil 311 | } 312 | 313 | func deleteRecord(ovhClient *ovh.Client, domain string, id int64) error { 314 | url := "/domain/zone/" + domain + "/record/" + strconv.FormatInt(id, 10) 315 | err := ovhClient.Delete(url, nil) 316 | if err != nil { 317 | return fmt.Errorf("OVH API call failed: DELETE %s - %v", url, err) 318 | } 319 | return nil 320 | } 321 | 322 | func createRecord(ovhClient *ovh.Client, domain, fieldType, subDomain, target string) (*ovhZoneRecord, error) { 323 | url := "/domain/zone/" + domain + "/record" 324 | params := ovhZoneRecord{ 325 | FieldType: fieldType, 326 | SubDomain: subDomain, 327 | Target: target, 328 | TTL: 60, 329 | } 330 | record := ovhZoneRecord{} 331 | err := ovhClient.Post(url, ¶ms, &record) 332 | if err != nil { 333 | return nil, fmt.Errorf("OVH API call failed: POST %s - %v", url, err) 334 | } 335 | 336 | return &record, nil 337 | } 338 | 339 | func refreshRecords(ovhClient *ovh.Client, domain string) error { 340 | url := "/domain/zone/" + domain + "/refresh" 341 | err := ovhClient.Post(url, nil, nil) 342 | if err != nil { 343 | return fmt.Errorf("OVH API call failed: POST %s - %v", url, err) 344 | } 345 | 346 | return nil 347 | } 348 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | zone = os.Getenv("TEST_ZONE_NAME") 10 | ) 11 | 12 | func TestRunsSuite(t *testing.T) { 13 | // The manifest path should contain a file named config.json that is a 14 | // snippet of valid configuration that should be included on the 15 | // ChallengeRequest passed as part of the test cases. 16 | 17 | // fixture := dns.NewFixture(&ovhDNSProviderSolver{}, 18 | // dns.SetResolvedZone(zone), 19 | // dns.SetAllowAmbientCredentials(false), 20 | // dns.SetManifestPath("testdata/ovh"), 21 | // ) 22 | 23 | // fixture.RunConformance(t) 24 | } 25 | -------------------------------------------------------------------------------- /scripts/fetch-test-binaries.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | k8s_version=1.22.0 6 | arch=amd64 7 | 8 | if [[ "$OSTYPE" == "linux-gnu" ]]; then 9 | os="linux" 10 | elif [[ "$OSTYPE" == "darwin"* ]]; then 11 | os="darwin" 12 | else 13 | echo "OS '$OSTYPE' not supported." >&2 14 | exit 1 15 | fi 16 | 17 | root=$(cd "`dirname $0`"/..; pwd) 18 | output_dir="$root"/_out 19 | archive_name="kubebuilder-tools-$k8s_version-$os-$arch.tar.gz" 20 | archive_file="$output_dir/$archive_name" 21 | archive_url="https://storage.googleapis.com/kubebuilder-tools/$archive_name" 22 | 23 | mkdir -p "$output_dir" 24 | curl -sL "$archive_url" -o "$archive_file" 25 | tar -zxf "$archive_file" -C "$output_dir/" 26 | -------------------------------------------------------------------------------- /testdata/ovh/README.md: -------------------------------------------------------------------------------- 1 | # Solver testdata directory 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /testdata/ovh/config.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "endpoint": "ovh-eu", 3 | "applicationKey": "", 4 | "applicationSecretRef": { 5 | "Name": "ovh-credentials", 6 | "Key": "applicationSecret" 7 | }, 8 | "consumerKey": "" 9 | } 10 | -------------------------------------------------------------------------------- /testdata/ovh/ovh-credentials.yaml.sample: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: ovh-credentials 5 | type: Opaque 6 | data: 7 | applicationSecret: 8 | --------------------------------------------------------------------------------