Skip to content

Commit

Permalink
Initial commit for use with Linode API
Browse files Browse the repository at this point in the history
  • Loading branch information
slicen committed Jun 23, 2020
1 parent bcb3c75 commit e51694c
Show file tree
Hide file tree
Showing 28 changed files with 1,103 additions and 314 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

# Test binary, build with `go test -c`
*.test
/_out/

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Ignore the built binary
cert-manager-webhook-example
./cert-manager-webhook-linode
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.12.4-alpine AS build_deps
FROM golang:1.14-alpine AS build_deps

RUN apk add --no-cache git

Expand All @@ -16,7 +16,7 @@ COPY . .

RUN CGO_ENABLED=0 go build -o webhook -ldflags '-w -extldflags "-static"' .

FROM alpine:3.9
FROM alpine:3.12

RUN apk add --no-cache ca-certificates

Expand Down
19 changes: 13 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
IMAGE_NAME := "webhook"
IMAGE_TAG := "latest"
IMAGE_NAME := "slicen/cert-manager-webhook-linode"
IMAGE_TAG := "v0.1.0"

OUT := $(shell pwd)/_out

$(shell mkdir -p "$(OUT)")

.DEFAULT_GOAL := build

.PHONY: verify test build clean rendered-manifest.yaml

verify:
go test -v .

test: verify

build:
docker build -t "$(IMAGE_NAME):$(IMAGE_TAG)" .
docker build --rm -t "$(IMAGE_NAME):$(IMAGE_TAG)" -t "$(IMAGE_NAME):latest" .

clean:
rm -r "$(OUT)"

.PHONY: rendered-manifest.yaml
rendered-manifest.yaml:
helm template \
--name example-webhook \
--set image.repository=$(IMAGE_NAME) \
--set image.tag=$(IMAGE_TAG) \
deploy/example-webhook > "$(OUT)/rendered-manifest.yaml"
deploy/cert-manager-webhook-linode > "$(OUT)/rendered-manifest.yaml"
120 changes: 84 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,102 @@
# ACME webhook example
# Cert-Manager ACME DNS01 Webhook Solver for Linode DNS Manager

