SSL Certificates¶
Overview¶
By default, the controller creates a default SSL certificate for HTTPS traffic. This page covers SSL certificate configuration via cert-manager or manual management, as well as webhook certificate setup. You can also disable HTTPS entirely — see Disabling HTTPS.
Default SSL Certificate¶
Default Behavior (Development/Testing)¶
The chart works out of the box with cert-manager installed. By default, it creates:
- A self-signed
Issuernamed<release>-ssl-selfsigned - A
Certificateforlocaldev.meand*.localdev.me
The localdev.me domain resolves to 127.0.0.1, making it useful for local development. No additional configuration is required beyond having cert-manager installed:
# Install cert-manager (if not already installed)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.0/cert-manager.yaml
# Install the chart - SSL works out of the box
helm install my-release oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic --version 0.1.0
Note
The default self-signed certificate is intended for development and testing only. For production, override with your own domain and issuer.
Production Deployment¶
For production, override the default certificate configuration with your actual domain and a trusted issuer:
controller:
defaultSSLCertificate:
certManager:
createIssuer: false # Use your own issuer
dnsNames:
- "*.example.com"
- "example.com"
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
This requires an existing ClusterIssuer or Issuer. Create one if you haven't already:
# Create a ClusterIssuer (example with Let's Encrypt)
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: haproxy
EOF
The Helm chart creates a Certificate resource that cert-manager uses to automatically provision and renew the TLS Secret.
Alternative: Manual Certificate¶
To manage certificates without cert-manager, disable cert-manager integration and create a TLS Secret manually:
kubectl create secret tls default-ssl-cert \
--cert=path/to/tls.crt \
--key=path/to/tls.key \
--namespace=haptic
Custom Certificate Names¶
To use a different Secret name or namespace:
The controller will reference the Secret at certificates/my-wildcard-cert.
TLS Secret Format¶
The Secret must be of type kubernetes.io/tls and contain two keys:
apiVersion: v1
kind: Secret
metadata:
name: default-ssl-cert
namespace: haptic
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTi... # Base64-encoded certificate
tls.key: LS0tLS1CRUdJTi... # Base64-encoded private key
Disabling HTTPS¶
To run in HTTP-only mode (not recommended):
Warning
This disables HTTPS support entirely. HAProxy will only serve HTTP traffic.
Certificate Rotation¶
With cert-manager: Certificates are automatically renewed before expiration.
Manual certificates: You must update the Secret with a new certificate before the old one expires:
# Update Secret with new certificate
kubectl create secret tls default-ssl-cert \
--cert=new-tls.crt \
--key=new-tls.key \
--namespace=haptic \
--dry-run=client -o yaml | kubectl apply -f -
The controller watches the Secret and will automatically deploy the updated certificate to HAProxy.
SSL Troubleshooting¶
"Secret not found" errors:
Check that the Secret exists in the correct namespace:
HAProxy fails to start with SSL errors:
Verify the certificate and key are valid:
# Extract and verify certificate
kubectl get secret default-ssl-cert -n haptic -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout
# Verify key
kubectl get secret default-ssl-cert -n haptic -o jsonpath='{.data.tls\.key}' | base64 -d | openssl rsa -check -noout
Certificate not being updated:
The controller watches Secrets with store: on-demand. Changes are detected automatically, but HAProxy deployment follows the configured drift prevention interval (default: 60s).
Webhook Certificates¶
The admission webhook requires TLS certificates. The simplest setup uses cert-manager with a self-signed issuer:
webhook:
enabled: true
certManager:
enabled: true
createIssuer: true # Creates a self-signed Issuer automatically
This is the recommended approach when cert-manager is installed. The chart creates:
- A self-signed
Issuerresource - A
Certificateresource that references the Issuer - The webhook is automatically configured with CA bundle injection
To use an existing Issuer or ClusterIssuer instead:
webhook:
certManager:
enabled: true
createIssuer: false
issuerRef:
name: my-existing-issuer
kind: ClusterIssuer
For manual certificate management without cert-manager, provide the CA bundle: