├── deploy └── infomaniak-webhook │ ├── templates │ ├── NOTES.txt │ ├── namespace.yaml │ ├── service.yaml │ ├── apiservice.yaml │ ├── _helpers.tpl │ ├── pki.yaml │ ├── deployment.yaml │ └── rbac.yaml │ ├── Chart.yaml │ ├── .helmignore │ ├── README.md │ └── values.yaml ├── testdata └── infomaniak │ └── README.md ├── Dockerfile ├── .gitignore ├── Tilt.Dockerfile ├── tilt └── manifests.yaml ├── .github └── workflows │ ├── docker.yml │ └── manifest.yml ├── Makefile ├── main_test.go ├── README.md ├── Tiltfile ├── go.mod ├── main.go ├── infomaniak_api.go ├── LICENSE └── go.sum /deploy/infomaniak-webhook/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Infomaniak's cert-manager webhook 4 | name: infomaniak-webhook 5 | version: 0.2.0 6 | -------------------------------------------------------------------------------- /testdata/infomaniak/README.md: -------------------------------------------------------------------------------- 1 | # Solver testdata directory 2 | 3 | Running `go test` will generate 2 files here: 4 | - api-key.yaml: secret definition to contact APIs 5 | - config.json: the webhook config 6 | 7 | Don't edit these files, export `INFOMANIAK_TOKEN` & run `TEST_ZONE_NAME=example.com. make test` instead -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/templates/namespace.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.createReleaseNamespace -}} 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: {{ .Release.Namespace | quote }} 6 | labels: 7 | app: {{ include "infomaniak-webhook.name" . }} 8 | chart: {{ include "infomaniak-webhook.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | {{- end -}} -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.22.2-alpine3.19 AS build_deps 2 | 3 | RUN apk add --no-cache git 4 | 5 | WORKDIR /workspace 6 | 7 | COPY go.mod . 8 | COPY go.sum . 9 | 10 | RUN go mod download 11 | 12 | FROM build_deps AS build 13 | 14 | COPY . . 15 | 16 | RUN CGO_ENABLED=0 go build -o webhook -ldflags '-w -extldflags "-static"' . 17 | 18 | FROM alpine:3.19 19 | 20 | RUN apk add --no-cache ca-certificates 21 | 22 | COPY --from=build /workspace/webhook /usr/local/bin/webhook 23 | 24 | ENTRYPOINT ["webhook"] 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Ignore the built binary 15 | cert-manager-webhook-infomaniak 16 | 17 | # Make artifacts 18 | _out 19 | _test 20 | apiserver.local.config 21 | testdata/infomaniak/*.yaml 22 | testdata/infomaniak/*.json 23 | deploy/rendered-manifest.yaml 24 | .tiltbuild 25 | 26 | -------------------------------------------------------------------------------- /Tilt.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24.3-alpine3.21 AS build 2 | 3 | RUN go install github.com/go-delve/delve/cmd/dlv@v1.24 4 | 5 | FROM alpine:3.21 6 | 7 | RUN apk add --no-cache ca-certificates 8 | 9 | ADD https://raw.githubusercontent.com/tilt-dev/rerun-process-wrapper/master/restart.sh /restart.sh 10 | ADD https://raw.githubusercontent.com/tilt-dev/rerun-process-wrapper/master/start.sh /start.sh 11 | RUN chmod +x /start.sh && chmod +x /restart.sh && touch /process.txt && chmod 0777 /process.txt 12 | COPY webhook /usr/local/bin/webhook 13 | COPY --from=build /go/bin/dlv /usr/local/bin/dlv 14 | 15 | ENTRYPOINT ["/start.sh", "webhook"] 16 | -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "infomaniak-webhook.fullname" . }} 5 | namespace: {{ .Release.Namespace | quote }} 6 | labels: 7 | app: {{ include "infomaniak-webhook.name" . }} 8 | chart: {{ include "infomaniak-webhook.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | spec: 12 | type: {{ .Values.service.type }} 13 | ports: 14 | - port: {{ .Values.service.port }} 15 | targetPort: https 16 | protocol: TCP 17 | name: https 18 | selector: 19 | app: {{ include "infomaniak-webhook.name" . }} 20 | release: {{ .Release.Name }} 21 | -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/templates/apiservice.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiregistration.k8s.io/v1 2 | kind: APIService 3 | metadata: 4 | name: v1alpha1.{{ .Values.groupName }} 5 | labels: 6 | app: {{ include "infomaniak-webhook.name" . }} 7 | chart: {{ include "infomaniak-webhook.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | annotations: 11 | cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "infomaniak-webhook.servingCertificate" . }}" 12 | spec: 13 | group: {{ .Values.groupName }} 14 | groupPriorityMinimum: 1000 15 | versionPriority: 15 16 | service: 17 | name: {{ include "infomaniak-webhook.fullname" . }} 18 | namespace: {{ .Release.Namespace }} 19 | version: v1alpha1 20 | -------------------------------------------------------------------------------- /tilt/manifests.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: infomaniak-api-credentials 5 | namespace: cert-manager 6 | type: Opaque 7 | stringData: 8 | api-token: ~ 9 | --- 10 | apiVersion: cert-manager.io/v1 11 | kind: ClusterIssuer 12 | metadata: 13 | name: letsencrypt-staging 14 | spec: 15 | acme: 16 | email: ~ 17 | privateKeySecretRef: 18 | name: le-staging-account-key 19 | server: https://acme-staging-v02.api.letsencrypt.org/directory 20 | solvers: 21 | - selector: {} 22 | dns01: 23 | webhook: 24 | groupName: acme.infomaniak.com 25 | solverName: infomaniak 26 | config: 27 | apiTokenSecretRef: 28 | name: infomaniak-api-credentials 29 | key: api-token 30 | --- 31 | apiVersion: cert-manager.io/v1 32 | kind: Certificate 33 | metadata: 34 | name: test-certificate 35 | spec: 36 | secretName: test-certificate-tls 37 | issuerRef: 38 | name: letsencrypt-staging 39 | kind: ClusterIssuer 40 | dnsNames: 41 | - ~ 42 | -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/README.md: -------------------------------------------------------------------------------- 1 | # Infomaniak ACME Webhook - Helm Chart 2 | 3 | ## Flux Helm Release 4 | 5 | If you are using Flux to manage your configuration, a `GitRepository` can be used as a source for the `HelmRelease`. 6 | This also allows for customization of the chart values. 7 | 8 | ```yaml 9 | --- 10 | apiVersion: source.toolkit.fluxcd.io/v1 11 | kind: GitRepository 12 | metadata: 13 | name: infomaniak-webhook 14 | namespace: cert-manager 15 | spec: 16 | interval: 5m 17 | url: https://github.com/Infomaniak/cert-manager-webhook-infomaniak 18 | ref: 19 | branch: master 20 | ignore: | 21 | /* 22 | !/deploy/ 23 | --- 24 | apiVersion: helm.toolkit.fluxcd.io/v2beta2 25 | kind: HelmRelease 26 | metadata: 27 | name: infomaniak-webhook 28 | namespace: cert-manager 29 | spec: 30 | interval: 5m 31 | chart: 32 | spec: 33 | chart: deploy/infomaniak-webhook 34 | sourceRef: 35 | kind: GitRepository 36 | name: infomaniak-webhook 37 | values: 38 | groupName: example.com 39 | secretsNames: 40 | - infomaniak-api-credentials 41 | ``` 42 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: docker 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - 'v*' 9 | 10 | jobs: 11 | main: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v4 16 | 17 | - name: Docker meta 18 | id: meta 19 | uses: docker/metadata-action@v5 20 | with: 21 | images: ghcr.io/${{ github.repository }} 22 | 23 | - name: Set up QEMU 24 | uses: docker/setup-qemu-action@v3 25 | 26 | - name: Set up Docker Buildx 27 | uses: docker/setup-buildx-action@v3 28 | 29 | - name: Log in to GitHub Container Registry 30 | uses: docker/login-action@v3 31 | with: 32 | registry: ghcr.io 33 | username: ${{ github.actor }} 34 | password: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | - name: Build and push 37 | id: docker_build 38 | uses: docker/build-push-action@v5 39 | with: 40 | context: . 41 | platforms: linux/amd64,linux/arm64 42 | push: true 43 | tags: ${{ steps.meta.outputs.tags }} 44 | labels: ${{ steps.meta.outputs.labels }} 45 | 46 | - name: Image digest 47 | run: echo ${{ steps.docker_build.outputs.digest }} 48 | -------------------------------------------------------------------------------- /.github/workflows/manifest.yml: -------------------------------------------------------------------------------- 1 | name: manifest 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | name: Build & Upload Manifest 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | - name: Setup helm 17 | id: install 18 | uses: azure/setup-helm@v4 19 | 20 | - name: Build rendered-manifest.yaml 21 | run: | 22 | sudo apt-get update 23 | sudo apt-get install -y make 24 | make rendered-manifest.yaml 25 | 26 | - name: Create Release 27 | id: create_release 28 | uses: actions/create-release@v1 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | with: 32 | tag_name: ${{ github.ref }} 33 | release_name: Release ${{ github.ref }} 34 | draft: false 35 | prerelease: false 36 | 37 | - name: Upload rendered-manifest.yaml 38 | id: upload-release-asset 39 | uses: actions/upload-release-asset@v1 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | with: 43 | upload_url: ${{ steps.create_release.outputs.upload_url }} 44 | asset_path: ./_out/rendered-manifest.yaml 45 | asset_name: rendered-manifest.yaml 46 | asset_content_type: application/yaml 47 | -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "infomaniak-webhook.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "infomaniak-webhook.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "infomaniak-webhook.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{- define "infomaniak-webhook.selfSignedIssuer" -}} 35 | {{ printf "%s-selfsign" (include "infomaniak-webhook.fullname" .) }} 36 | {{- end -}} 37 | 38 | {{- define "infomaniak-webhook.rootCAIssuer" -}} 39 | {{ printf "%s-ca" (include "infomaniak-webhook.fullname" .) }} 40 | {{- end -}} 41 | 42 | {{- define "infomaniak-webhook.rootCACertificate" -}} 43 | {{ printf "%s-ca" (include "infomaniak-webhook.fullname" .) }} 44 | {{- end -}} 45 | 46 | {{- define "infomaniak-webhook.servingCertificate" -}} 47 | {{ printf "%s-webhook-tls" (include "infomaniak-webhook.fullname" .) }} 48 | {{- end -}} 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO ?= $(shell which go) 2 | OS ?= $(shell $(GO) env GOOS) 3 | ARCH ?= $(shell $(GO) env GOARCH) 4 | 5 | IMAGE_NAME ?= "ghcr.io/infomaniak/cert-manager-webhook-infomaniak" 6 | IMAGE_TAG ?= "latest" 7 | NAMESPACE ?= "cert-manager-infomaniak" 8 | 9 | OUT := $(shell pwd)/_out 10 | 11 | KUBEBUILDER_VERSION=2.3.1 12 | 13 | HELM_FILES := $(shell find deploy/infomaniak-webhook) 14 | 15 | test: _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/etcd _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kube-apiserver _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kubectl 16 | TEST_ASSET_ETCD=_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/etcd \ 17 | TEST_ASSET_KUBE_APISERVER=_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kube-apiserver \ 18 | TEST_ASSET_KUBECTL=_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kubectl \ 19 | $(GO) test -v . 20 | 21 | _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH).tar.gz: | _test 22 | curl -fsSL https://github.com/kubernetes-sigs/kubebuilder/releases/download/v$(KUBEBUILDER_VERSION)/kubebuilder_$(KUBEBUILDER_VERSION)_$(OS)_$(ARCH).tar.gz -o $@ 23 | 24 | _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/etcd _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kube-apiserver _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kubectl: _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH).tar.gz | _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH) 25 | tar xfO $< kubebuilder_$(KUBEBUILDER_VERSION)_$(OS)_$(ARCH)/bin/$(notdir $@) > $@ && chmod +x $@ 26 | 27 | .PHONY: clean 28 | clean: 29 | rm -rf _test $(OUT) apiserver.local.config testdata/infomaniak/*.json testdata/infomaniak/*.yaml 30 | 31 | .PHONY: build 32 | build: 33 | docker build -t "$(IMAGE_NAME):$(IMAGE_TAG)" . 34 | 35 | .PHONY: deploy 36 | deploy: rendered-manifest.yaml 37 | kubectl apply -f "$(OUT)/rendered-manifest.yaml" 38 | 39 | .PHONY: rendered-manifest.yaml 40 | rendered-manifest.yaml: $(OUT)/rendered-manifest.yaml 41 | 42 | $(OUT)/rendered-manifest.yaml: $(HELM_FILES) | $(OUT) 43 | helm template \ 44 | infomaniak-webhook \ 45 | --namespace $(NAMESPACE) \ 46 | --set image.repository=$(IMAGE_NAME) \ 47 | --set image.tag=$(IMAGE_TAG) \ 48 | --set createReleaseNamespace=true \ 49 | deploy/infomaniak-webhook > $@ 50 | 51 | _test $(OUT) _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH): 52 | mkdir -p $@ 53 | -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/values.yaml: -------------------------------------------------------------------------------- 1 | # The GroupName here is used to identify your company or business unit that 2 | # created this webhook. 3 | # For example, this may be "acme.mycompany.com". 4 | # This name will need to be referenced in each Issuer's `webhook` stanza to 5 | # inform cert-manager of where to send ChallengePayload resources in order to 6 | # solve the DNS01 challenge. 7 | # This group name should be **unique**, hence using your own company's domain 8 | # here is recommended. 9 | groupName: acme.infomaniak.com 10 | 11 | certManager: 12 | namespace: cert-manager 13 | serviceAccountName: cert-manager 14 | 15 | 16 | # Use these variables to configure the HTTP_PROXY environment variables. 17 | 18 | # Configures the HTTP_PROXY environment variable where a HTTP proxy is required. 19 | # +docs:property 20 | # http_proxy: "http://proxy:8080" 21 | 22 | # Configures the HTTPS_PROXY environment variable where a HTTP proxy is required. 23 | # +docs:property 24 | # https_proxy: "https://proxy:8080" 25 | 26 | # Configures the NO_PROXY environment variable where a HTTP proxy is required, 27 | # but certain domains should be excluded. 28 | # +docs:property 29 | # no_proxy: 127.0.0.1,localhost 30 | 31 | # used to add a namespace declaration to the static manifest, 32 | # prefer `--create-namespaces` with `helm install` >= 3.2 33 | # see https://github.com/helm/helm/issues/6794 34 | createReleaseNamespace: false 35 | 36 | replicaCount: 1 37 | 38 | image: 39 | repository: ghcr.io/infomaniak/cert-manager-webhook-infomaniak 40 | tag: latest 41 | pullPolicy: IfNotPresent 42 | 43 | imagePullSecret: [] 44 | 45 | nameOverride: "" 46 | fullnameOverride: "" 47 | 48 | secretsNames: 49 | - "infomaniak-api-credentials" 50 | 51 | service: 52 | type: ClusterIP 53 | port: 443 54 | 55 | debug: 56 | enabled: false 57 | port: 30000 58 | 59 | resources: {} 60 | # We usually recommend not to specify default resources and to leave this as a conscious 61 | # choice for the user. This also increases chances charts run on environments with little 62 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 63 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 64 | # limits: 65 | # cpu: 100m 66 | # memory: 128Mi 67 | # requests: 68 | # cpu: 100m 69 | # memory: 128Mi 70 | 71 | nodeSelector: {} 72 | 73 | tolerations: [] 74 | 75 | affinity: {} 76 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "os" 7 | "testing" 8 | "text/template" 9 | 10 | acmetest "github.com/cert-manager/cert-manager/test/acme" 11 | ) 12 | 13 | var ( 14 | testZoneName = os.Getenv("TEST_ZONE_NAME") 15 | manifestPath = "testdata/infomaniak" 16 | ) 17 | 18 | func createSecretFile() error { 19 | apiToken := os.Getenv("INFOMANIAK_TOKEN") 20 | if apiToken == "" { 21 | return errors.New("INFOMANIAK_TOKEN should be defined") 22 | } 23 | apiTokenBase64 := base64.StdEncoding.EncodeToString([]byte(apiToken)) 24 | 25 | secretTmpl := `--- 26 | apiVersion: v1 27 | kind: Secret 28 | metadata: 29 | name: infomaniak-api-credentials 30 | type: Opaque 31 | data: 32 | api-token: {{.}} 33 | ` 34 | secretFile, err := os.Create(manifestPath + "/api-key.yaml") 35 | if err != nil { 36 | return err 37 | } 38 | defer secretFile.Close() 39 | 40 | tmpl, err := template.New("api-key.yaml").Parse(secretTmpl) 41 | if err != nil { 42 | return err 43 | } 44 | err = tmpl.Execute(secretFile, apiTokenBase64) 45 | 46 | return nil 47 | } 48 | 49 | func createConfig() error { 50 | config := []byte(`{ 51 | "apiTokenSecretRef": { 52 | "name": "infomaniak-api-credentials", 53 | "key": "api-token" 54 | } 55 | } 56 | `) 57 | err := os.WriteFile(manifestPath+"/config.json", config, 0644) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return nil 63 | } 64 | 65 | func runTestSuite(t *testing.T, zone string) { 66 | // The manifest path should contain a file named config.json that is a 67 | // snippet of valid configuration that should be included on the 68 | // ChallengeRequest passed as part of the test cases. 69 | 70 | if len(zone) == 0 || zone == "api." { 71 | t.Fatal("Can't run tests on empty zone, please define TEST_ZONE_NAME") 72 | } 73 | 74 | // Create the secret file from INFOMANIAK_TOKEN env. variable 75 | if err := createSecretFile(); err != nil { 76 | t.Fatal(err) 77 | } 78 | 79 | // Create the config file from TEST_METHOD env. variable 80 | if err := createConfig(); err != nil { 81 | t.Fatal(err) 82 | } 83 | 84 | fixture := acmetest.NewFixture(&infomaniakDNSProviderSolver{}, 85 | acmetest.SetResolvedZone(zone), 86 | acmetest.SetAllowAmbientCredentials(false), 87 | acmetest.SetManifestPath(manifestPath), 88 | ) 89 | 90 | fixture.RunConformance(t) 91 | 92 | } 93 | 94 | func TestRunsSuiteIKAPI(t *testing.T) { 95 | runTestSuite(t, testZoneName) 96 | } 97 | 98 | func TestRunsSuiteIKAPISubdomain(t *testing.T) { 99 | runTestSuite(t, "api."+testZoneName) 100 | } 101 | -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/templates/pki.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Create a selfsigned Issuer, in order to create a root CA certificate for 3 | # signing webhook serving certificates 4 | apiVersion: cert-manager.io/v1 5 | kind: Issuer 6 | metadata: 7 | name: {{ include "infomaniak-webhook.selfSignedIssuer" . }} 8 | namespace: {{ .Release.Namespace | quote }} 9 | labels: 10 | app: {{ include "infomaniak-webhook.name" . }} 11 | chart: {{ include "infomaniak-webhook.chart" . }} 12 | release: {{ .Release.Name }} 13 | heritage: {{ .Release.Service }} 14 | spec: 15 | selfSigned: {} 16 | 17 | --- 18 | 19 | # Generate a CA Certificate used to sign certificates for the webhook 20 | apiVersion: cert-manager.io/v1 21 | kind: Certificate 22 | metadata: 23 | name: {{ include "infomaniak-webhook.rootCACertificate" . }} 24 | namespace: {{ .Release.Namespace | quote }} 25 | labels: 26 | app: {{ include "infomaniak-webhook.name" . }} 27 | chart: {{ include "infomaniak-webhook.chart" . }} 28 | release: {{ .Release.Name }} 29 | heritage: {{ .Release.Service }} 30 | spec: 31 | secretName: {{ include "infomaniak-webhook.rootCACertificate" . }} 32 | duration: 43800h0m0s # 5y 33 | issuerRef: 34 | name: {{ include "infomaniak-webhook.selfSignedIssuer" . }} 35 | commonName: "ca.infomaniak-webhook.cert-manager" 36 | isCA: true 37 | 38 | --- 39 | 40 | # Create an Issuer that uses the above generated CA certificate to issue certs 41 | apiVersion: cert-manager.io/v1 42 | kind: Issuer 43 | metadata: 44 | name: {{ include "infomaniak-webhook.rootCAIssuer" . }} 45 | namespace: {{ .Release.Namespace | quote }} 46 | labels: 47 | app: {{ include "infomaniak-webhook.name" . }} 48 | chart: {{ include "infomaniak-webhook.chart" . }} 49 | release: {{ .Release.Name }} 50 | heritage: {{ .Release.Service }} 51 | spec: 52 | ca: 53 | secretName: {{ include "infomaniak-webhook.rootCACertificate" . }} 54 | 55 | --- 56 | 57 | # Finally, generate a serving certificate for the webhook to use 58 | apiVersion: cert-manager.io/v1 59 | kind: Certificate 60 | metadata: 61 | name: {{ include "infomaniak-webhook.servingCertificate" . }} 62 | namespace: {{ .Release.Namespace | quote }} 63 | labels: 64 | app: {{ include "infomaniak-webhook.name" . }} 65 | chart: {{ include "infomaniak-webhook.chart" . }} 66 | release: {{ .Release.Name }} 67 | heritage: {{ .Release.Service }} 68 | spec: 69 | secretName: {{ include "infomaniak-webhook.servingCertificate" . }} 70 | duration: 8760h0m0s # 1y 71 | issuerRef: 72 | name: {{ include "infomaniak-webhook.rootCAIssuer" . }} 73 | dnsNames: 74 | - {{ include "infomaniak-webhook.fullname" . }} 75 | - {{ include "infomaniak-webhook.fullname" . }}.{{ .Release.Namespace }} 76 | - {{ include "infomaniak-webhook.fullname" . }}.{{ .Release.Namespace }}.svc 77 | -------------------------------------------------------------------------------- /deploy/infomaniak-webhook/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "infomaniak-webhook.fullname" . }} 5 | namespace: {{ .Release.Namespace | quote }} 6 | labels: 7 | app: {{ include "infomaniak-webhook.name" . }} 8 | chart: {{ include "infomaniak-webhook.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | spec: 12 | replicas: {{ .Values.replicaCount }} 13 | selector: 14 | matchLabels: 15 | app: {{ include "infomaniak-webhook.name" . }} 16 | release: {{ .Release.Name }} 17 | template: 18 | metadata: 19 | labels: 20 | app: {{ include "infomaniak-webhook.name" . }} 21 | release: {{ .Release.Name }} 22 | spec: 23 | serviceAccountName: {{ include "infomaniak-webhook.fullname" . }} 24 | {{- with .Values.imagePullSecrets }} 25 | imagePullSecrets: 26 | {{- toYaml . | nindent 8 }} 27 | {{- end }} 28 | containers: 29 | - name: {{ .Chart.Name }} 30 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 31 | imagePullPolicy: {{ .Values.image.pullPolicy }} 32 | {{- if .Values.debug.enabled }} 33 | command: 34 | - /start.sh 35 | args: 36 | - dlv 37 | - --listen=:{{ .Values.debug.port }} 38 | - --accept-multiclient 39 | - --api-version=2 40 | - --headless=true 41 | - exec 42 | - /usr/local/bin/webhook 43 | - -- 44 | {{- else }} 45 | args: 46 | {{- end }} 47 | - --v=2 48 | - --tls-cert-file=/tls/tls.crt 49 | - --tls-private-key-file=/tls/tls.key 50 | env: 51 | - name: GROUP_NAME 52 | value: {{ .Values.groupName | quote }} 53 | {{- with .Values.http_proxy }} 54 | - name: HTTP_PROXY 55 | value: {{ . }} 56 | {{- end }} 57 | {{- with .Values.https_proxy }} 58 | - name: HTTPS_PROXY 59 | value: {{ . }} 60 | {{- end }} 61 | {{- with .Values.no_proxy }} 62 | - name: NO_PROXY 63 | value: {{ . }} 64 | {{- end }} 65 | ports: 66 | - name: https 67 | containerPort: 443 68 | protocol: TCP 69 | {{- if not .Values.debug.enabled }} 70 | livenessProbe: 71 | httpGet: 72 | scheme: HTTPS 73 | path: /healthz 74 | port: https 75 | readinessProbe: 76 | httpGet: 77 | scheme: HTTPS 78 | path: /healthz 79 | port: https 80 | {{- end }} 81 | volumeMounts: 82 | - name: certs 83 | mountPath: /tls 84 | readOnly: true 85 | resources: 86 | {{- toYaml .Values.resources | nindent 12 }} 87 | volumes: 88 | - name: certs 89 | secret: 90 | secretName: {{ include "infomaniak-webhook.servingCertificate" . }} 91 | {{- with .Values.nodeSelector }} 92 | nodeSelector: 93 | {{ toYaml . | nindent 8 }} 94 | {{- end }} 95 | {{- with .Values.affinity }} 96 | affinity: 97 | {{ toYaml . | nindent 8 }} 98 | {{- end }} 99 | {{- with .Values.tolerations }} 100 | tolerations: 101 | {{ toYaml . | nindent 8 }} 102 | {{- end }} 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Infomaniak ACME webhook 2 | 3 | A cert-manager webhook which works with domains handled by [Infomaniak](https://www.infomaniak.com), a 🇨🇭 Swiss hosting provider 4 | 5 | ## Quick start 6 | 7 | 1. Deploy cert-manager (if needed) 8 | ``` 9 | $ kubectl apply --validate=false -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml 10 | ``` 11 | 12 | 1. Deploy Infomaniak webhook 13 | ``` 14 | $ kubectl apply -f https://github.com/infomaniak/cert-manager-webhook-infomaniak/releases/download/v0.2.0/rendered-manifest.yaml 15 | ``` 16 | 17 | 1. Create a Secret with your Infomaniak API token. You can generate a new one by [clicking here](https://manager.infomaniak.com/v3/infomaniak-api). You need to have at least the `Domain` scope. 18 | ``` 19 | $ cat <> 45 | EOF 46 | ``` 47 | 48 | 49 | 1. Create a staging ClusterIssuer 50 | ``` 51 | $ cat <