Skip to content

Commit

Permalink
add letsencrypt ClusterIssuers + Hetzner webhook + reflector + auto w…
Browse files Browse the repository at this point in the history
…idlcard cert generation
  • Loading branch information
jaumebarber committed Apr 29, 2024
1 parent 5583100 commit 4058a6d
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 5 deletions.
51 changes: 47 additions & 4 deletions init.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ resource "hcloud_load_balancer" "cluster" {
}
}


resource "null_resource" "first_control_plane" {
connection {
user = "root"
Expand Down Expand Up @@ -255,6 +254,43 @@ resource "null_resource" "kustomization" {
destination = "/var/post_install/cert_manager.yaml"
}

# Upload the ClusterIssuers config
provisioner "file" {
content = templatefile(
"${path.module}/templates/cluster_issuers.yaml.tpl",
{
base_domain = var.base_domain
common_name = regex("[^.]+\\.[^.]+$", var.base_domain)
cert_manager_email = var.cert_manager_email
ingress_controller = var.ingress_controller
hetzner_dns_api_key = base64encode(var.hetzner_dns_api_key)
target_namespace = local.ingress_controller_namespace
})
destination = "/var/post_install/cluster_issuers.yaml"
}

# Upload the Reflector config
provisioner "file" {
content = templatefile(
"${path.module}/templates/reflector.yaml.tpl",
{
values = indent(4, trimspace(var.reflector_values))
})
destination = "/var/post_install/reflector.yaml"
}

# Upload the wildcard cert secret
provisioner "file" {
content = templatefile(
"${path.module}/templates/wildcard_cert.yaml.tpl",
{
common_name = regex("[^.]+\\.[^.]+$", var.base_domain)
reflector_enabled = var.enable_reflector
hetzner_dns_api_key = var.hetzner_dns_api_key
})
destination = "/var/post_install/wildcard_cert.yaml"
}

# Upload the Rancher config
provisioner "file" {
content = templatefile(
Expand Down Expand Up @@ -311,9 +347,7 @@ resource "null_resource" "kustomization" {
done
EOF
EOT
]
,

],
[
# Ready, set, go for the kustomization
"kubectl apply -k /var/post_install",
Expand All @@ -322,6 +356,15 @@ resource "null_resource" "kustomization" {
"sleep 7", # important as the system upgrade controller CRDs sometimes don't get ready right away, especially with Cilium.
"kubectl -n system-upgrade apply -f /var/post_install/plans.yaml"
],
var.enable_cluster_issuers && var.enable_cert_manager ? [
"echo 'Waiting for the cert-manager to become available...'",
"curl -fsSL -o cmctl.tar.gz https://github.com/cert-manager/cert-manager/releases/download/v1.9.2/cmctl-linux-arm.tar.gz && tar xzf cmctl.tar.gz && mv cmctl /usr/local/bin", # install cmctl util to check for cert-manager api readiness
"cmctl check api --wait=2m --kubeconfig /etc/rancher/k3s/k3s.yaml 2> /dev/null",
"kubectl -n cert-manager apply -f /var/post_install/cluster_issuers.yaml",
"echo 'Waiting for the cert-manager webhooks to become available...'",
"cmctl check api --wait=2m --kubeconfig /etc/rancher/k3s/k3s.yaml 2> /dev/null",
"kubectl -n cert-manager apply -f /var/post_install/wildcard_cert.yaml",
] : [],
local.has_external_load_balancer ? [] : [
<<-EOT
timeout 360 bash <<EOF
Expand Down
13 changes: 13 additions & 0 deletions kube.tf.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ module "kube-hetzner" {
hcloud = hcloud
}
hcloud_token = var.hcloud_token != "" ? var.hcloud_token : local.hcloud_token
# This is only for Hetzner delegated DNS Zones and it is a requirement for those who want to obtain a LetsEncrypt TLS wildcard certificate
# using the Hetzner webhook to solve a DNS Challenge. See through https://docs.hetzner.com/dns-console/dns for more info on how to delegate
# your DNS Zone and check templates/cluster_issuers.yaml.tpl for webhook installation. Once you've delegated your DNS Zone, go to your Hetzner
# Control Panel, select DNS, go to API Tokens on the User Menu and create a token to use here.
# hetzner_dns_api_key = var.hetzner_dns_api_key != "" ? var.hetzner_dns_api_key : local.hetzner_dns_api_key

# Then fill or edit the below values. Only the first values starting with a * are obligatory; the rest can remain with their default values, or you
# could adapt them to your needs.
Expand Down Expand Up @@ -714,6 +719,14 @@ module "kube-hetzner" {
# You can enable cert-manager (installed by Helm behind the scenes) with the following flag, the default is "true".
# enable_cert_manager = false

# You can enable LetsEncrypt ClusterIssuers (installed by Helm behind the scenes) to obtain FQDN domains or wildcard domains (requires Hetzner DNS API Token for wildcards read carefully above) with the following flag, the default is "false".
# enable_cluster_issuers = true
# LetsEncrypt email for cert-manager certs renewal updates
# cert_manager_email = "[email protected]"
# Reflector copies Secrets or ConfigMaps so they are available for all Namespaces. Default is "false".
# enable_reflector = true
# reflector_values = ""

# IP Addresses to use for the DNS Servers, the defaults are the ones provided by Hetzner https://docs.hetzner.com/dns-console/dns/general/recursive-name-servers/.
# The number of different DNS servers is limited to 3 by Kubernetes itself.
# It's always a good idea to have at least 1 IPv4 and 1 IPv6 DNS server for robustness.
Expand Down
3 changes: 2 additions & 1 deletion locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ locals {
var.enable_csi_driver_smb ? ["csi-driver-smb.yaml"] : [],
var.enable_cert_manager || var.enable_rancher ? ["cert_manager.yaml"] : [],
var.enable_rancher ? ["rancher.yaml"] : [],
var.rancher_registration_manifest_url != "" ? [var.rancher_registration_manifest_url] : []
var.rancher_registration_manifest_url != "" ? [var.rancher_registration_manifest_url] : [],
var.enable_reflector ? ["reflector.yaml"] : [],
),
patches = [
{
Expand Down
73 changes: 73 additions & 0 deletions templates/cluster_issuers.yaml.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

%{if hetzner_dns_api_key != ""~}
apiVersion: v1
kind: Secret
metadata:
name: hetzner-dns-secret
namespace: cert-manager
type: Opaque
data:
api-key: ${hetzner_dns_api_key}
---
%{endif~}
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: cert-manager-webhook-hetzner
namespace: cert-manager
spec:
chart: cert-manager-webhook-hetzner
version: 1.3.1
repo: https://vadimkim.github.io/cert-manager-webhook-hetzner
valuesContent: |-
groupName: ${base_domain}
secretName:
- hetzner-dns-secret
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
namespace: cert-manager
spec:
acme:
email: ${cert_manager_email}
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
ingressClassName: ${ingress_controller}
- dns01:
webhook:
groupName: ${base_domain}
solverName: hetzner
config:
secretName: hetzner-dns-secret
zoneName: ${common_name}
apiUrl: https://dns.hetzner.com/api/v1
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
namespace: cert-manager
spec:
acme:
email: ${cert_manager_email}
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
ingressClassName: ${ingress_controller}
- dns01:
webhook:
groupName: ${base_domain}
solverName: hetzner
config:
secretName: hetzner-dns-secret
zoneName: ${common_name}
apiUrl: https://dns.hetzner.com/api/v1
10 changes: 10 additions & 0 deletions templates/reflector.yaml.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: emberstack
namespace: kube-system
spec:
chart: reflector
repo: https://emberstack.github.io/helm-charts
valuesContent: |-
${values}
23 changes: 23 additions & 0 deletions templates/wildcard_cert.yaml.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
%{if hetzner_dns_api_key != ""~}
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-domain
namespace: cert-manager
spec:
commonName: "*.${common_name}"
dnsNames:
- "*.${common_name}"
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
secretName: wildcard-domain-secret
%{if reflector_enabled}
secretTemplate:
annotations:
reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: ""
reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
reflector.v1.k8s.emberstack.com/reflection-auto-namespaces: ""
%{endif}
%{endif~}
30 changes: 30 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ variable "hcloud_token" {
type = string
sensitive = true
}
variable "hetzner_dns_api_key" {
description = "Hetzner DNS Zone API Key."
type = string
sensitive = true
default = ""
}

variable "k3s_token" {
description = "k3s master token (must match when restoring a cluster)."
Expand Down Expand Up @@ -718,12 +724,36 @@ variable "enable_cert_manager" {
description = "Enable cert manager."
}

variable "cert_manager_email" {
type = string
default = "[email protected]"
description = "Cert Manager Letsencrypt email"
}

variable "cert_manager_values" {
type = string
default = ""
description = "Additional helm values file to pass to Cert-Manager as 'valuesContent' at the HelmChart."
}

variable "enable_cluster_issuers" {
type = bool
default = false
description = "Enable LetsEncrypt ClusterIssuers."
}

variable "enable_reflector" {
type = bool
default = false
description = "Enable Reflector."
}

variable "reflector_values" {
type = string
default = ""
description = "Additional helm values file to pass to Reflector as 'valuesContent' at the HelmChart."
}

variable "enable_rancher" {
type = bool
default = false
Expand Down

0 comments on commit 4058a6d

Please sign in to comment.