Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
||||
.gitignore
|
||||
.idea
|
||||
LICENSE
|
||||
.dockerignore
|
||||
.github
|
||||
Makefile
|
||||
README.*
|
||||
__main__
|
||||
LICENSE
|
||||
CHANGELOG.*
|
||||
_test
|
||||
_out
|
||||
apiserver.local.config
|
||||
cert-manager-webhook-gandi
|
||||
@@ -12,3 +15,7 @@ deploy
|
||||
main_test.go
|
||||
scripts
|
||||
testdata
|
||||
examples
|
||||
*.yaml
|
||||
Dockerfile
|
||||
charts
|
||||
182
.github/workflows/main.yml
vendored
182
.github/workflows/main.yml
vendored
@@ -1,16 +1,172 @@
|
||||
name: Container-image
|
||||
on: [push]
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- '!v0.1.*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build docker image
|
||||
base:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
go_version: ${{ steps.get_versions.outputs.go_version }}
|
||||
build_version: ${{ steps.get_versions.outputs.build_version }}
|
||||
chart_version: ${{ steps.get_versions.outputs.chart_version }}
|
||||
go_updated: ${{ steps.file_updates.outputs.go }}
|
||||
yaml_updated: ${{ steps.file_updates.outputs.yaml }}
|
||||
|
||||
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
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get versions
|
||||
id: get_versions
|
||||
run: |
|
||||
echo ::set-output name=go_version::$(go mod edit -json | grep -Po '"Go":\s+"([0-9.]+)"' | sed -E 's/.+"([0-9.]+)"/\1/')
|
||||
echo ::set-output name=build_version::${GITHUB_REF#refs/tags/v}
|
||||
echo ::set-output name=chart_version::${GITHUB_REF#refs/tags/}
|
||||
|
||||
- name: Get last release
|
||||
id: last_release
|
||||
shell: bash
|
||||
run: |
|
||||
tag=$(gh release list | sed -n '1 p' | awk '{print $(NF -1);}')
|
||||
sha=$(git show-ref --tags | grep $tag | awk '{print $1;}')
|
||||
echo ::set-output name=sha::$sha
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check updated files
|
||||
id: file_updates
|
||||
uses: dorny/paths-filter@v2
|
||||
with:
|
||||
base: ${{ steps.last_release.outputs.sha }}
|
||||
filters: |
|
||||
go:
|
||||
- '*.go'
|
||||
- 'go.*'
|
||||
yaml:
|
||||
- 'deploy/cert-manager-webhook-gandi/*.yaml'
|
||||
- 'deploy/cert-manager-webhook-gandi/templates/*.yaml'
|
||||
|
||||
build:
|
||||
needs: base
|
||||
if: ${{ needs.base.outputs.go_updated == 'true' }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
tag_commit: ${{ steps.update_image.outputs.commit }}
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Set up Docker buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64
|
||||
target: image
|
||||
push: true
|
||||
build-args: GO_VERSION=${{ needs.base.outputs.go_version }}
|
||||
tags: bwolf/cert-manager-webhook-gandi:latest,bwolf/cert-manager-webhook-gandi:${{ needs.base.outputs.build_version }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
||||
|
||||
# Temp fix
|
||||
# https://github.com/docker/build-push-action/issues/252
|
||||
# https://github.com/moby/buildkit/issues/1896
|
||||
- name: Move cache
|
||||
run: |
|
||||
rm -rf /tmp/.buildx-cache
|
||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
||||
|
||||
- name: Update Helm image tag
|
||||
id: update_image
|
||||
uses: fjogeleit/yaml-update-action@master
|
||||
with:
|
||||
valueFile: 'deploy/cert-manager-webhook-gandi/values.yaml'
|
||||
propertyPath: 'image.tag'
|
||||
value: ${{ needs.base.outputs.build_version }}
|
||||
message: 'Update image tag to ${{ needs.base.outputs.build_version }}'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
release:
|
||||
needs:
|
||||
- base
|
||||
- build
|
||||
|
||||
if: |
|
||||
always() && needs.base.result == 'success' &&
|
||||
((needs.build.result == 'success' && needs.build.outputs.tag_commit != '' ) || (needs.build.result == 'skipped' && needs.base.outputs.yaml_updated == 'true'))
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Retrieve commit ref
|
||||
id: commit_ref
|
||||
run: |
|
||||
if [[ '${{ needs.build.outputs.tag_commit }}' = '' ]]; then
|
||||
sha=${{ github.sha }}
|
||||
else
|
||||
sha=${{ needs.build.outputs.tag_commit }}
|
||||
fi
|
||||
|
||||
echo ::set-output name=sha::$sha
|
||||
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ steps.commit_ref.outputs.sha }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
|
||||
- name: Update Helm chart version
|
||||
uses: fjogeleit/yaml-update-action@master
|
||||
with:
|
||||
valueFile: 'deploy/cert-manager-webhook-gandi/Chart.yaml'
|
||||
propertyPath: 'version'
|
||||
value: ${{ needs.base.outputs.chart_version }}
|
||||
message: 'Update chart version to ${{ needs.base.outputs.chart_version }}'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
updateFile: true
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: v3.4.0
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.2.1
|
||||
with:
|
||||
charts_dir: deploy
|
||||
env:
|
||||
CR_RELEASE_NAME_TEMPLATE: '{{ .Version }}'
|
||||
CR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -7,7 +7,7 @@
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
/__main__/
|
||||
/_test/
|
||||
/apiserver.local.config/
|
||||
/_out/
|
||||
|
||||
@@ -16,3 +16,12 @@
|
||||
|
||||
# Ignore the built binary
|
||||
cert-manager-webhook-gandi
|
||||
|
||||
# Ignore the Helm chart
|
||||
/charts/
|
||||
|
||||
# JetBrains
|
||||
.idea
|
||||
|
||||
# Nix
|
||||
.envrc
|
||||
|
||||
7
CHANGELOG.md
Normal file
7
CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# CHANGELOG
|
||||
| Date | Version | Description |
|
||||
| ------ | ------ | ------ |
|
||||
| 2021-10-11 | v0.2.0 | add chart-releaser GitHub action |
|
||||
| 2021-10-06 | v0.2.0 | update cert-manager to 1.5.4<br>update k8s API version to 0.22.2<br>migrate to new LiveDNS API (https://api.gandi.net)<br>add Helm repo with GitHub pages<br>simplify Dockerfile & switch to Buildx<br>update make test target (remove shell script)<br>update README.md with changes made<br>update GitHub workflow with Buildx<br>add k8s APF support (k8s >= 1.20) |
|
||||
| 2020-02-26 | v0.1.1 | switch to Docker Hub |
|
||||
| 2020-02-26 | v0.1.0 | initial release |
|
||||
@@ -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
|
||||
ENV GO111MODULE=on
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
apk add --no-cache git ca-certificates && \
|
||||
go mod download
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
FROM base AS build
|
||||
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 . .
|
||||
|
||||
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"]
|
||||
ENTRYPOINT ["/usr/local/bin/webhook"]
|
||||
|
||||
50
Makefile
50
Makefile
@@ -1,20 +1,52 @@
|
||||
IMAGE_NAME := "cert-manager-webhook-gandi"
|
||||
IMAGE_TAG := "latest"
|
||||
OS ?= $(shell go env GOOS)
|
||||
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/')
|
||||
|
||||
IMAGE_NAME := bwolf/cert-manager-webhook-gandi
|
||||
IMAGE_TAG := 0.2.0
|
||||
|
||||
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 .
|
||||
|
||||
_test/kubebuilder:
|
||||
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
|
||||
|
||||
build:
|
||||
docker build --rm -t "$(IMAGE_NAME):$(IMAGE_TAG)" .
|
||||
docker buildx build --target=image --platform=linux/amd64 --output=type=docker,name=${IMAGE_NAME}:${IMAGE_TAG} --tag=${IMAGE_NAME}:latest --build-arg=GO_VERSION=${GO_VERSION} .
|
||||
|
||||
package:
|
||||
helm package deploy/cert-manager-webhook-gandi -d charts/
|
||||
helm repo index charts/ --url https://bwolf.github.io/cert-manager-webhook-gandi
|
||||
|
||||
.PHONY: rendered-manifest.yaml
|
||||
rendered-manifest.yaml:
|
||||
# --name cert-manager-webhook-gandi $BACKSLASH
|
||||
helm template \
|
||||
--set image.repository=$(IMAGE_NAME) \
|
||||
--set image.tag=$(IMAGE_TAG) \
|
||||
deploy/cert-manager-webhook-gandi > "$(OUT)/rendered-manifest.yaml"
|
||||
--set image.repository=${IMAGE_NAME} \
|
||||
--set image.tag=${IMAGE_TAG} \
|
||||
deploy/cert-manager-webhook-gandi > "${OUT}/rendered-manifest.yaml"
|
||||
193
README.md
193
README.md
@@ -13,17 +13,17 @@ Build the container image `cert-manager-webhook-gandi:latest`:
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
### Release History
|
||||
Refer to the [ChangeLog](ChangeLog.md) file.
|
||||
Refer to the [CHANGELOG](CHANGELOG.md) file.
|
||||
|
||||
|
||||
## 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
|
||||
@@ -36,171 +36,170 @@ This webhook has been tested with [cert-manager] v0.13.1 and Kubernetes v0.17.x
|
||||
|
||||
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 install cert-manager --namespace cert-manager \
|
||||
--set 'extraArgs={--dns01-recursive-nameservers=8.8.8.8:53\,1.1.1.1:53}' \
|
||||
jetstack/cert-manager
|
||||
|
||||
helm install 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
|
||||
|
||||
**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
|
||||
|
||||
Example output:
|
||||
Example output:
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 \
|
||||
--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.*
|
||||
|
||||
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):
|
||||
*The `features.apiPriorityAndFairness` argument must be removed or set to `false` for Kubernetes older than 1.20.*
|
||||
|
||||
helm install cert-manager-webhook-gandi \
|
||||
--namespace cert-manager \
|
||||
--set features.apiPriorityAndFairness=true \
|
||||
--set image.repository=cert-manager-webhook-gandi \
|
||||
--set image.tag=latest \
|
||||
--set logLevel=2 \
|
||||
./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 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 -
|
||||
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
|
||||
See [letsencrypt-staging-issuer.yaml](examples/issuers/letsencrypt-staging-issuer.yaml)
|
||||
|
||||
Check status of the Issuer:
|
||||
Don't forget to replace email `invalid@example.com`.
|
||||
|
||||
Check status of the Issuer:
|
||||
|
||||
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 -
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: example-com
|
||||
spec:
|
||||
dnsNames:
|
||||
- example-com
|
||||
issuerRef:
|
||||
name: letsencrypt-staging
|
||||
secretName: example-com-tls
|
||||
EOF
|
||||
7. Issue a [Certificate] for your domain: see [certif-example-com.yaml](examples/certificates/certif-example-com.yaml)
|
||||
|
||||
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 -
|
||||
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:
|
||||
|
||||
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
|
||||
kubectl delete -f rbac.yaml
|
||||
kubectl delete gandi-credentials
|
||||
kubectl delete gandi-credentials --namespace cert-manager
|
||||
|
||||
100. Uninstalling cert-manager:
|
||||
This is out of scope here. Refer to the official [documentation](https://cert-manager.io/docs/installation/uninstall/kubernetes/).
|
||||
10. Uninstalling cert-manager:
|
||||
This is out of scope here. Refer to the official [documentation][cert-manager-uninstall].
|
||||
|
||||
|
||||
## 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`.
|
||||
|
||||
|
||||
## Release process
|
||||
- Code changes result in a new image version and Git tag
|
||||
- Helm chart changes result in a new chart version
|
||||
## Release process (automated with [GitHub actions](.github/workflows/main.yml))
|
||||
- Changes in the Go code result in the build of a Docker image and the release of a new Helm chart
|
||||
- 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 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
|
||||
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.
|
||||
|
||||
``` shell
|
||||
cp testdata/gandi/api-key.yaml.sample testdata/gandi/api-key.yaml
|
||||
echo -n $YOUR_GANDI_API_KEY | base64 | pbcopy # or xclip
|
||||
$EDITOR testdata/gandi/api-key.yaml
|
||||
./scripts/fetch-test-binaries.sh
|
||||
TEST_ZONE_NAME=example.com. go test -v .
|
||||
TEST_ZONE_NAME=example.com. make test
|
||||
make clean
|
||||
```
|
||||
|
||||
|
||||
[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/
|
||||
[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
|
||||
[image tags]: https://hub.docker.com/r/bwolf/cert-manager-webhook-gandi
|
||||
[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
|
||||
description: A Helm chart for cert-manager-webhook-gandi
|
||||
name: cert-manager-webhook-gandi
|
||||
version: 0.1.1
|
||||
version: v0.1.1
|
||||
|
||||
@@ -45,4 +45,4 @@ Create chart name and version as used by the chart label.
|
||||
|
||||
{{- define "cert-manager-webhook-gandi.servingCertificate" -}}
|
||||
{{ printf "%s-webhook-tls" (include "cert-manager-webhook-gandi.fullname" .) }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: apiregistration.k8s.io/v1beta1
|
||||
apiVersion: apiregistration.k8s.io/v1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1alpha1.{{ .Values.groupName }}
|
||||
@@ -8,12 +8,12 @@ metadata:
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
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:
|
||||
group: {{ .Values.groupName }}
|
||||
groupPriorityMinimum: 1000
|
||||
versionPriority: 15
|
||||
service:
|
||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
version: v1alpha1
|
||||
namespace: {{ .Values.certManager.namespace | quote }}
|
||||
version: v1alpha1
|
||||
@@ -2,6 +2,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||
namespace: {{ .Values.certManager.namespace | quote }}
|
||||
labels:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
@@ -68,4 +69,4 @@ spec:
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
# Create a selfsigned Issuer, in order to create a root CA certificate for
|
||||
# signing webhook serving certificates
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: {{ include "cert-manager-webhook-gandi.selfSignedIssuer" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
namespace: {{ .Values.certManager.namespace | quote }}
|
||||
labels:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
@@ -13,15 +13,13 @@ metadata:
|
||||
heritage: {{ .Release.Service }}
|
||||
spec:
|
||||
selfSigned: {}
|
||||
|
||||
---
|
||||
|
||||
# Generate a CA Certificate used to sign certificates for the webhook
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ include "cert-manager-webhook-gandi.rootCACertificate" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
namespace: {{ .Values.certManager.namespace | quote }}
|
||||
labels:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
@@ -34,15 +32,13 @@ spec:
|
||||
name: {{ include "cert-manager-webhook-gandi.selfSignedIssuer" . }}
|
||||
commonName: "ca.cert-manager-webhook-gandi.cert-manager"
|
||||
isCA: true
|
||||
|
||||
---
|
||||
|
||||
# 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
|
||||
metadata:
|
||||
name: {{ include "cert-manager-webhook-gandi.rootCAIssuer" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
namespace: {{ .Values.certManager.namespace | quote }}
|
||||
labels:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
@@ -51,15 +47,13 @@ metadata:
|
||||
spec:
|
||||
ca:
|
||||
secretName: {{ include "cert-manager-webhook-gandi.rootCACertificate" . }}
|
||||
|
||||
---
|
||||
|
||||
# Finally, generate a serving certificate for the webhook to use
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ include "cert-manager-webhook-gandi.servingCertificate" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
namespace: {{ .Values.certManager.namespace | quote }}
|
||||
labels:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
@@ -72,5 +66,5 @@ spec:
|
||||
name: {{ include "cert-manager-webhook-gandi.rootCAIssuer" . }}
|
||||
dnsNames:
|
||||
- {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||
- {{ include "cert-manager-webhook-gandi.fullname" . }}.{{ .Release.Namespace }}
|
||||
- {{ include "cert-manager-webhook-gandi.fullname" . }}.{{ .Release.Namespace }}.svc
|
||||
- {{ include "cert-manager-webhook-gandi.fullname" . }}.{{ .Values.certManager.namespace }}
|
||||
- {{ include "cert-manager-webhook-gandi.fullname" . }}.{{ .Values.certManager.namespace }}.svc
|
||||
@@ -2,6 +2,7 @@ apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||
namespace: {{ .Values.certManager.namespace | quote }}
|
||||
labels:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
@@ -9,8 +10,8 @@ metadata:
|
||||
heritage: {{ .Release.Service }}
|
||||
---
|
||||
# Grant the webhook permission to read the ConfigMap containing the Kubernetes
|
||||
# apiserver's requestheader-ca-certificate.
|
||||
# This ConfigMap is automatically created by the Kubernetes apiserver.
|
||||
# apiserver's requestheader-ca-certificate
|
||||
# This ConfigMap is automatically created by the Kubernetes apiserver
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
@@ -29,7 +30,7 @@ subjects:
|
||||
- apiGroup: ""
|
||||
kind: ServiceAccount
|
||||
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
|
||||
# the core apiserver
|
||||
@@ -50,7 +51,7 @@ subjects:
|
||||
- apiGroup: ""
|
||||
kind: ServiceAccount
|
||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
namespace: {{ .Values.certManager.namespace | quote}}
|
||||
---
|
||||
# Grant cert-manager permission to validate using our apiserver
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
@@ -66,9 +67,9 @@ rules:
|
||||
- apiGroups:
|
||||
- {{ .Values.groupName }}
|
||||
resources:
|
||||
- '*'
|
||||
- "*"
|
||||
verbs:
|
||||
- 'create'
|
||||
- "create"
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
@@ -87,4 +88,78 @@ subjects:
|
||||
- apiGroup: ""
|
||||
kind: ServiceAccount
|
||||
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:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
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:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
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,6 +2,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "cert-manager-webhook-gandi.fullname" . }}
|
||||
namespace: {{ .Values.certManager.namespace | quote }}
|
||||
labels:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
chart: {{ include "cert-manager-webhook-gandi.chart" . }}
|
||||
@@ -16,4 +17,4 @@ spec:
|
||||
name: https
|
||||
selector:
|
||||
app: {{ include "cert-manager-webhook-gandi.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
@@ -16,7 +16,7 @@ certManager:
|
||||
|
||||
image:
|
||||
repository: bwolf/cert-manager-webhook-gandi
|
||||
tag: latest
|
||||
tag: v0.1.1
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
nameOverride: ""
|
||||
@@ -26,6 +26,11 @@ service:
|
||||
type: ClusterIP
|
||||
port: 443
|
||||
|
||||
features:
|
||||
# API Priority and Fairness should be enabled from Kubernetes 1.20
|
||||
# https://kubernetes.io/docs/concepts/cluster-administration/flow-control/
|
||||
apiPriorityAndFairness: false
|
||||
|
||||
resources:
|
||||
{}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
|
||||
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
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
GandiLiveDnsBaseUrl = "https://dns.api.gandi.net/api/v5"
|
||||
GandiLiveDnsBaseUrl = "https://api.gandi.net/v5/livedns"
|
||||
)
|
||||
|
||||
type GandiClient struct {
|
||||
@@ -33,7 +33,7 @@ type GandiRRSetValues struct {
|
||||
|
||||
func NewGandiClient(apiKey string) *GandiClient {
|
||||
return &GandiClient{
|
||||
apiKey: apiKey,
|
||||
apiKey: apiKey,
|
||||
dumpRequestResponse: false,
|
||||
}
|
||||
}
|
||||
@@ -48,8 +48,7 @@ func (c *GandiClient) doRequest(req *http.Request, readResponseBody bool) (int,
|
||||
fmt.Printf("Request: %q\n", dump)
|
||||
}
|
||||
|
||||
req.Header.Set("X-Api-Key", c.apiKey)
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Apikey %s", c.apiKey))
|
||||
client := http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
@@ -76,8 +75,9 @@ func (c *GandiClient) doRequest(req *http.Request, readResponseBody bool) (int,
|
||||
}
|
||||
|
||||
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>
|
||||
// curl -X GET -H "Content-Type: application/json" \
|
||||
// -H "Authorization: Apikey $APIKEY" \
|
||||
// https://api.gandi.net/v5/livedns/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 {
|
||||
@@ -101,12 +101,9 @@ func (c *GandiClient) HasTxtRecord(domain *string, name *string) (bool, error) {
|
||||
|
||||
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
|
||||
// -H "Authorization: Apikey $APIKEY" \
|
||||
// -d '{"rrset_name": "<NAME>", "rrset_type": "<TYPE>", "rrset_ttl": 10800, "rrset_values": ["<VALUE>"]}' \
|
||||
// https://api.gandi.net/v5/livedns/domains/<DOMAIN>/records
|
||||
rrs := GandiRRSet{Name: *name, Type: "TXT", TTL: ttl, Values: []string{*value}}
|
||||
body, err := json.Marshal(rrs)
|
||||
if err != nil {
|
||||
@@ -135,10 +132,9 @@ func (c *GandiClient) CreateTxtRecord(domain *string, name *string, value *strin
|
||||
|
||||
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>
|
||||
// -H "Authorization: Apikey $APIKEY" \
|
||||
// -d '{"rrset_ttl": 10800, "rrset_values":["<VALUE>"]}' \
|
||||
// https://api.gandi.net/v5/livedns/domains/<DOMAIN>/records/<NAME>/<TYPE>
|
||||
rrs := GandiRRSetValues{TTL: ttl, Values: []string{*value}}
|
||||
body, err := json.Marshal(rrs)
|
||||
if err != nil {
|
||||
@@ -167,8 +163,8 @@ func (c *GandiClient) UpdateTxtRecord(domain *string, name *string, value *strin
|
||||
|
||||
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>
|
||||
// -H "Authorization: Apikey $APIKEY" \
|
||||
// https://api.gandi.net/v5/livedns/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 {
|
||||
@@ -186,4 +182,3 @@ func (c *GandiClient) DeleteTxtRecord(domain *string, name *string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
114
go.mod
114
go.mod
@@ -1,11 +1,113 @@
|
||||
module github.com/bwolf/cert-manager-webhook-gandi
|
||||
|
||||
go 1.13
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/jetstack/cert-manager v0.13.1
|
||||
k8s.io/apiextensions-apiserver v0.17.0
|
||||
k8s.io/apimachinery v0.17.0
|
||||
k8s.io/client-go v0.17.0
|
||||
k8s.io/klog v1.0.0
|
||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful v2.15.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible // indirect
|
||||
github.com/felixge/httpsnoop v1.0.2 // indirect
|
||||
github.com/go-logr/logr v0.4.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // 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.2 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jetstack/cert-manager v1.5.4
|
||||
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.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/miekg/dns v1.1.34 // 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/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.11.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.31.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/spf13/cobra v1.2.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.0 // indirect
|
||||
go.opentelemetry.io/contrib v0.20.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v0.20.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211001223012-bfb93cce50d9 // indirect
|
||||
google.golang.org/grpc v1.41.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // 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.0-20210107192922-496545a6307b // indirect
|
||||
k8s.io/api v0.22.2 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.22.2
|
||||
k8s.io/apimachinery v0.22.2
|
||||
k8s.io/apiserver v0.22.2 // indirect
|
||||
k8s.io/client-go v0.22.2
|
||||
k8s.io/component-base v0.22.2 // indirect
|
||||
k8s.io/klog/v2 v2.9.0
|
||||
k8s.io/kube-aggregator v0.22.2 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20210929172449-94abcedd1aa4 // indirect
|
||||
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.24 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.9.2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
// Force packages versions over versions used by https://github.com/kubernetes/apiserver v0.22.2
|
||||
replace (
|
||||
github.com/go-logr/logr => github.com/go-logr/logr v0.4.0
|
||||
go.opentelemetry.io/contrib => go.opentelemetry.io/contrib v0.20.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc => go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0
|
||||
go.opentelemetry.io/otel => go.opentelemetry.io/otel v0.20.0
|
||||
go.opentelemetry.io/otel/exporters/otlp => go.opentelemetry.io/otel/exporters/otlp v0.20.0
|
||||
go.opentelemetry.io/otel/metric => go.opentelemetry.io/otel/metric v0.20.0
|
||||
go.opentelemetry.io/otel/sdk => go.opentelemetry.io/otel/sdk v0.20.0
|
||||
go.opentelemetry.io/otel/sdk/export/metric => go.opentelemetry.io/otel/sdk/export/metric v0.20.0
|
||||
go.opentelemetry.io/otel/sdk/metric => go.opentelemetry.io/otel/sdk/metric v0.20.0
|
||||
go.opentelemetry.io/otel/trace => go.opentelemetry.io/otel/trace v0.20.0
|
||||
go.opentelemetry.io/proto/otlp => go.opentelemetry.io/proto/otlp v0.7.0
|
||||
k8s.io/klog/v2 => k8s.io/klog/v2 v2.9.0
|
||||
)
|
||||
|
||||
16
main.go
16
main.go
@@ -1,20 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
||||
extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
|
||||
"github.com/jetstack/cert-manager/pkg/acme/webhook/cmd"
|
||||
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
||||
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/rest"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -196,7 +196,6 @@ func loadConfig(cfgJSON *extapi.JSON) (gandiDNSProviderConfig, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
||||
func (c *gandiDNSProviderSolver) getDomainAndEntry(ch *v1alpha1.ChallengeRequest) (string, string) {
|
||||
// Both ch.ResolvedZone and ch.ResolvedFQDN end with a dot: '.'
|
||||
entry := strings.TrimSuffix(ch.ResolvedFQDN, ch.ResolvedZone)
|
||||
@@ -211,7 +210,7 @@ func (c *gandiDNSProviderSolver) getApiKey(cfg *gandiDNSProviderConfig, namespac
|
||||
|
||||
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 {
|
||||
return nil, fmt.Errorf("unable to get secret `%s`; %v", secretName, err)
|
||||
}
|
||||
@@ -225,4 +224,3 @@ func (c *gandiDNSProviderSolver) getApiKey(cfg *gandiDNSProviderConfig, namespac
|
||||
apiKey := string(secBytes)
|
||||
return &apiKey, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ func TestRunsSuite(t *testing.T) {
|
||||
|
||||
solver := &gandiDNSProviderSolver{}
|
||||
fixture := dns.NewFixture(solver,
|
||||
dns.SetBinariesPath("__main__/hack/bin"),
|
||||
dns.SetResolvedZone(zone),
|
||||
dns.SetAllowAmbientCredentials(false),
|
||||
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
|
||||
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