Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f143a34954 | |||
| 4d78692298 | |||
| dfa2e7f205 | |||
| 89deb39e81 | |||
| e2d1e1c355 | |||
| 95a1557538 | |||
| b62f1cf632 | |||
| cfc6360ffc | |||
| 9b1fde11a2 | |||
| 8c1403a87b | |||
| 9d4d1a1d03 | |||
| cdaa33a539 | |||
| aaa26cf714 | |||
| e64faa0ab1 | |||
| 2612fb7733 | |||
| ffa1368f78 | |||
| a9c3ae7661 | |||
| 373cb7ffde | |||
| 625cd71ef7 | |||
| 2664ae3f11 | |||
| 4c0ef13c61 | |||
| ac9a9fd796 | |||
| 7bd3f5769a | |||
| 84ac6d4d27 | |||
| 52583c3e12 | |||
| 382418418b | |||
| 661abf8df2 | |||
| 4c7c0b121a | |||
| ef409187de | |||
|
|
33b74ccc20 | ||
|
|
b818887966 | ||
|
|
970e5fb44e | ||
| 5785110ea0 | |||
| aaa5f621e4 | |||
|
|
41edd7368c | ||
|
|
61bcd3d286 | ||
|
|
f63550ff6f | ||
|
|
d0315c169a | ||
|
|
f147f05230 | ||
|
|
e544607e94 | ||
|
|
a54c83f476 | ||
|
|
b1bb8bf811 | ||
|
|
b11945d5af | ||
|
|
42e43fff16 | ||
|
|
f60877ce45 | ||
|
|
b34bcde92e | ||
|
|
542afe1151 | ||
|
|
4f5ac39ef6 | ||
|
|
1dde54459e | ||
|
|
df59d33f56 | ||
|
|
b62d5f15fc | ||
|
|
fd228063ff | ||
|
|
ac256fabbb | ||
|
|
60d73a9a32 | ||
|
|
1145d1f447 | ||
|
|
e245e18ac2 | ||
|
|
9ce331de4c | ||
|
|
356295e4ed | ||
|
|
4a3f2ce2f4 | ||
|
|
511d439c06 | ||
|
|
85fc3c33a4 | ||
|
|
4d6401bc48 | ||
|
|
bf4cb02129 | ||
|
|
a8447a628b | ||
|
|
e1e7f4602c | ||
|
|
ba9afbdad6 | ||
|
|
a53acb412e | ||
|
|
d99deb8641 |
@@ -1,10 +1,13 @@
|
|||||||
.git
|
.git
|
||||||
.gitignore
|
.gitignore
|
||||||
.idea
|
.idea
|
||||||
LICENSE
|
.dockerignore
|
||||||
|
.github
|
||||||
Makefile
|
Makefile
|
||||||
README.*
|
README.*
|
||||||
__main__
|
LICENSE
|
||||||
|
CHANGELOG.*
|
||||||
|
_test
|
||||||
_out
|
_out
|
||||||
apiserver.local.config
|
apiserver.local.config
|
||||||
cert-manager-webhook-gandi
|
cert-manager-webhook-gandi
|
||||||
@@ -12,3 +15,7 @@ deploy
|
|||||||
main_test.go
|
main_test.go
|
||||||
scripts
|
scripts
|
||||||
testdata
|
testdata
|
||||||
|
examples
|
||||||
|
*.yaml
|
||||||
|
Dockerfile
|
||||||
|
charts
|
||||||
16
.github/workflows/main.yml
vendored
16
.github/workflows/main.yml
vendored
@@ -1,16 +0,0 @@
|
|||||||
name: Container-image
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build docker image
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: Build and Publish to Registry
|
|
||||||
uses: elgohr/Publish-Docker-Github-Action@master
|
|
||||||
with:
|
|
||||||
name: bwolf/cert-manager-webhook-gandi
|
|
||||||
username: '${{ secrets.DOCKER_USERNAME }}'
|
|
||||||
password: '${{ secrets.DOCKER_PASSWORD }}'
|
|
||||||
buildoptions: "--compress --force-rm"
|
|
||||||
tag_names: true
|
|
||||||
93
.github/workflows/release.yaml
vendored
Normal file
93
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
name: Release workflow
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v[0-9]+.[0-9]+.[0-9]+
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check version
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 2
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Check version
|
||||||
|
run: |
|
||||||
|
version="$(awk '/^version: /{ print $2 }' deploy/cert-manager-webhook-gandi/Chart.yaml)"
|
||||||
|
if [ "$version" != "$GITHUB_REF_NAME" ]; then
|
||||||
|
echo "Chart.yaml is not up to date"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
chart:
|
||||||
|
name: Publish Helm chart
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 10
|
||||||
|
needs: check
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install docker
|
||||||
|
uses: actions/docker-install@v1
|
||||||
|
|
||||||
|
- name: Install helm
|
||||||
|
uses: actions/helm-install@v1
|
||||||
|
|
||||||
|
- name: Login to registry
|
||||||
|
uses: actions/docker-login@v2
|
||||||
|
with:
|
||||||
|
registry: git.mug.lv
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build & publish Helm chart
|
||||||
|
run: |
|
||||||
|
helm package ./deploy/cert-manager-webhook-gandi
|
||||||
|
helm push \
|
||||||
|
cert-manager-webhook-gandi-*.tgz \
|
||||||
|
oci://git.mug.lv/galen
|
||||||
|
|
||||||
|
image:
|
||||||
|
name: Publish Docker image
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 10
|
||||||
|
needs: check
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Get versions
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
chart="$(awk '/^version: /{ print $2 }' deploy/cert-manager-webhook-gandi/Chart.yaml)"
|
||||||
|
chart="${chart//v/}"
|
||||||
|
go="$(awk '/^go /{ print $2 }' go.mod)"
|
||||||
|
cat <<EOF >>"$GITHUB_OUTPUT"
|
||||||
|
chart=$chart
|
||||||
|
go=$go
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Install docker
|
||||||
|
uses: actions/docker-install@v1
|
||||||
|
|
||||||
|
- name: Login to registry
|
||||||
|
uses: actions/docker-login@v2
|
||||||
|
with:
|
||||||
|
registry: git.mug.lv
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Setup buildx
|
||||||
|
uses: actions/docker-setup-buildx@v2
|
||||||
|
|
||||||
|
- name: Build image
|
||||||
|
uses: actions/docker-build-push@v4
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: git.mug.lv/${{ github.repository }}-server:${{ steps.version.outputs.chart }}
|
||||||
|
build-args: GO_VERSION=${{ steps.version.outputs.go }}
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
# Test binary, build with `go test -c`
|
# Test binary, build with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
/__main__/
|
/_test/
|
||||||
/apiserver.local.config/
|
/apiserver.local.config/
|
||||||
/_out/
|
/_out/
|
||||||
|
|
||||||
@@ -16,3 +16,11 @@
|
|||||||
|
|
||||||
# Ignore the built binary
|
# Ignore the built binary
|
||||||
cert-manager-webhook-gandi
|
cert-manager-webhook-gandi
|
||||||
|
# But not the Helm chart
|
||||||
|
!deploy/cert-manager-webhook-gandi
|
||||||
|
|
||||||
|
# JetBrains
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Nix
|
||||||
|
.envrc
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
# ChangeLog
|
|
||||||
|
|
||||||
- 2020-02-26 - v0.1.1 - switch to Docker Hub
|
|
||||||
- 2020-02-26 - v0.1.0 - initial release
|
|
||||||
36
Dockerfile
36
Dockerfile
@@ -1,25 +1,23 @@
|
|||||||
FROM golang:1.13-alpine AS build_deps
|
# syntax=docker/dockerfile:1.3
|
||||||
|
ARG GO_VERSION
|
||||||
|
FROM --platform=${TARGETPLATFORM} golang:${GO_VERSION}-alpine AS base
|
||||||
|
|
||||||
RUN apk add --no-cache git bzr
|
WORKDIR /go/src/cert-manager-webhook-gandi
|
||||||
|
COPY go.* .
|
||||||
|
|
||||||
WORKDIR /workspace
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
ENV GO111MODULE=on
|
apk add --no-cache git ca-certificates && \
|
||||||
|
go mod download
|
||||||
|
|
||||||
COPY go.mod .
|
FROM base AS build
|
||||||
COPY go.sum .
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
|
||||||
RUN go mod download
|
RUN --mount=readonly,target=. --mount=type=cache,target=/go/pkg/mod \
|
||||||
|
GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -a -o /go/bin/webhook -ldflags '-w -extldflags "-static"' .
|
||||||
|
|
||||||
FROM build_deps AS build
|
FROM scratch AS image
|
||||||
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
|
COPY --from=build /go/bin/webhook /usr/local/bin/webhook
|
||||||
|
|
||||||
COPY . .
|
ENTRYPOINT ["/usr/local/bin/webhook"]
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 go build -o webhook -ldflags '-w -extldflags "-static"' .
|
|
||||||
|
|
||||||
FROM alpine:3.9
|
|
||||||
|
|
||||||
RUN apk add --no-cache ca-certificates
|
|
||||||
|
|
||||||
COPY --from=build /workspace/webhook /usr/local/bin/webhook
|
|
||||||
|
|
||||||
ENTRYPOINT ["webhook"]
|
|
||||||
|
|||||||
42
Makefile
42
Makefile
@@ -1,20 +1,42 @@
|
|||||||
IMAGE_NAME := "cert-manager-webhook-gandi"
|
OS ?= $(shell go env GOOS)
|
||||||
IMAGE_TAG := "latest"
|
ARCH ?= $(shell go env GOARCH)
|
||||||
|
|
||||||
|
ifeq (Darwin, $(shell uname))
|
||||||
|
GREP_PREGEX_FLAG := E
|
||||||
|
else
|
||||||
|
GREP_PREGEX_FLAG := P
|
||||||
|
endif
|
||||||
|
|
||||||
|
GO_VERSION ?= $(shell go mod edit -json | grep -${GREP_PREGEX_FLAG}o '"Go":\s+"([0-9.]+)"' | sed -E 's/.+"([0-9.]+)"/\1/')
|
||||||
|
|
||||||
OUT := $(shell pwd)/_out
|
OUT := $(shell pwd)/_out
|
||||||
|
|
||||||
$(shell mkdir -p "$(OUT)")
|
KUBEBUILDER_VERSION=2.3.2
|
||||||
|
|
||||||
verify:
|
$(shell mkdir -p "${OUT}")
|
||||||
|
|
||||||
|
test: _test/kubebuilder
|
||||||
|
TEST_ASSET_ETCD=_test/kubebuilder/bin/etcd \
|
||||||
|
TEST_ASSET_KUBE_APISERVER=_test/kubebuilder/bin/kube-apiserver \
|
||||||
|
TEST_ASSET_KUBECTL=_test/kubebuilder/bin/kubectl \
|
||||||
go test -v .
|
go test -v .
|
||||||
|
|
||||||
build:
|
_test/kubebuilder:
|
||||||
docker build --rm -t "$(IMAGE_NAME):$(IMAGE_TAG)" .
|
curl -fsSL https://github.com/kubernetes-sigs/kubebuilder/releases/download/v${KUBEBUILDER_VERSION}/kubebuilder_${KUBEBUILDER_VERSION}_${OS}_${ARCH}.tar.gz -o kubebuilder-tools.tar.gz
|
||||||
|
mkdir -p _test/kubebuilder
|
||||||
|
tar -xvf kubebuilder-tools.tar.gz
|
||||||
|
mv kubebuilder_${KUBEBUILDER_VERSION}_${OS}_${ARCH}/bin _test/kubebuilder/
|
||||||
|
rm kubebuilder-tools.tar.gz
|
||||||
|
rm -R kubebuilder_${KUBEBUILDER_VERSION}_${OS}_${ARCH}
|
||||||
|
|
||||||
|
clean: clean-kubebuilder
|
||||||
|
|
||||||
|
clean-kubebuilder:
|
||||||
|
rm -Rf _test/kubebuilder
|
||||||
|
|
||||||
.PHONY: rendered-manifest.yaml
|
.PHONY: rendered-manifest.yaml
|
||||||
rendered-manifest.yaml:
|
rendered-manifest.yaml:
|
||||||
# --name cert-manager-webhook-gandi $BACKSLASH
|
|
||||||
helm template \
|
helm template \
|
||||||
--set image.repository=$(IMAGE_NAME) \
|
--set image.repository=${IMAGE_NAME} \
|
||||||
--set image.tag=$(IMAGE_TAG) \
|
--set image.tag=${IMAGE_TAG} \
|
||||||
deploy/cert-manager-webhook-gandi > "$(OUT)/rendered-manifest.yaml"
|
deploy/cert-manager-webhook-gandi > "${OUT}/rendered-manifest.yaml"
|
||||||
|
|||||||
198
README.md
198
README.md
@@ -1,4 +1,9 @@
|
|||||||
# ACME webhook for Gandi (cert-manager-webhook-gandi)
|
# ACME webhook for Gandi (cert-manager-webhook-gandi)
|
||||||
|
|
||||||
|
Forked from [bwolf/cert-manager-webhook-gandi](https://github.com/bwolf/cert-manager-webhook-gandi), with additional patches applied from pull requests.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
`cert-manager-webhook-gandi` is an ACME webhook for [cert-manager]. It provides an ACME (read: Let's Encrypt) webhook for [cert-manager], which allows to use a `DNS-01` challenge with [Gandi]. This allows to provide Let's Encrypt certificates to [Kubernetes] for service protocols other than HTTP and furthermore to request wildcard certificates. Internally it uses the [Gandi LiveDNS API] to communicate with Gandi.
|
`cert-manager-webhook-gandi` is an ACME webhook for [cert-manager]. It provides an ACME (read: Let's Encrypt) webhook for [cert-manager], which allows to use a `DNS-01` challenge with [Gandi]. This allows to provide Let's Encrypt certificates to [Kubernetes] for service protocols other than HTTP and furthermore to request wildcard certificates. Internally it uses the [Gandi LiveDNS API] to communicate with Gandi.
|
||||||
|
|
||||||
Quoting the [ACME DNS-01 challenge]:
|
Quoting the [ACME DNS-01 challenge]:
|
||||||
@@ -13,17 +18,17 @@ Build the container image `cert-manager-webhook-gandi:latest`:
|
|||||||
|
|
||||||
|
|
||||||
## Image
|
## Image
|
||||||
Ready made images are hosted on Docker Hub ([browse](https://hub.docker.com/r/bwolf/cert-manager-webhook-gandi)). Use at your own risk:
|
Ready made images are hosted on Docker Hub ([image tags]). Use at your own risk:
|
||||||
|
|
||||||
bwolf/cert-manager-webhook-gandi
|
bwolf/cert-manager-webhook-gandi
|
||||||
|
|
||||||
|
|
||||||
### Release History
|
### Release History
|
||||||
Refer to the [ChangeLog](ChangeLog.md) file.
|
Refer to the [CHANGELOG](CHANGELOG.md) file.
|
||||||
|
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
This webhook has been tested with [cert-manager] v0.13.1 and Kubernetes v0.17.x on `amd64`. In theory it should work on other hardware platforms as well but no steps have been taken to verify this. Please drop me a note if you had success.
|
This webhook has been tested with [cert-manager] v1.5.4 and Kubernetes v1.22.2 on `amd64`. In theory it should work on other hardware platforms as well but no steps have been taken to verify this. Please drop me a note if you had success.
|
||||||
|
|
||||||
|
|
||||||
## Testing with Minikube
|
## Testing with Minikube
|
||||||
@@ -36,171 +41,170 @@ This webhook has been tested with [cert-manager] v0.13.1 and Kubernetes v0.17.x
|
|||||||
|
|
||||||
2. Install [cert-manager] with [Helm]:
|
2. Install [cert-manager] with [Helm]:
|
||||||
|
|
||||||
kubectl create namespace cert-manager
|
|
||||||
kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/v0.13.1/deploy/manifests/00-crds.yaml
|
|
||||||
|
|
||||||
helm repo add jetstack https://charts.jetstack.io
|
helm repo add jetstack https://charts.jetstack.io
|
||||||
helm install cert-manager --namespace cert-manager \
|
|
||||||
--set 'extraArgs={--dns01-recursive-nameservers=8.8.8.8:53\,1.1.1.1:53}' \
|
helm install cert-manager jetstack/cert-manager \
|
||||||
jetstack/cert-manager
|
--namespace cert-manager \
|
||||||
|
--create-namespace \
|
||||||
|
--set installCRDs=true \
|
||||||
|
--version v1.5.4 \
|
||||||
|
--set 'extraArgs={--dns01-recursive-nameservers=8.8.8.8:53\,1.1.1.1:53}'
|
||||||
|
|
||||||
kubectl get pods --namespace cert-manager --watch
|
kubectl get pods --namespace cert-manager --watch
|
||||||
|
|
||||||
**Note**: refer to Name servers in the official [documentation](https://cert-manager.io/docs/configuration/acme/dns01/#setting-nameservers-for-dns01-self-check) according the `extraArgs`.
|
**Note**: refer to Name servers in the official [documentation][setting-nameservers-for-dns01-self-check] according the `extraArgs`.
|
||||||
|
|
||||||
**Note**: ensure that the custom CRDS of cert-manager match the major version of the cert-manager release by comparing the URL of the CRDS with the helm info of the charts app version:
|
**Note**: ensure that the custom CRDS of cert-manager match the major version of the cert-manager release by comparing the URL of the CRDS with the helm info of the charts app version:
|
||||||
|
|
||||||
helm search repo jetstack
|
helm search repo jetstack
|
||||||
|
|
||||||
Example output:
|
Example output:
|
||||||
|
|
||||||
NAME CHART VERSION APP VERSION DESCRIPTION
|
NAME CHART VERSION APP VERSION DESCRIPTION
|
||||||
jetstack/cert-manager v0.13.1 v0.13.1 A Helm chart for cert-manager
|
jetstack/cert-manager v1.5.4 v1.5.4 A Helm chart for cert-manager
|
||||||
|
|
||||||
Check the state and ensure that all pods are running fine (watch out for any issues regarding the `cert-manager-webhook-` pod and its volume mounts):
|
Check the state and ensure that all pods are running fine (watch out for any issues regarding the `cert-manager-webhook-` pod and its volume mounts):
|
||||||
|
|
||||||
kubectl describe pods -n cert-manager | less
|
kubectl describe pods -n cert-manager | less
|
||||||
|
|
||||||
|
|
||||||
3. Create the secret to keep the Gandi API key in the default namespace, where later on the Issuer and the Certificate are created:
|
3. Create the secret to keep the Gandi API key in the cert-manager namespace:
|
||||||
|
|
||||||
kubectl create secret generic gandi-credentials \
|
kubectl create secret generic gandi-credentials \
|
||||||
--from-literal=api-token='<GANDI-API-KEY>'
|
--namespace cert-manager --from-literal=api-token='<GANDI-API-KEY>'
|
||||||
|
|
||||||
**Note**: See [RBAC Authorization](https://kubernetes.io/docs/reference/access-authn-authz/rbac/):
|
*The `Secret` must reside in the same namespace as `cert-manager`.*
|
||||||
|
|
||||||
> A Role can only be used to grant access to resources within a single namespace.
|
4. Deploy this webhook (add `--dry-run` to try it and `--debug` to inspect the rendered manifests; Set `logLevel` to 6 for verbose logs):
|
||||||
|
|
||||||
*As far as I understand cert-manager, the `Secret` must reside in the same namespace as the `Issuer` and `Certificate` resource.*
|
*The `features.apiPriorityAndFairness` argument must be removed or set to `false` for Kubernetes older than 1.20.*
|
||||||
|
|
||||||
4. Grant permission for the service-account to access the secret holding the Gandi API key:
|
|
||||||
|
|
||||||
kubectl apply -f rbac.yaml
|
|
||||||
|
|
||||||
5. Deploy this webhook (add `--dry-run` to try it and `--debug` to inspect the rendered manifests; Set `logLevel` to 6 for verbose logs):
|
|
||||||
|
|
||||||
helm install cert-manager-webhook-gandi \
|
helm install cert-manager-webhook-gandi \
|
||||||
--namespace cert-manager \
|
--namespace cert-manager \
|
||||||
|
--set features.apiPriorityAndFairness=true \
|
||||||
--set image.repository=cert-manager-webhook-gandi \
|
--set image.repository=cert-manager-webhook-gandi \
|
||||||
|
--set image.tag=latest \
|
||||||
--set logLevel=2 \
|
--set logLevel=2 \
|
||||||
./deploy/cert-manager-webhook-gandi
|
./deploy/cert-manager-webhook-gandi
|
||||||
|
|
||||||
Check the logs
|
To deploy using the image from Docker Hub (for example using the `0.2.0` tag):
|
||||||
|
|
||||||
|
helm install cert-manager-webhook-gandi \
|
||||||
|
--namespace cert-manager \
|
||||||
|
--set features.apiPriorityAndFairness=true \
|
||||||
|
--set image.tag=0.2.0 \
|
||||||
|
--set logLevel=2 \
|
||||||
|
./deploy/cert-manager-webhook-gandi
|
||||||
|
|
||||||
|
To deploy using the Helm repository (for example using the `v0.2.0` version):
|
||||||
|
|
||||||
|
helm install cert-manager-webhook-gandi \
|
||||||
|
--repo https://bwolf.github.io/cert-manager-webhook-gandi \
|
||||||
|
--version v0.2.0 \
|
||||||
|
--namespace cert-manager \
|
||||||
|
--set features.apiPriorityAndFairness=true \
|
||||||
|
--set logLevel=2
|
||||||
|
|
||||||
|
Check the logs
|
||||||
|
|
||||||
kubectl get pods -n cert-manager --watch
|
kubectl get pods -n cert-manager --watch
|
||||||
kubectl logs -n cert-manager cert-manager-webhook-gandi-XYZ
|
kubectl logs -n cert-manager cert-manager-webhook-gandi-XYZ
|
||||||
|
|
||||||
6. Create a staging issuer (email addresses with the suffix `example.com` are forbidden):
|
6. Create a staging issuer (email addresses with the suffix `example.com` are forbidden).
|
||||||
|
|
||||||
cat << EOF | sed "s/invalid@example.com/$email/" | kubectl apply -f -
|
See [letsencrypt-staging-issuer.yaml](examples/issuers/letsencrypt-staging-issuer.yaml)
|
||||||
apiVersion: cert-manager.io/v1alpha2
|
|
||||||
kind: Issuer
|
|
||||||
metadata:
|
|
||||||
name: letsencrypt-staging
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
acme:
|
|
||||||
# The ACME server URL
|
|
||||||
server: https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
# Email address used for ACME registration
|
|
||||||
email: invalid@example.com
|
|
||||||
# Name of a secret used to store the ACME account private key
|
|
||||||
privateKeySecretRef:
|
|
||||||
name: letsencrypt-staging
|
|
||||||
solvers:
|
|
||||||
- dns01:
|
|
||||||
webhook:
|
|
||||||
groupName: acme.bwolf.me
|
|
||||||
solverName: gandi
|
|
||||||
config:
|
|
||||||
apiKeySecretRef:
|
|
||||||
key: api-token
|
|
||||||
name: gandi-credentials
|
|
||||||
EOF
|
|
||||||
|
|
||||||
Check status of the Issuer:
|
Don't forget to replace email `invalid@example.com`.
|
||||||
|
|
||||||
|
Check status of the Issuer:
|
||||||
|
|
||||||
kubectl describe issuer letsencrypt-staging
|
kubectl describe issuer letsencrypt-staging
|
||||||
|
|
||||||
*Note*: The production Issuer is [similar](https://cert-manager.io/docs/configuration/acme/).
|
You can deploy a ClusterIssuer instead : see [letsencrypt-staging-clusterissuer.yaml](examples/issuers/letsencrypt-staging-clusterissuer.yaml)
|
||||||
|
|
||||||
7. Issue a Certificate for your `$DOMAIN` ([documentation](https://cert-manager.io/docs/usage/certificate/)):
|
*Note*: The production Issuer is [similar][ACME documentation].
|
||||||
|
|
||||||
cat << EOF | sed "s/example-com/$DOMAIN/" | kubectl apply -f -
|
7. Issue a [Certificate] for your domain: see [certif-example-com.yaml](examples/certificates/certif-example-com.yaml)
|
||||||
apiVersion: cert-manager.io/v1alpha2
|
|
||||||
kind: Certificate
|
|
||||||
metadata:
|
|
||||||
name: example-com
|
|
||||||
spec:
|
|
||||||
dnsNames:
|
|
||||||
- example-com
|
|
||||||
issuerRef:
|
|
||||||
name: letsencrypt-staging
|
|
||||||
secretName: example-com-tls
|
|
||||||
EOF
|
|
||||||
|
|
||||||
Check the status of the Certificate:
|
Replace `your-domain` and `your.domain` in the [certif-example-com.yaml](examples/certificates/certif-example-com.yaml)
|
||||||
|
|
||||||
kubectl describe certificate $DOMAIN
|
Create the Certificate:
|
||||||
|
|
||||||
8. Issue a wildcard Certificate for your `$DOMAIN`:
|
kubectl apply -f ./examples/certificates/certif-example-com.yaml
|
||||||
|
|
||||||
cat << EOF | sed "s/example-com/$DOMAIN/" | kubectl apply -f -
|
Check the status of the Certificate:
|
||||||
apiVersion: cert-manager.io/v1alpha2
|
|
||||||
kind: Certificate
|
|
||||||
metadata:
|
|
||||||
name: wildcard-example-com
|
|
||||||
spec:
|
|
||||||
dnsNames:
|
|
||||||
- '*.example-com'
|
|
||||||
issuerRef:
|
|
||||||
name: letsencrypt-staging
|
|
||||||
secretName: wildcard-example-com-tls
|
|
||||||
EOF
|
|
||||||
|
|
||||||
Check the status of the Certificate:
|
kubectl describe certificate example-com
|
||||||
|
|
||||||
kubectl describe certificate $DOMAIN
|
Display the details like the common name and subject alternative names:
|
||||||
|
|
||||||
99. Uninstall this webhook:
|
kubectl get secret example-com-tls -o yaml
|
||||||
|
|
||||||
|
If you deployed a ClusterIssuer : use [certif-example-com-clusterissuer.yaml](examples/certificates/certif-example-com-clusterissuer.yaml)
|
||||||
|
|
||||||
|
8. Issue a wildcard Certificate for your domain: see [certif-wildcard-example-com.yaml](examples/certificates/certif-wildcard-example-com.yaml)
|
||||||
|
|
||||||
|
Replace `your-domain` and `your.domain` in the [certif-wildcard-example-com.yaml](examples/certificates/certif-wildcard-example-com.yaml)
|
||||||
|
|
||||||
|
Create the Certificate:
|
||||||
|
|
||||||
|
kubectl apply -f ./examples/certificates/certif-wildcard-example-com.yaml
|
||||||
|
|
||||||
|
Check the status of the Certificate:
|
||||||
|
|
||||||
|
kubectl describe certificate wildcard-example-com
|
||||||
|
|
||||||
|
Display the details like the common name and subject alternative names:
|
||||||
|
|
||||||
|
kubectl get secret wildcard-example-com-tls -o yaml
|
||||||
|
|
||||||
|
If you deployed a ClusterIssuer : use [certif-wildcard-example-com-clusterissuer.yaml](examples/certificates/certif-wildcard-example-com-clusterissuer.yaml)
|
||||||
|
|
||||||
|
9. Uninstall this webhook:
|
||||||
|
|
||||||
helm uninstall cert-manager-webhook-gandi --namespace cert-manager
|
helm uninstall cert-manager-webhook-gandi --namespace cert-manager
|
||||||
kubectl delete -f rbac.yaml
|
kubectl delete gandi-credentials --namespace cert-manager
|
||||||
kubectl delete gandi-credentials
|
|
||||||
|
|
||||||
100. Uninstalling cert-manager:
|
10. Uninstalling cert-manager:
|
||||||
This is out of scope here. Refer to the official [documentation](https://cert-manager.io/docs/installation/uninstall/kubernetes/).
|
This is out of scope here. Refer to the official [documentation][cert-manager-uninstall].
|
||||||
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
**Note**: If some tool (IDE or build process) fails resolving a dependency, it may be the cause that a indirect dependency uses `bzr` for versioning. In such a case it may help to put the `bzr` binary into `$PATH` or `$GOPATH/bin`.
|
**Note**: If some tool (IDE or build process) fails resolving a dependency, it may be the cause that a indirect dependency uses `bzr` for versioning. In such a case it may help to put the `bzr` binary into `$PATH` or `$GOPATH/bin`.
|
||||||
|
|
||||||
|
|
||||||
## Release process
|
## Release process (automated with [GitHub actions](.github/workflows/main.yml))
|
||||||
- Code changes result in a new image version and Git tag
|
- Changes in the Go code result in the build of a Docker image and the release of a new Helm chart
|
||||||
- Helm chart changes result in a new chart version
|
- Changes at Helm chart level only, result in the release of a new Chart without building a new Docker image
|
||||||
- All other changes are pushed to master
|
- All other changes are pushed to master
|
||||||
|
- All versions are to be documented in [CHANGELOG](CHANGELOG.md)
|
||||||
|
|
||||||
|
**Note**: All changes to the Go code or Helm chart must go with a version tag `vX.X.X` to trigger the GitHub workflow
|
||||||
|
|
||||||
|
**Note**: Any Helm chart release results in the creation of a [GitHub release](https://github.com/bwolf/cert-manager-webhook-gandi/releases)
|
||||||
|
|
||||||
## Conformance test
|
## Conformance test
|
||||||
Please note that the test is not a typical unit or integration test. Instead it invokes the web hook in a Kubernetes-like environment which asks the web hook to really call the DNS provider (.i.e. Gandi). It attempts to create an `TXT` entry like `cert-manager-dns01-tests.example.com`, verifies the presence of the entry via Google DNS. Finally it removes the entry by calling the cleanup method of web hook.
|
Please note that the test is not a typical unit or integration test. Instead it invokes the web hook in a Kubernetes-like environment which asks the web hook to really call the DNS provider (.i.e. Gandi). It attempts to create an `TXT` entry like `cert-manager-dns01-tests.example.com`, verifies the presence of the entry via Google DNS. Finally it removes the entry by calling the cleanup method of web hook.
|
||||||
|
|
||||||
**Note**: Replace the string `darwin` in the URL below with an OS matching your system (e.g. `linux`).
|
|
||||||
|
|
||||||
As said above, the conformance test is run against the real Gandi API. Therefore you *must* have a Gandi account, a domain and an API key.
|
As said above, the conformance test is run against the real Gandi API. Therefore you *must* have a Gandi account, a domain and an API key.
|
||||||
|
|
||||||
``` shell
|
``` shell
|
||||||
cp testdata/gandi/api-key.yaml.sample testdata/gandi/api-key.yaml
|
cp testdata/gandi/api-key.yaml.sample testdata/gandi/api-key.yaml
|
||||||
echo -n $YOUR_GANDI_API_KEY | base64 | pbcopy # or xclip
|
echo -n $YOUR_GANDI_API_KEY | base64 | pbcopy # or xclip
|
||||||
$EDITOR testdata/gandi/api-key.yaml
|
$EDITOR testdata/gandi/api-key.yaml
|
||||||
./scripts/fetch-test-binaries.sh
|
TEST_ZONE_NAME=example.com. make test
|
||||||
TEST_ZONE_NAME=example.com. go test -v .
|
make clean
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
[ACME DNS-01 challenge]: https://letsencrypt.org/docs/challenge-types/#dns-01-challenge
|
[ACME DNS-01 challenge]: https://letsencrypt.org/docs/challenge-types/#dns-01-challenge
|
||||||
|
[ACME documentation]: https://cert-manager.io/docs/configuration/acme/
|
||||||
|
[Certificate]: https://cert-manager.io/docs/usage/certificate/
|
||||||
[cert-manager]: https://cert-manager.io/
|
[cert-manager]: https://cert-manager.io/
|
||||||
[Gandi]: https://gandi.net/
|
[Gandi]: https://gandi.net/
|
||||||
[Gandi LiveDNS API]: https://doc.livedns.gandi.net
|
[Gandi LiveDNS API]: https://api.gandi.net/docs/livedns/
|
||||||
[Helm]: https://helm.sh
|
[Helm]: https://helm.sh
|
||||||
|
[image tags]: https://hub.docker.com/r/bwolf/cert-manager-webhook-gandi
|
||||||
[Kubernetes]: https://kubernetes.io/
|
[Kubernetes]: https://kubernetes.io/
|
||||||
|
[setting-nameservers-for-dns01-self-check]: https://cert-manager.io/docs/configuration/acme/dns01/#setting-nameservers-for-dns01-self-check
|
||||||
|
[cert-manager-uninstall]: https://cert-manager.io/docs/installation/uninstall/kubernetes/
|
||||||
|
|||||||
6
TODO
6
TODO
@@ -1,6 +0,0 @@
|
|||||||
- The softlayer repository (git@github.com:cgroschupp/cert-manager-webhook-softlayer.git) contains mock code, this helps testing
|
|
||||||
- Makefile does not include a download target of the test binaries
|
|
||||||
|
|
||||||
;; Local Variables:
|
|
||||||
;; mode: text
|
|
||||||
;; End:
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
description: A Helm chart for cert-manager-webhook-gandi
|
description: A Helm chart for cert-manager-webhook-gandi
|
||||||
name: cert-manager-webhook-gandi
|
name: cert-manager-webhook-gandi
|
||||||
version: 0.1.1
|
version: v0.4.6
|
||||||
|
|||||||
@@ -31,6 +31,26 @@ Create chart name and version as used by the chart label.
|
|||||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Common labels
|
||||||
|
*/}}
|
||||||
|
{{- define "cert-manager-webhook-gandi.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||||
|
{{ include "cert-manager-webhook-gandi.selectorLabels" . }}
|
||||||
|
{{- if .Chart.AppVersion }}
|
||||||
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
|
{{- end }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Selector labels
|
||||||
|
*/}}
|
||||||
|
{{- define "cert-manager-webhook-gandi.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
{{- define "cert-manager-webhook-gandi.selfSignedIssuer" -}}
|
{{- define "cert-manager-webhook-gandi.selfSignedIssuer" -}}
|
||||||
{{ printf "%s-selfsign" (include "cert-manager-webhook-gandi.fullname" .) }}
|
{{ printf "%s-selfsign" (include "cert-manager-webhook-gandi.fullname" .) }}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
apiVersion: apiregistration.k8s.io/v1beta1
|
apiVersion: apiregistration.k8s.io/v1
|
||||||
kind: APIService
|
kind: APIService
|
||||||
metadata:
|
metadata:
|
||||||
name: v1alpha1.{{ .Values.groupName }}
|
name: v1alpha1.{{ .Values.groupName }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "cert-manager-webhook-gandi.servingCertificate" . }}"
|
cert-manager.io/inject-ca-from: "{{ .Values.certManager.namespace }}/{{ include "cert-manager-webhook-gandi.servingCertificate" . }}"
|
||||||
spec:
|
spec:
|
||||||
group: {{ .Values.groupName }}
|
group: {{ .Values.groupName }}
|
||||||
groupPriorityMinimum: 1000
|
groupPriorityMinimum: 1000
|
||||||
versionPriority: 15
|
versionPriority: 15
|
||||||
service:
|
service:
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
version: v1alpha1
|
version: v1alpha1
|
||||||
|
|||||||
@@ -2,29 +2,26 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
spec:
|
spec:
|
||||||
replicas: {{ .Values.replicaCount }}
|
replicas: {{ .Values.replicaCount }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.selectorLabels" . | nindent 6 }}
|
||||||
release: {{ .Release.Name }}
|
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.selectorLabels" . | nindent 8 }}
|
||||||
release: {{ .Release.Name }}
|
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
serviceAccountName: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}
|
- name: {{ .Chart.Name }}
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
image: {{ .Values.image.repository }}:{{ default .Values.image.tag (trimPrefix "v" .Chart.Version) }}
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
args:
|
args:
|
||||||
|
- --secure-port={{ .Values.containerport }}
|
||||||
- --tls-cert-file=/tls/tls.crt
|
- --tls-cert-file=/tls/tls.crt
|
||||||
- --tls-private-key-file=/tls/tls.key
|
- --tls-private-key-file=/tls/tls.key
|
||||||
{{- if .Values.logLevel }}
|
{{- if .Values.logLevel }}
|
||||||
@@ -35,7 +32,7 @@ spec:
|
|||||||
value: {{ .Values.groupName | quote }}
|
value: {{ .Values.groupName | quote }}
|
||||||
ports:
|
ports:
|
||||||
- name: https
|
- name: https
|
||||||
containerPort: 443
|
containerPort: {{ .Values.containerport }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
@@ -52,20 +49,20 @@ spec:
|
|||||||
mountPath: /tls
|
mountPath: /tls
|
||||||
readOnly: true
|
readOnly: true
|
||||||
resources:
|
resources:
|
||||||
{{ toYaml .Values.resources | indent 12 }}
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
volumes:
|
volumes:
|
||||||
- name: certs
|
- name: certs
|
||||||
secret:
|
secret:
|
||||||
secretName: {{ include "cert-manager-webhook-gandi.servingCertificate" . }}
|
secretName: {{ include "cert-manager-webhook-gandi.servingCertificate" . }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{ toYaml . | indent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{ toYaml . | indent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.tolerations }}
|
{{- with .Values.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{ toYaml . | indent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -1,32 +1,24 @@
|
|||||||
---
|
---
|
||||||
# Create a selfsigned Issuer, in order to create a root CA certificate for
|
# Create a selfsigned Issuer, in order to create a root CA certificate for
|
||||||
# signing webhook serving certificates
|
# signing webhook serving certificates
|
||||||
apiVersion: cert-manager.io/v1alpha2
|
apiVersion: cert-manager.io/v1
|
||||||
kind: Issuer
|
kind: Issuer
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.selfSignedIssuer" . }}
|
name: {{ include "cert-manager-webhook-gandi.selfSignedIssuer" . }}
|
||||||
namespace: {{ .Release.Namespace | quote }}
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
spec:
|
spec:
|
||||||
selfSigned: {}
|
selfSigned: {}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Generate a CA Certificate used to sign certificates for the webhook
|
# Generate a CA Certificate used to sign certificates for the webhook
|
||||||
apiVersion: cert-manager.io/v1alpha2
|
apiVersion: cert-manager.io/v1
|
||||||
kind: Certificate
|
kind: Certificate
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.rootCACertificate" . }}
|
name: {{ include "cert-manager-webhook-gandi.rootCACertificate" . }}
|
||||||
namespace: {{ .Release.Namespace | quote }}
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
spec:
|
spec:
|
||||||
secretName: {{ include "cert-manager-webhook-gandi.rootCACertificate" . }}
|
secretName: {{ include "cert-manager-webhook-gandi.rootCACertificate" . }}
|
||||||
duration: 43800h # 5y
|
duration: 43800h # 5y
|
||||||
@@ -34,37 +26,27 @@ spec:
|
|||||||
name: {{ include "cert-manager-webhook-gandi.selfSignedIssuer" . }}
|
name: {{ include "cert-manager-webhook-gandi.selfSignedIssuer" . }}
|
||||||
commonName: "ca.cert-manager-webhook-gandi.cert-manager"
|
commonName: "ca.cert-manager-webhook-gandi.cert-manager"
|
||||||
isCA: true
|
isCA: true
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Create an Issuer that uses the above generated CA certificate to issue certs
|
# Create an Issuer that uses the above generated CA certificate to issue certs
|
||||||
apiVersion: cert-manager.io/v1alpha2
|
apiVersion: cert-manager.io/v1
|
||||||
kind: Issuer
|
kind: Issuer
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.rootCAIssuer" . }}
|
name: {{ include "cert-manager-webhook-gandi.rootCAIssuer" . }}
|
||||||
namespace: {{ .Release.Namespace | quote }}
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
spec:
|
spec:
|
||||||
ca:
|
ca:
|
||||||
secretName: {{ include "cert-manager-webhook-gandi.rootCACertificate" . }}
|
secretName: {{ include "cert-manager-webhook-gandi.rootCACertificate" . }}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Finally, generate a serving certificate for the webhook to use
|
# Finally, generate a serving certificate for the webhook to use
|
||||||
apiVersion: cert-manager.io/v1alpha2
|
apiVersion: cert-manager.io/v1
|
||||||
kind: Certificate
|
kind: Certificate
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.servingCertificate" . }}
|
name: {{ include "cert-manager-webhook-gandi.servingCertificate" . }}
|
||||||
namespace: {{ .Release.Namespace | quote }}
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
spec:
|
spec:
|
||||||
secretName: {{ include "cert-manager-webhook-gandi.servingCertificate" . }}
|
secretName: {{ include "cert-manager-webhook-gandi.servingCertificate" . }}
|
||||||
duration: 8760h # 1y
|
duration: 8760h # 1y
|
||||||
@@ -72,5 +54,5 @@ spec:
|
|||||||
name: {{ include "cert-manager-webhook-gandi.rootCAIssuer" . }}
|
name: {{ include "cert-manager-webhook-gandi.rootCAIssuer" . }}
|
||||||
dnsNames:
|
dnsNames:
|
||||||
- {{ include "cert-manager-webhook-gandi.fullname" . }}
|
- {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
- {{ include "cert-manager-webhook-gandi.fullname" . }}.{{ .Release.Namespace }}
|
- {{ include "cert-manager-webhook-gandi.fullname" . }}.{{ .Values.certManager.namespace }}
|
||||||
- {{ include "cert-manager-webhook-gandi.fullname" . }}.{{ .Release.Namespace }}.svc
|
- {{ include "cert-manager-webhook-gandi.fullname" . }}.{{ .Values.certManager.namespace }}.svc
|
||||||
|
|||||||
@@ -2,25 +2,20 @@ apiVersion: v1
|
|||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
---
|
---
|
||||||
# Grant the webhook permission to read the ConfigMap containing the Kubernetes
|
# Grant the webhook permission to read the ConfigMap containing the Kubernetes
|
||||||
# apiserver's requestheader-ca-certificate.
|
# apiserver's requestheader-ca-certificate
|
||||||
# This ConfigMap is automatically created by the Kubernetes apiserver.
|
# This ConfigMap is automatically created by the Kubernetes apiserver
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: RoleBinding
|
kind: RoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:webhook-authentication-reader
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:webhook-authentication-reader
|
||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: Role
|
kind: Role
|
||||||
@@ -29,7 +24,7 @@ subjects:
|
|||||||
- apiGroup: ""
|
- apiGroup: ""
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
---
|
---
|
||||||
# apiserver gets the auth-delegator role to delegate auth decisions to
|
# apiserver gets the auth-delegator role to delegate auth decisions to
|
||||||
# the core apiserver
|
# the core apiserver
|
||||||
@@ -38,10 +33,7 @@ kind: ClusterRoleBinding
|
|||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:auth-delegator
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:auth-delegator
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
@@ -50,7 +42,7 @@ subjects:
|
|||||||
- apiGroup: ""
|
- apiGroup: ""
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Values.certManager.namespace | quote}}
|
||||||
---
|
---
|
||||||
# Grant cert-manager permission to validate using our apiserver
|
# Grant cert-manager permission to validate using our apiserver
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
@@ -58,27 +50,21 @@ kind: ClusterRole
|
|||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:domain-solver
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:domain-solver
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
rules:
|
rules:
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- {{ .Values.groupName }}
|
- {{ .Values.groupName }}
|
||||||
resources:
|
resources:
|
||||||
- '*'
|
- "*"
|
||||||
verbs:
|
verbs:
|
||||||
- 'create'
|
- "create"
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:domain-solver
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:domain-solver
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
@@ -87,4 +73,72 @@ subjects:
|
|||||||
- apiGroup: ""
|
- apiGroup: ""
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
name: {{ .Values.certManager.serviceAccountName }}
|
name: {{ .Values.certManager.serviceAccountName }}
|
||||||
namespace: {{ .Values.certManager.namespace }}
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:secret-reader
|
||||||
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- "secrets"
|
||||||
|
resourceNames:
|
||||||
|
- "gandi-credentials"
|
||||||
|
verbs:
|
||||||
|
- "get"
|
||||||
|
- "watch"
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:secret-reader
|
||||||
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:secret-reader
|
||||||
|
subjects:
|
||||||
|
- apiGroup: ""
|
||||||
|
kind: ServiceAccount
|
||||||
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
|
{{- if .Values.features.apiPriorityAndFairness }}
|
||||||
|
---
|
||||||
|
# Grant cert-manager-webhook-gandi permission to read the flow control mechanism (APF)
|
||||||
|
# API Priority and Fairness is enabled by default in Kubernetes 1.20
|
||||||
|
# https://kubernetes.io/docs/concepts/cluster-administration/flow-control/
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:flowcontrol-solver
|
||||||
|
labels:
|
||||||
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- "flowcontrol.apiserver.k8s.io"
|
||||||
|
resources:
|
||||||
|
- "prioritylevelconfigurations"
|
||||||
|
- "flowschemas"
|
||||||
|
verbs:
|
||||||
|
- "list"
|
||||||
|
- "watch"
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:flowcontrol-solver
|
||||||
|
labels:
|
||||||
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}:flowcontrol-solver
|
||||||
|
subjects:
|
||||||
|
- apiGroup: ""
|
||||||
|
kind: ServiceAccount
|
||||||
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
|
{{- end }}
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||||
|
namespace: {{ .Values.certManager.namespace | quote }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.labels" . | nindent 4 }}
|
||||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
heritage: {{ .Release.Service }}
|
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.service.type }}
|
type: {{ .Values.service.type }}
|
||||||
ports:
|
ports:
|
||||||
@@ -15,5 +13,4 @@ spec:
|
|||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: https
|
name: https
|
||||||
selector:
|
selector:
|
||||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
{{- include "cert-manager-webhook-gandi.selectorLabels" . | nindent 4 }}
|
||||||
release: {{ .Release.Name }}
|
|
||||||
|
|||||||
@@ -1,46 +1,21 @@
|
|||||||
# The GroupName here is used to identify your company or business unit that
|
|
||||||
# created this webhook.
|
|
||||||
# For example, this may be "acme.mycompany.com".
|
|
||||||
# This name will need to be referenced in each Issuer's `webhook` stanza to
|
|
||||||
# inform cert-manager of where to send ChallengePayload resources in order to
|
|
||||||
# solve the DNS01 challenge.
|
|
||||||
# This group name should be **unique**, hence using your own company's domain
|
|
||||||
# here is recommended.
|
|
||||||
groupName: acme.bwolf.me
|
groupName: acme.bwolf.me
|
||||||
|
|
||||||
logLevel: 6
|
logLevel: 6
|
||||||
|
|
||||||
certManager:
|
certManager:
|
||||||
namespace: cert-manager
|
namespace: cert-manager
|
||||||
serviceAccountName: cert-manager
|
serviceAccountName: cert-manager
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: bwolf/cert-manager-webhook-gandi
|
repository: git.mug.lv/galen/cert-manager-webhook-gandi-server
|
||||||
tag: latest
|
# tag: X.Y.Z defaults to app version
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
nameOverride: ''
|
||||||
nameOverride: ""
|
fullnameOverride: ''
|
||||||
fullnameOverride: ""
|
|
||||||
|
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 443
|
port: 443
|
||||||
|
containerport: 8443
|
||||||
resources:
|
features:
|
||||||
{}
|
apiPriorityAndFairness: false
|
||||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
resources: {}
|
||||||
# choice for the user. This also increases chances charts run on environments with little
|
|
||||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
|
||||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
|
||||||
# limits:
|
|
||||||
# cpu: 100m
|
|
||||||
# memory: 128Mi
|
|
||||||
# requests:
|
|
||||||
# cpu: 100m
|
|
||||||
# memory: 128Mi
|
|
||||||
|
|
||||||
nodeSelector: {}
|
nodeSelector: {}
|
||||||
|
|
||||||
tolerations: []
|
tolerations: []
|
||||||
|
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|||||||
11
examples/certificates/certif-example-com-clusterissuer.yaml
Normal file
11
examples/certificates/certif-example-com-clusterissuer.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: example-com
|
||||||
|
spec:
|
||||||
|
dnsNames:
|
||||||
|
- example.com
|
||||||
|
issuerRef:
|
||||||
|
name: letsencrypt-staging
|
||||||
|
kind: ClusterIssuer
|
||||||
|
secretName: example-com-tls
|
||||||
10
examples/certificates/certif-example-com.yaml
Normal file
10
examples/certificates/certif-example-com.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: example-com
|
||||||
|
spec:
|
||||||
|
dnsNames:
|
||||||
|
- example.com
|
||||||
|
issuerRef:
|
||||||
|
name: letsencrypt-staging
|
||||||
|
secretName: example-com-tls
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: wildcard-example-com
|
||||||
|
spec:
|
||||||
|
dnsNames:
|
||||||
|
- '*.example.com'
|
||||||
|
issuerRef:
|
||||||
|
name: letsencrypt-staging
|
||||||
|
kind: ClusterIssuer
|
||||||
|
secretName: wildcard-example-com-tls
|
||||||
10
examples/certificates/certif-wildcard-example-com.yaml
Normal file
10
examples/certificates/certif-wildcard-example-com.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: wildcard-example-com
|
||||||
|
spec:
|
||||||
|
dnsNames:
|
||||||
|
- '*.example.com'
|
||||||
|
issuerRef:
|
||||||
|
name: letsencrypt-staging
|
||||||
|
secretName: wildcard-example-com
|
||||||
22
examples/issuers/letsencrypt-staging-clusterissuer.yaml
Normal file
22
examples/issuers/letsencrypt-staging-clusterissuer.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: ClusterIssuer
|
||||||
|
metadata:
|
||||||
|
name: letsencrypt-staging
|
||||||
|
spec:
|
||||||
|
acme:
|
||||||
|
# The ACME server URL
|
||||||
|
server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
# Email address used for ACME registration
|
||||||
|
email: invalid@example.com
|
||||||
|
# Name of a secret used to store the ACME account private key
|
||||||
|
privateKeySecretRef:
|
||||||
|
name: letsencrypt-staging
|
||||||
|
solvers:
|
||||||
|
- dns01:
|
||||||
|
webhook:
|
||||||
|
groupName: acme.bwolf.me
|
||||||
|
solverName: gandi
|
||||||
|
config:
|
||||||
|
apiKeySecretRef:
|
||||||
|
key: api-token
|
||||||
|
name: gandi-credentials
|
||||||
23
examples/issuers/letsencrypt-staging-issuer.yaml
Normal file
23
examples/issuers/letsencrypt-staging-issuer.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Issuer
|
||||||
|
metadata:
|
||||||
|
name: letsencrypt-staging
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
acme:
|
||||||
|
# The ACME server URL
|
||||||
|
server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
# Email address used for ACME registration
|
||||||
|
email: invalid@example.com
|
||||||
|
# Name of a secret used to store the ACME account private key
|
||||||
|
privateKeySecretRef:
|
||||||
|
name: letsencrypt-staging
|
||||||
|
solvers:
|
||||||
|
- dns01:
|
||||||
|
webhook:
|
||||||
|
groupName: acme.bwolf.me
|
||||||
|
solverName: gandi
|
||||||
|
config:
|
||||||
|
apiKeySecretRef:
|
||||||
|
key: api-token
|
||||||
|
name: gandi-credentials
|
||||||
189
gandiclient.go
189
gandiclient.go
@@ -1,189 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
GandiLiveDnsBaseUrl = "https://dns.api.gandi.net/api/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GandiClient struct {
|
|
||||||
apiKey string
|
|
||||||
dumpRequestResponse bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type GandiRRSet struct {
|
|
||||||
Type string `json:"rrset_type"`
|
|
||||||
TTL int `json:"rrset_ttl"`
|
|
||||||
Name string `json:"rrset_name"`
|
|
||||||
Values []string `json:"rrset_values"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GandiRRSetValues struct {
|
|
||||||
TTL int `json:"rrset_ttl"`
|
|
||||||
Values []string `json:"rrset_values"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGandiClient(apiKey string) *GandiClient {
|
|
||||||
return &GandiClient{
|
|
||||||
apiKey: apiKey,
|
|
||||||
dumpRequestResponse: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GandiClient) gandiRecordsUrl(domain string) string {
|
|
||||||
return fmt.Sprintf("%s/domains/%s/records", GandiLiveDnsBaseUrl, domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GandiClient) doRequest(req *http.Request, readResponseBody bool) (int, []byte, error) {
|
|
||||||
if c.dumpRequestResponse {
|
|
||||||
dump, _ := httputil.DumpRequest(req, true)
|
|
||||||
fmt.Printf("Request: %q\n", dump)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("X-Api-Key", c.apiKey)
|
|
||||||
|
|
||||||
client := http.Client{
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.dumpRequestResponse {
|
|
||||||
dump, _ := httputil.DumpResponse(res, true)
|
|
||||||
fmt.Printf("Response: %q\n", dump)
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.StatusCode == http.StatusOK && readResponseBody {
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
return res.StatusCode, data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.StatusCode, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GandiClient) HasTxtRecord(domain *string, name *string) (bool, error) {
|
|
||||||
// curl -H "X-Api-Key: $APIKEY" \
|
|
||||||
// https://dns.api.gandi.net/api/v5/domains/<DOMAIN>/records/<NAME>/<TYPE>
|
|
||||||
url := fmt.Sprintf("%s/%s/TXT", c.gandiRecordsUrl(*domain), *name)
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
status, _, err := c.doRequest(req, false)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if status == http.StatusNotFound {
|
|
||||||
return false, nil
|
|
||||||
} else if status == http.StatusOK {
|
|
||||||
// Maybe parse response body here to really ensure that the record is present
|
|
||||||
return true, nil
|
|
||||||
} else {
|
|
||||||
return false, fmt.Errorf("unexpected HTTP status: %d", status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GandiClient) CreateTxtRecord(domain *string, name *string, value *string, ttl int) error {
|
|
||||||
// curl -X POST -H "Content-Type: application/json" \
|
|
||||||
// -H "X-Api-Key: $APIKEY" \
|
|
||||||
// -d '{"rrset_name": "<NAME>",
|
|
||||||
// "rrset_type": "<TYPE>",
|
|
||||||
// "rrset_ttl": 10800,
|
|
||||||
// "rrset_values": ["<VALUE>"]}' \
|
|
||||||
// https://dns.api.gandi.net/api/v5/domains/<DOMAIN>/records
|
|
||||||
rrs := GandiRRSet{Name: *name, Type: "TXT", TTL: ttl, Values: []string{*value}}
|
|
||||||
body, err := json.Marshal(rrs)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot marshall to json: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
url := c.gandiRecordsUrl(*domain)
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewReader(body))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
status, _, err := c.doRequest(req, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if status != http.StatusCreated && status != http.StatusOK {
|
|
||||||
return fmt.Errorf("failed creating TXT record: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GandiClient) UpdateTxtRecord(domain *string, name *string, value *string, ttl int) error {
|
|
||||||
// curl -X PUT -H "Content-Type: application/json" \
|
|
||||||
// -H "X-Api-Key: $APIKEY" \
|
|
||||||
// -d '{"rrset_ttl": 10800,
|
|
||||||
// "rrset_values":["<VALUE>"]}' \
|
|
||||||
// https://dns.api.gandi.net/api/v5/domains/<DOMAIN>/records/<NAME>/<TYPE>
|
|
||||||
rrs := GandiRRSetValues{TTL: ttl, Values: []string{*value}}
|
|
||||||
body, err := json.Marshal(rrs)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot marshall to json: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/%s/TXT", c.gandiRecordsUrl(*domain), *name)
|
|
||||||
req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
status, _, err := c.doRequest(req, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if status != http.StatusCreated && status != http.StatusOK {
|
|
||||||
return fmt.Errorf("failed updating TXT record: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GandiClient) DeleteTxtRecord(domain *string, name *string) error {
|
|
||||||
// curl -X DELETE -H "Content-Type: application/json" \
|
|
||||||
// -H "X-Api-Key: $APIKEY" \
|
|
||||||
// https://dns.api.gandi.net/api/v5/domains/<DOMAIN>/records/<NAME>/<TYPE>
|
|
||||||
url := fmt.Sprintf("%s/%s/TXT", c.gandiRecordsUrl(*domain), *name)
|
|
||||||
req, err := http.NewRequest("DELETE", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
status, _, err := c.doRequest(req, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if status != http.StatusOK && status != http.StatusNoContent {
|
|
||||||
return fmt.Errorf("failed deleting TXT record: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
118
go.mod
118
go.mod
@@ -1,11 +1,117 @@
|
|||||||
module github.com/bwolf/cert-manager-webhook-gandi
|
module github.com/bwolf/cert-manager-webhook-gandi
|
||||||
|
|
||||||
go 1.13
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/jetstack/cert-manager v0.13.1
|
github.com/cert-manager/cert-manager v1.12.1
|
||||||
k8s.io/apiextensions-apiserver v0.17.0
|
github.com/go-gandi/go-gandi v0.6.0
|
||||||
k8s.io/apimachinery v0.17.0
|
k8s.io/apiextensions-apiserver v0.27.2
|
||||||
k8s.io/client-go v0.17.0
|
k8s.io/apimachinery v0.27.2
|
||||||
k8s.io/klog v1.0.0
|
k8s.io/client-go v0.27.2
|
||||||
|
k8s.io/klog/v2 v2.100.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||||
|
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/coreos/go-semver v0.3.0 // indirect
|
||||||
|
github.com/coreos/go-systemd/v22 v22.4.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
||||||
|
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||||
|
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-logr/zapr v1.2.4 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.20.1 // indirect
|
||||||
|
github.com/go-openapi/swag v0.22.3 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/google/cel-go v0.12.6 // indirect
|
||||||
|
github.com/google/gnostic v0.6.9 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
|
github.com/miekg/dns v1.1.50 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/peterhellberg/link v1.2.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.15.1 // indirect
|
||||||
|
github.com/prometheus/client_model v0.4.0 // indirect
|
||||||
|
github.com/prometheus/common v0.42.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.9.0 // indirect
|
||||||
|
github.com/smartystreets/goconvey v1.8.1 // indirect
|
||||||
|
github.com/spf13/cobra v1.7.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.7 // indirect
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
|
||||||
|
go.etcd.io/etcd/client/v3 v3.5.7 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.39.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.15.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v0.36.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk v1.15.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.15.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
go.uber.org/zap v1.24.0 // indirect
|
||||||
|
golang.org/x/crypto v0.14.0 // indirect
|
||||||
|
golang.org/x/mod v0.13.0 // indirect
|
||||||
|
golang.org/x/net v0.16.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.5.0 // indirect
|
||||||
|
golang.org/x/sync v0.4.0 // indirect
|
||||||
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
|
golang.org/x/term v0.13.0 // indirect
|
||||||
|
golang.org/x/text v0.13.0 // indirect
|
||||||
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
golang.org/x/tools v0.14.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 // indirect
|
||||||
|
google.golang.org/grpc v1.54.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
k8s.io/api v0.27.2 // indirect
|
||||||
|
k8s.io/apiserver v0.27.2 // indirect
|
||||||
|
k8s.io/component-base v0.27.2 // indirect
|
||||||
|
k8s.io/kms v0.27.2 // indirect
|
||||||
|
k8s.io/kube-aggregator v0.27.2 // indirect
|
||||||
|
k8s.io/kube-openapi v0.0.0-20230515203736-54b630e78af5 // indirect
|
||||||
|
k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect
|
||||||
|
moul.io/http2curl v1.0.0 // indirect
|
||||||
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2 // indirect
|
||||||
|
sigs.k8s.io/controller-runtime v0.15.0 // indirect
|
||||||
|
sigs.k8s.io/gateway-api v0.7.0 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/go-gandi/go-gandi => git.mug.lv/galen/go-gandi v0.0.0-20231017185104-ac95d5a2c330
|
||||||
|
|||||||
129
main.go
129
main.go
@@ -1,27 +1,37 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
"github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
|
||||||
extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
"github.com/cert-manager/cert-manager/pkg/acme/webhook/cmd"
|
||||||
|
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
|
||||||
|
"github.com/go-gandi/go-gandi"
|
||||||
|
"github.com/go-gandi/go-gandi/config"
|
||||||
|
"github.com/go-gandi/go-gandi/livedns"
|
||||||
|
"github.com/go-gandi/go-gandi/types"
|
||||||
|
extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
"github.com/jetstack/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
|
|
||||||
"github.com/jetstack/cert-manager/pkg/acme/webhook/cmd"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GandiMinTtl = 300 // Gandi reports an error for values < this value
|
GandiMinTtl = 300 // Gandi reports an error for values < this value
|
||||||
)
|
)
|
||||||
|
|
||||||
var GroupName = os.Getenv("GROUP_NAME")
|
var (
|
||||||
|
DebugHTTP = os.Getenv("DEBUG_HTTP")
|
||||||
|
GroupName = os.Getenv("GROUP_NAME")
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if GroupName == "" {
|
if GroupName == "" {
|
||||||
@@ -40,7 +50,7 @@ func main() {
|
|||||||
|
|
||||||
// gandiDNSProviderSolver implements the provider-specific logic needed to
|
// gandiDNSProviderSolver implements the provider-specific logic needed to
|
||||||
// 'present' an ACME challenge TXT record for your own DNS provider.
|
// 'present' an ACME challenge TXT record for your own DNS provider.
|
||||||
// To do so, it must implement the `github.com/jetstack/cert-manager/pkg/acme/webhook.Solver`
|
// To do so, it must implement the `github.com/cert-manager/cert-manager/pkg/acme/webhook.Solver`
|
||||||
// interface.
|
// interface.
|
||||||
type gandiDNSProviderSolver struct {
|
type gandiDNSProviderSolver struct {
|
||||||
client *kubernetes.Clientset
|
client *kubernetes.Clientset
|
||||||
@@ -85,38 +95,30 @@ func (c *gandiDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error {
|
|||||||
klog.V(6).Infof("call function Present: namespace=%s, zone=%s, fqdn=%s",
|
klog.V(6).Infof("call function Present: namespace=%s, zone=%s, fqdn=%s",
|
||||||
ch.ResourceNamespace, ch.ResolvedZone, ch.ResolvedFQDN)
|
ch.ResourceNamespace, ch.ResolvedZone, ch.ResolvedFQDN)
|
||||||
|
|
||||||
cfg, err := loadConfig(ch.Config)
|
client, err := c.newClient(ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to load config: %v", err)
|
return fmt.Errorf("failed to initialize client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
klog.V(6).Infof("decoded configuration %v", cfg)
|
|
||||||
|
|
||||||
apiKey, err := c.getApiKey(&cfg, ch.ResourceNamespace)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get API key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gandiClient := NewGandiClient(*apiKey)
|
|
||||||
|
|
||||||
entry, domain := c.getDomainAndEntry(ch)
|
entry, domain := c.getDomainAndEntry(ch)
|
||||||
klog.V(6).Infof("present for entry=%s, domain=%s", entry, domain)
|
|
||||||
|
|
||||||
present, err := gandiClient.HasTxtRecord(&domain, &entry)
|
klog.V(6).Infof("present for entry=%s, domain=%s", entry, domain)
|
||||||
if err != nil {
|
present := true
|
||||||
return fmt.Errorf("unable to check TXT record: %v", err)
|
_, err = client.GetDomainRecordByNameAndType(domain, entry, "TXT")
|
||||||
|
var reqErr *types.RequestError
|
||||||
|
if errors.As(err, &reqErr) && reqErr.StatusCode == http.StatusNotFound {
|
||||||
|
present = false
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("failed to get record: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
txtValues := []string{fmt.Sprintf(`"%s"`, ch.Key)}
|
||||||
if present {
|
if present {
|
||||||
err := gandiClient.UpdateTxtRecord(&domain, &entry, &ch.Key, GandiMinTtl)
|
_, err = client.UpdateDomainRecordByNameAndType(domain, entry, "TXT", GandiMinTtl, txtValues)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to change TXT record: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err := gandiClient.CreateTxtRecord(&domain, &entry, &ch.Key, GandiMinTtl)
|
_, err = client.CreateDomainRecord(domain, entry, "TXT", GandiMinTtl, txtValues)
|
||||||
if err != nil {
|
}
|
||||||
return fmt.Errorf("unable to create TXT record: %v", err)
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("failed to set record: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -132,31 +134,19 @@ func (c *gandiDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error {
|
|||||||
klog.V(6).Infof("call function CleanUp: namespace=%s, zone=%s, fqdn=%s",
|
klog.V(6).Infof("call function CleanUp: namespace=%s, zone=%s, fqdn=%s",
|
||||||
ch.ResourceNamespace, ch.ResolvedZone, ch.ResolvedFQDN)
|
ch.ResourceNamespace, ch.ResolvedZone, ch.ResolvedFQDN)
|
||||||
|
|
||||||
cfg, err := loadConfig(ch.Config)
|
client, err := c.newClient(ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to initialize client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
apiKey, err := c.getApiKey(&cfg, ch.ResourceNamespace)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get API key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gandiClient := NewGandiClient(*apiKey)
|
|
||||||
|
|
||||||
entry, domain := c.getDomainAndEntry(ch)
|
entry, domain := c.getDomainAndEntry(ch)
|
||||||
|
|
||||||
present, err := gandiClient.HasTxtRecord(&domain, &entry)
|
klog.V(6).Infof("deleting entry=%s, domain=%s", entry, domain)
|
||||||
if err != nil {
|
err = client.DeleteDomainRecord(domain, entry, "TXT")
|
||||||
return fmt.Errorf("unable to check TXT record: %v", err)
|
var reqErr *types.RequestError
|
||||||
}
|
if errors.As(err, &reqErr) && reqErr.StatusCode == http.StatusNotFound {
|
||||||
|
return nil
|
||||||
if present {
|
} else if err != nil {
|
||||||
klog.V(6).Infof("deleting entry=%s, domain=%s", entry, domain)
|
return fmt.Errorf("failed to delete record: %w", err)
|
||||||
err := gandiClient.DeleteTxtRecord(&domain, &entry)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to remove TXT record: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -196,7 +186,6 @@ func loadConfig(cfgJSON *extapi.JSON) (gandiDNSProviderConfig, error) {
|
|||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (c *gandiDNSProviderSolver) getDomainAndEntry(ch *v1alpha1.ChallengeRequest) (string, string) {
|
func (c *gandiDNSProviderSolver) getDomainAndEntry(ch *v1alpha1.ChallengeRequest) (string, string) {
|
||||||
// Both ch.ResolvedZone and ch.ResolvedFQDN end with a dot: '.'
|
// Both ch.ResolvedZone and ch.ResolvedFQDN end with a dot: '.'
|
||||||
entry := strings.TrimSuffix(ch.ResolvedFQDN, ch.ResolvedZone)
|
entry := strings.TrimSuffix(ch.ResolvedFQDN, ch.ResolvedZone)
|
||||||
@@ -205,24 +194,40 @@ func (c *gandiDNSProviderSolver) getDomainAndEntry(ch *v1alpha1.ChallengeRequest
|
|||||||
return entry, domain
|
return entry, domain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *gandiDNSProviderSolver) newClient(ch *v1alpha1.ChallengeRequest) (*livedns.LiveDNS, error) {
|
||||||
|
cfg, err := loadConfig(ch.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
apiKey, err := c.getApiKey(&cfg, ch.ResourceNamespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to get API key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gandi.NewLiveDNSClient(config.Config{
|
||||||
|
Token: apiKey,
|
||||||
|
Timeout: time.Second * 30,
|
||||||
|
Debug: DebugHTTP != "",
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Get Gandi API key from Kubernetes secret.
|
// Get Gandi API key from Kubernetes secret.
|
||||||
func (c *gandiDNSProviderSolver) getApiKey(cfg *gandiDNSProviderConfig, namespace string) (*string, error) {
|
func (c *gandiDNSProviderSolver) getApiKey(cfg *gandiDNSProviderConfig, namespace string) (string, error) {
|
||||||
secretName := cfg.APIKeySecretRef.LocalObjectReference.Name
|
secretName := cfg.APIKeySecretRef.LocalObjectReference.Name
|
||||||
|
|
||||||
klog.V(6).Infof("try to load secret `%s` with key `%s`", secretName, cfg.APIKeySecretRef.Key)
|
klog.V(6).Infof("try to load secret `%s` with key `%s`", secretName, cfg.APIKeySecretRef.Key)
|
||||||
|
|
||||||
sec, err := c.client.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{})
|
sec, err := c.client.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to get secret `%s`; %v", secretName, err)
|
return "", fmt.Errorf("unable to get secret `%s`; %v", secretName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secBytes, ok := sec.Data[cfg.APIKeySecretRef.Key]
|
secBytes, ok := sec.Data[cfg.APIKeySecretRef.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("key %q not found in secret \"%s/%s\"", cfg.APIKeySecretRef.Key,
|
return "", fmt.Errorf("key %q not found in secret \"%s/%s\"", cfg.APIKeySecretRef.Key,
|
||||||
cfg.APIKeySecretRef.LocalObjectReference.Name, namespace)
|
cfg.APIKeySecretRef.LocalObjectReference.Name, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
apiKey := string(secBytes)
|
return string(secBytes), nil
|
||||||
return &apiKey, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/jetstack/cert-manager/test/acme/dns"
|
"github.com/cert-manager/cert-manager/test/acme"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -18,7 +18,6 @@ func TestRunsSuite(t *testing.T) {
|
|||||||
|
|
||||||
solver := &gandiDNSProviderSolver{}
|
solver := &gandiDNSProviderSolver{}
|
||||||
fixture := dns.NewFixture(solver,
|
fixture := dns.NewFixture(solver,
|
||||||
dns.SetBinariesPath("__main__/hack/bin"),
|
|
||||||
dns.SetResolvedZone(zone),
|
dns.SetResolvedZone(zone),
|
||||||
dns.SetAllowAmbientCredentials(false),
|
dns.SetAllowAmbientCredentials(false),
|
||||||
dns.SetManifestPath("testdata/gandi"),
|
dns.SetManifestPath("testdata/gandi"),
|
||||||
|
|||||||
24
rbac.yaml
24
rbac.yaml
@@ -1,24 +0,0 @@
|
|||||||
# Role and RoleBinding for gandi-credentials in namespace default
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: Role
|
|
||||||
metadata:
|
|
||||||
name: cert-manager-webhook-gandi:secret-reader
|
|
||||||
rules:
|
|
||||||
- apiGroups: [""] # indicates the core API group
|
|
||||||
resources: ["secrets"]
|
|
||||||
resourceNames: ["gandi-credentials"]
|
|
||||||
verbs: ["get", "watch"]
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: RoleBinding
|
|
||||||
metadata:
|
|
||||||
name: cert-manager-webhook-gandi:secret-reader
|
|
||||||
subjects:
|
|
||||||
- apiGroup: ""
|
|
||||||
kind: ServiceAccount
|
|
||||||
name: cert-manager-webhook-gandi
|
|
||||||
namespace: cert-manager
|
|
||||||
roleRef:
|
|
||||||
kind: Role
|
|
||||||
name: cert-manager-webhook-gandi:secret-reader
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
mkdir -p __main__/hack
|
|
||||||
curl -sfL https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-1.14.1-darwin-amd64.tar.gz | tar xvz --strip-components=1 -C __main__/hack
|
|
||||||
33
scripts/release.sh
Executable file
33
scripts/release.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh -eux
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
version="$(yq '.version' ../deploy/cert-manager-webhook-gandi/Chart.yaml)"
|
||||||
|
version="${version#v}"
|
||||||
|
major="$(echo "$version" | cut -d'.' -f1)"
|
||||||
|
minor="$(echo "$version" | cut -d'.' -f2)"
|
||||||
|
patch="$(echo "$version" | cut -d'.' -f3)"
|
||||||
|
case "${1:-""}" in
|
||||||
|
major)
|
||||||
|
major=$((major + 1))
|
||||||
|
minor=0
|
||||||
|
patch=0
|
||||||
|
;;
|
||||||
|
minor)
|
||||||
|
minor=$((minor + 1))
|
||||||
|
patch=0
|
||||||
|
;;
|
||||||
|
patch)
|
||||||
|
patch=$((patch + 1))
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown release type"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
version="$major.$minor.$patch"
|
||||||
|
|
||||||
|
yq -i ".version |= \"v$version\"" ../deploy/cert-manager-webhook-gandi/Chart.yaml
|
||||||
|
git add ../deploy/
|
||||||
|
git commit -m "Bump version to v$version"
|
||||||
|
git tag -a "v$version"
|
||||||
14
shell.nix
Normal file
14
shell.nix
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
let
|
||||||
|
unstable = import (builtins.fetchTarball {
|
||||||
|
name = "nixos-unstable-2021-09-12";
|
||||||
|
url = "https://github.com/nixos/nixpkgs/archive/2ce4d21663113020195f1d953e360213954645b3.tar.gz";
|
||||||
|
sha256 = "15pnbmm702a4ni8dm2jdwl46b20qw7gfm5chlrvn7w54cm3h9p0c";
|
||||||
|
}) {};
|
||||||
|
in pkgs.mkShell {
|
||||||
|
buildInputs = with unstable; [
|
||||||
|
go_1_16
|
||||||
|
gopls
|
||||||
|
];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user