akyriako / cert-manager-webhook-opentelekomcloud

ACME DNS01 solver webhook for Open Telekom Cloud DNS
Apache License 2.0
3 stars 1 forks source link
acme cert-manager cert-manager-webhook cert-manager-webhook-opentelekomcloud dns golang kubernetes lets-encrypt letsencrypt opentelekomcloud t-systems webhook

ACME webhook for Open Telekom Cloud DNS

Cert-manager DNS providers are integrations with various DNS (Domain Name System) service providers that allow cert-manager, a Kubernetes add-on, to automate the management of SSL/TLS certificates. DNS providers enable cert-manager to automatically perform challenges to prove domain ownership and obtain certificates from certificate authorities like Let's Encrypt.

By configuring cert-manager with the compatible Open Telekom Cloud DNS provider, using this webhook, you can set up automatic certificate issuance and renewal for your Open Telekom Cloud CCE workloads without manual intervention. This automation is crucial for securing web applications and services deployed on CCE clusters.

Installation

This webhook is installed exclusively via Helm.

[!NOTE] If you dont't have a Kubernetes cluster in place, this project comes with "batteries included"; a Dev Container (a .devcontainer.json specifications file that can be found in the repo and will be discussed in a later chapter) will instruct any IDE that supports Dev Containers, to set up an isolated containerized Kubernetes environment for you along with all necessary tooling (cert-manager, Helm etc.)

Configuration

Configure the Chart by setting the following parameters:

[!NOTE] The remaining chart variables are, besides self-explanatory, the same that used already by cert-manager/webhook-example

One-step

If opentelekomcloud.accessKey and opentelekomcloud.secretKey are both set, the chart will automatically:

helm repo add cmw-otc https://akyriako.github.io/cert-manager-webhook-opentelekomcloud/
helm repo update

helm upgrade --install $CHART_RELEASE_NAME cmw-otc/cert-manager-webhook-opentelekomcloud \
  --set opentelekomcloud.accessKey=$OS_ACCESS_KEY \
  --set opentelekomcloud.secretKey=$OS_SECRET_KEY \
  --namespace cert-manager

If additionally debug parameter is set to true, --set debug=true, the helm chart will add to the deployment of the container an additional argument to increase logging verbosity:

      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 eq .Values.debug true }}
            - --v=4
            {{- end }}

or you can alternatively override the values.yaml and set there the parameters.

Two-steps

If for any reason the one-step installation is not fit for your deployment pipeline, you can split the installation in two steps:

First create and deploy a Secret manifest, that would match the name of credentialsSecretRef value:

apiVersion: v1
kind: Secret
metadata:
  name: cert-manager-webhook-opentelekomcloud-creds
  namespace: cert-manager
type: Opaque
data:
  accessKey: "<ACCESS_KEY_in_Base64>"
  secretKey: "<SECRET_KEY_in_Base64>"

Deploy the secret with kubectl

and then deploy the webhook:

helm repo add cmw-otc https://akyriako.github.io/cert-manager-webhook-opentelekomcloud/
helm repo update

helm upgrade --install $CHART_RELEASE_NAME cmw-otc/cert-manager-webhook-opentelekomcloud \
  --namespace cert-manager

Usage

Issuers & ClusterIssuers

Issuers, and ClusterIssuers, are Kubernetes resources that represent certificate authorities (CAs) that are able to generate signed certificates by honoring certificate signing requests. All cert-manager certificates require a referenced issuer that is in a ready condition to attempt to honor the request. The former is namespaced-scoped while the latter is cluster-wide.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: opentelekomcloud-letsencrypt-staging
  namespace: cert-manager
spec:
  acme:
    email: user@example.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: opentelekomcloud-letsencrypt-staging-tls-key
    solvers:
    - dns01:
        webhook:
          groupName: acme.opentelekomcloud.com
          solverName: opentelekomcloud
          config:
            region: "eu-de"
            accessKeySecretRef:
              name: cert-manager-webhook-opentelekomcloud-creds
              key: accessKey
            secretKeySecretRef:
              name: cert-manager-webhook-opentelekomcloud-creds
              key: secretKey

Deploy the manifest above with kubectl.

Certificate

In cert-manager, the Certificate resource represents a human readable definition of a certificate request. cert-manager uses this input to generate a private key and CertificateRequest resource in order to obtain a signed certificate from an Issuer or ClusterIssuer. The signed certificate and private key are then stored in the specified Secret resource. cert-manager will ensure that the certificate is auto-renewed before it expires and re-issued if requested.

[!CAUTION] In order to issue any certificates, you'll need to configure an Issuer or ClusterIssuer resource first. Nevertheless, at the time of this writing, cert-manager webhooks seems to have a known bug working with Issuers in this scenario and you are strongly recommended to create a ClusterIssuer instead, until it is officially solved.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: certificate-subdomain-example-com
  namespace: cert-manager
spec:
  dnsNames:
  - '*.subdomain.example.com'
  issuerRef:
    kind: ClusterIssuer
    name: opentelekomcloud-letsencrypt-staging
  secretName: certificate-subdomain-example-com-tls

Deploy the manifest above with kubectl.

Development

As mentioned already in the preface, this project is harnessing the versatilly of Dev Containers to provide to you, on any IDE that supports Dev Containers, an isolated containerized Kubernetes environment along with all necessary tooling (Cert-Manager, Helm, K9s, Dive etc.) so you can start directly developing without wasting time and energy on how to jumpstart yet another project's environment and dependencies.

Dev Container

[!NOTE] Although you can use any IDE that supports Dev Containers, the extensions and features added on the base image are tailored for Visual Studio Code.

Extensions & Features

A Dev Container will be created, with all the necessary prerequisites to get you started developing immediately. A container, based on mcr.microsoft.com/devcontainers/go:1.21-bullseye will be spawned with the following features pre-installed:

A postCreateCommand (.devcontainer/setup.sh) will provision:

Installation

In order to test the changes on a Kubernetes cluster, you need to build a new image, push the image to the container registry of your choice and recreate the manifests that Helm will deploy to the Kubernetes cluster:

For building new image, execute:

make docker-build

For pushing the new image to a container registry, execute:

make docker-push

For creating the manifests out of the helm template, execute:

make rendered-manifest.yaml

[!NOTE] Before executing the above target, you have to make sure that you have set the values of the following environment variables: OS_ACCESS_KEY and OS_SECRET_KEY.

The last one will create a yaml file that will contain all required manifests, rendered-manifest.yaml, in folder _out: You can then deploy them in your Kubernetes cluster using kubectl:

kubectl apply -f _out/rendered-manifest.yaml

Extend

Webhook Configuration

If you need to extend the webhook configuration via environment variables, you should extend struct config which can be found in main.go:

type config struct {
    GroupName string `env:"GROUP_NAME" envDefault:"acme.opentelekomcloud.com"`
    Debug     bool   `env:"OS_DEBUG" envDefault:"false"`
}

[!CAUTION] No sensitive information (either in plain or encoded text) should be added here for any reason.

Consequently, you might need to change the chart template values so they acknowledge and use the new parameters in the manifests.

DNS Solver Configuration

If you need to extend the configuration of the solver, extending its API Specs, you should extend struct OpenTelekomCloudDnsProviderConfig which can be found in config.go:

type OpenTelekomCloudDnsProviderConfig struct {
    // These fields will be set by users in the
    // `issuer.spec.acme.dns01.providers.webhook.config` field.
    Region             string                    `json:"region,required"`
    AccessKeySecretRef *corev1.SecretKeySelector `json:"accessKeySecretRef,omitempty"`
    SecretKeySecretRef *corev1.SecretKeySelector `json:"secretKeySecretRef,omitempty"`
}

[!CAUTION] No sensitive information (either in plain or encoded text) should be added here for any reason.

Consequently, you might need to change the chart template values so they acknowledge and use the new parameters in the manifests.

Secrets

If you need to extend the secrets & credentials of the solver, you should extend struct OpenTelekomCloudDnsProviderSecrets which can be found in config.go:

type OpenTelekomCloudDnsProviderSecrets struct {
    AccessKey string `env:"OS_ACCESS_KEY,required"`
    SecretKey string `env:"OS_SECRET_KEY,required"`
}

Consequently, you might need to change the chart template values so they acknowledge and use the new parameters in the manifests.

[!TIP] Access & Secret keys are enough to create an Open Telekom Cloud Provider Client and a DNS Service Client. User, Password, Domain or Tenant identifiers are not needed for the DNS Solver to work.

Conformance Testing

All DNS providers must run the DNS01 provider conformance testing suite, else they will have undetermined behaviour when used with cert-manager.

$ OS_DEBUG=true OS_ACCESS_KEY={AccessKey} OS_SECRET_KEY={SecretKey} TEST_ZONE_NAME=example.com. make test

[!NOTE] Fill in the values of OS_ACCESS_KEY and OS_SECRET_KEY. Replace example.com. with your own (sub)domain. Make sure not to forget the trailing . in the TEST_ZONE_NAME value. You can omit any variable already defined in your session's environment variables.