The ACME issuer type supports an optional 'webhook' solver, which can be used
to implement custom DNS01 challenge solving logic.
[![Go Report Card](https://goreportcard.com/badge/github.com/slicen/cert-manager-webhook-linode)](https://goreportcard.com/report/github.com/slicen/cert-manager-webhook-linode)
[![Releases](https://img.shields.io/github/v/release/slicen/cert-manager-webhook-linode?include_prereleases)](https://github.com/slicen/cert-manager-webhook-linode/releases)
[![LICENSE](https://img.shields.io/github/license/slicen/cert-manager-webhook-linode)](https://github.com/slicen/cert-manager-webhook-linode/blob/master/LICENSE)

This is useful if you need to use cert-manager with a DNS provider that is not
officially supported in cert-manager core.
A webhook to use [Linode DNS
Manager](https://www.linode.com/docs/platform/manager/dns-manager) as a DNS01
ACME Issuer for [cert-manager](https://github.com/jetstack/cert-manager).

## Why not in core?
## Installation

As the project & adoption has grown, there has been an influx of DNS provider
pull requests to our core codebase. As this number has grown, the test matrix
has become un-maintainable and so, it's not possible for us to certify that
providers work to a sufficient level.

By creating this 'interface' between cert-manager and DNS providers, we allow
users to quickly iterate and test out new integrations, and then packaging
those up themselves as 'extensions' to cert-manager.
```bash
helm install cert-manager-webhook-linode \
--namespace cert-manager \
https://github.com/slicen/cert-manager-webhook-linode/releases/download/v0.1.0/cert-manager-webhook-linode-v0.1.0.tgz
```

We can also then provide a standardised 'testing framework', or set of
conformance tests, which allow us to validate the a DNS provider works as
expected.
## Usage

## Creating your own webhook
### Create Linode API Token Secret

Webhook's themselves are deployed as Kubernetes API services, in order to allow
administrators to restrict access to webhooks with Kubernetes RBAC.
```bash
kubectl create secret generic linode-credentials \
--namespace=cert-manager \
--from-literal=token=<LINODE TOKEN>
```

This is important, as otherwise it'd be possible for anyone with access to your
webhook to complete ACME challenge validations and obtain certificates.
### Create Issuer

#### Cluster-wide Linode API Token

```yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- dns01:
webhook:
solverName: linode
groupName: acme.slicen.me
```
To make the set up of these webhook's easier, we provide a template repository
that can be used to get started quickly.
By default, the Linode API token used will be obtained from the
linode-credentials Secret in the same namespace as the webhook.
#### Per Namespace Linode API Tokens
If you would prefer to use separate Linode API tokens for each namespace (e.g.
in a multi-tenant environment):
```yaml
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt-staging
namespace: default
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- dns01:
webhook:
solverName: linode
groupName: acme.slicen.me
config:
apiKeySecretRef:
name: linode-credentials
key: token
```
### Creating your own repository
## Development
### Running the test suite
All DNS providers **must** run the DNS01 provider conformance testing suite,
else they will have undetermined behaviour when used with cert-manager.
Conformance testing is achieved through Kubernetes emulation via the
kubebuilder-tools suite, in conjunction with real calls to the Linode API on an
test domain, using a valid API token.
**It is essential that you configure and run the test suite when creating a
DNS01 webhook.**
The test configures a cert-manager-dns01-tests TXT entry, attempts to verify its
presence, and removes the entry, thereby verifying the Prepare and CleanUp
functions.
An example Go test file has been provided in [main_test.go]().

You can run the test suite with:
Run the test suite with:
```bash
$ TEST_ZONE_NAME=example.com go test .
./scripts/fetch-test-binaries.sh
export LINODE_TOKEN=$(echo -n "<your API token>" | base64 -w 0)
envsubst < testdata/linode/secret.yaml.example > testdata/linode/secret.yaml
TEST_ZONE_NAME=yourdomain.com. make verify
```

The example file has a number of areas you must fill in and replace with your
own options in order for tests to pass.
File renamed without changes.
5 changes: 5 additions & 0 deletions deploy/cert-manager-webhook-linode/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v2
appVersion: "v0.1.0"
description: A Helm chart for cert-manager-webhook-linode
name: cert-manager-webhook-linode
version: v0.1.0
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "example-webhook.name" -}}
{{- define "cert-manager-webhook-linode.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

Expand All @@ -11,7 +11,7 @@ Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "example-webhook.fullname" -}}
{{- define "cert-manager-webhook-linode.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
Expand All @@ -27,22 +27,22 @@ If release name contains chart name it will be used as a full name.
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "example-webhook.chart" -}}
{{- define "cert-manager-webhook-linode.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{- define "example-webhook.selfSignedIssuer" -}}
{{ printf "%s-selfsign" (include "example-webhook.fullname" .) }}
{{- define "cert-manager-webhook-linode.selfSignedIssuer" -}}
{{ printf "%s-selfsign" (include "cert-manager-webhook-linode.fullname" .) }}
{{- end -}}

{{- define "example-webhook.rootCAIssuer" -}}
{{ printf "%s-ca" (include "example-webhook.fullname" .) }}
{{- define "cert-manager-webhook-linode.rootCAIssuer" -}}
{{ printf "%s-ca" (include "cert-manager-webhook-linode.fullname" .) }}
{{- end -}}

{{- define "example-webhook.rootCACertificate" -}}
{{ printf "%s-ca" (include "example-webhook.fullname" .) }}
{{- define "cert-manager-webhook-linode.rootCACertificate" -}}
{{ printf "%s-ca" (include "cert-manager-webhook-linode.fullname" .) }}
{{- end -}}

{{- define "example-webhook.servingCertificate" -}}
{{ printf "%s-webhook-tls" (include "example-webhook.fullname" .) }}
{{- define "cert-manager-webhook-linode.servingCertificate" -}}
{{ printf "%s-webhook-tls" (include "cert-manager-webhook-linode.fullname" .) }}
{{- end -}}
19 changes: 19 additions & 0 deletions deploy/cert-manager-webhook-linode/templates/apiservice.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
name: v1alpha1.{{ .Values.api.groupName }}
labels:
app: {{ include "cert-manager-webhook-linode.name" . }}
chart: {{ include "cert-manager-webhook-linode.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "cert-manager-webhook-linode.servingCertificate" . }}"
spec:
group: {{ .Values.api.groupName }}
groupPriorityMinimum: 1000
versionPriority: 15
service:
name: {{ include "cert-manager-webhook-linode.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
version: v1alpha1
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
apiVersion: apps/v1beta2
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "example-webhook.fullname" . }}
name: {{ include "cert-manager-webhook-linode.fullname" . }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-webhook-linode.name" . }}
chart: {{ include "cert-manager-webhook-linode.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ include "example-webhook.name" . }}
app: {{ include "cert-manager-webhook-linode.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ include "example-webhook.name" . }}
app: {{ include "cert-manager-webhook-linode.name" . }}
release: {{ .Release.Name }}
spec:
serviceAccountName: {{ include "example-webhook.fullname" . }}
serviceAccountName: {{ include "cert-manager-webhook-linode.fullname" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
- --tls-cert-file=/tls/tls.crt
- --tls-private-key-file=/tls/tls.key
{{- if .Values.deployment.logLevel }}
- --v={{ .Values.deployment.logLevel }}
{{- end }}
env:
- name: GROUP_NAME
value: {{ .Values.groupName | quote }}
value: {{ .Values.api.groupName | quote }}
- name: POD_SECRET_NAME
value: {{ .Values.deployment.secretName | quote }}
- name: POD_SECRET_KEY
value: {{ .Values.deployment.secretKey | quote }}
ports:
- name: https
containerPort: 443
Expand All @@ -53,7 +60,7 @@ spec:
volumes:
- name: certs
secret:
secretName: {{ include "example-webhook.servingCertificate" . }}
secretName: {{ include "cert-manager-webhook-linode.servingCertificate" . }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
Expand Down
Loading

0 comments on commit e51694c

Please sign in to comment.