Skip to content

Instantly share code, notes, and snippets.

@mbround18
Created September 26, 2024 22:30
Show Gist options
  • Save mbround18/ade481693d20aec3e978897856ba1ea9 to your computer and use it in GitHub Desktop.
Save mbround18/ade481693d20aec3e978897856ba1ea9 to your computer and use it in GitHub Desktop.
Traefik v3 cert roller

Traefik v3 Certificate Creation Script

Overview

This script helps users resolve "certificate not found" errors when upgrading to Traefik v3 by automating the process of creating Kubernetes TLS secrets for your certificates.

Prerequisites

Before using this script, ensure that:

  1. kubectl is installed and can connect to your Kubernetes cluster.
  2. You have administrative permissions to create TLS secrets in your Kubernetes cluster.

Usage

  1. Download the Script:
    Download the create-certs.ps1 PowerShell script from this repository to your local machine.

  2. Update Resolver: Change the $certResolverName = "cloudflare" to match the resolve you use for certificates.

  3. Run the Script:
    Open a PowerShell terminal and execute the script using the following command:

    .\create-certs.ps1

Troubleshooting

Common Issues

  • kubectl connection issues:
    If the script cannot connect to your cluster, ensure that kubectl is properly configured and pointing to the correct cluster context.

  • Permissions:
    Ensure you have appropriate permissions in the Kubernetes namespace where the secrets are being created.

$certResolverName = "cloudflare"
# Function to generate self-signed certificates using OpenSSL
function Generate-SelfSignedCert {
param (
[string]$certPath,
[string]$keyPath,
[string]$commonName
)
# Generate a private key and certificate using OpenSSL
& openssl req -x509 -newkey rsa:8192 -keyout $keyPath -out $certPath -days 365 -nodes -subj "/CN=$commonName"
}
# Path to store temporary certificate and key files
$tempDir = [System.IO.Path]::GetTempPath()
$certFile = "$tempDir\selfsigned.crt"
$keyFile = "$tempDir\selfsigned.key"
# Iterate through all namespaces
$namespaces = kubectl get namespaces -o jsonpath='{.items[*].metadata.name}' | Out-String
$namespaces = $namespaces.Trim() -split '\s+'
foreach ($namespace in $namespaces) {
Write-Host "Processing namespace: $namespace"
# Get all ingresses in the namespace
$ingressesJson = kubectl get ingress -n $namespace -o json
if (-not $ingressesJson) {
continue
}
$ingresses = $ingressesJson | ConvertFrom-Json
foreach ($ingress in $ingresses.items) {
$ingressName = $ingress.metadata.name
Write-Host "Processing ingress: $ingressName"
$commonName = $null # Initialize $commonName as null
# Check the annotation for the certresolver
if ($ingress.metadata.annotations.'traefik.ingress.kubernetes.io/router.tls.certresolver' -ne $certResolverName) {
Write-Host "Updating annotation 'traefik.ingress.kubernetes.io/router.tls.certresolver' to '$certResolverName'"
kubectl -n $namespace annotate ingress --all --overwrite "traefik.ingress.kubernetes.io/router.tls.certresolver=$certResolverName"
}
$ingressHost = $null
# Access TLS hosts, check if they exist, and use index for array access
if ($ingress.spec.tls -and $ingress.spec.tls[0].hosts -and $ingress.spec.tls[0].hosts[0]) {
$ingressHost = $ingress.spec.tls[0].hosts[0]
}
# If TLS host doesn't exist, fall back to rules host
if (-not $ingressHost -and $ingress.spec.rules -and $ingress.spec.rules[0].host) {
$ingressHost = $ingress.spec.rules[0].host
}
# Check if ingressHost is populated and set $commonName accordingly
if (-not [string]::IsNullOrEmpty($ingressHost)) {
$commonName = $ingressHost
Write-Host "Using TLS host: $commonName"
}
# If $commonName is still null, fall back to rules host
if (-not $commonName) {
if ($ingress.spec.rules -and $ingress.spec.rules[0].host) {
$ruleHost = $ingress.spec.rules[0].host
if (-not [string]::IsNullOrEmpty($ruleHost)) {
$commonName = $ruleHost
Write-Host "Using rule host: $commonName"
}
}
}
# Continue if no valid host was found
if (-not $commonName) {
Write-Host "No valid host found for ingress: $ingressName. Skipping."
continue
}
foreach ($tls in $ingress.spec.tls) {
$secretName = $tls.secretName
if (-not [string]::IsNullOrEmpty($secretName)) {
Write-Host "Found TLS secret name: $secretName"
# Generate a self-signed certificate and key
Write-Host "Generating self-signed certificate for CN: $commonName"
Generate-SelfSignedCert -certPath $certFile -keyPath $keyFile -commonName $commonName
# Convert the generated certificate and key to base64
$base64Cert = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes($certFile))
$base64Key = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes($keyFile))
# Delete the existing secret if it exists
$secretExists = kubectl get secret $secretName -n $namespace --ignore-not-found
if ($secretExists) {
Write-Host "Deleting existing secret: $secretName in namespace $namespace"
kubectl delete secret $secretName -n $namespace
}
# Recreate the TLS secret with the self-signed certificate and key
Write-Host "Recreating TLS secret: $secretName in namespace $namespace with self-signed cert"
$secretYaml = @"
apiVersion: v1
kind: Secret
metadata:
name: $secretName
namespace: $namespace
type: kubernetes.io/tls
data:
tls.crt: $base64Cert
tls.key: $base64Key
"@
$secretYaml | kubectl apply -f -
# Cleanup temporary certificate and key files
Remove-Item $certFile, $keyFile
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment