# cert-manager-stack A wrapper Helm chart that bundles [cert-manager](https://cert-manager.io) with pre-configured Cloudflare DNS-01 solver and Let's Encrypt ClusterIssuer for GitOps deployments. ## 📋 Overview This chart provides a production-ready cert-manager deployment by: - **Using cert-manager as a dependency** (pristine upstream chart, easily upgradable) - **Adding custom resources** (Cloudflare API Secret, Let's Encrypt ClusterIssuer) - **Single configuration file** (`manifests/cert-manager-stack/values.yaml`) - **GitOps-friendly** (works seamlessly with ArgoCD) ## 🏗️ Architecture ``` cert-manager-stack (wrapper chart) ├── Dependency: cert-manager (local chart from ../cert-manager) │ ├── CRDs (Certificate, ClusterIssuer, etc.) │ ├── cert-manager controller │ ├── cert-manager webhook │ └── cert-manager cainjector └── Custom Resources (from wrapper templates) ├── Secret: cloudflare-api-token └── ClusterIssuer: letsencrypt ``` ## 📁 Files Structure ``` charts/cert-manager-stack/ ├── Chart.yaml # Wrapper chart definition ├── values.yaml # Default values (DO NOT EDIT) ├── templates/ │ ├── _helpers.tpl # Template helpers │ ├── cloudflare-api-token-secret.yaml # Cloudflare API Secret │ ├── clusterissuer-letsencrypt.yaml # Let's Encrypt ClusterIssuer │ └── NOTES.txt # Post-install notes └── README.md # This file manifests/cert-manager-stack/ └── values.yaml # ✏️ EDIT THIS FILE ``` ## ⚙️ Configuration ### Single Source of Truth **`manifests/cert-manager-stack/values.yaml`** is the only file you need to edit. ### Configuration Structure ```yaml # cert-manager upstream chart values certManager: enabled: true crds: enabled: true prometheus: enabled: false # Cloudflare DNS provider cloudflare: enabled: true apiToken: "YOUR_CLOUDFLARE_API_TOKEN" secretName: cloudflare-api-token namespace: cert-manager # Let's Encrypt ClusterIssuer clusterIssuer: enabled: true name: letsencrypt email: dvirlabs@gmail.com server: https://acme-v02.api.letsencrypt.org/directory ``` ## 🚀 ArgoCD Integration Create an ArgoCD Application: ```yaml # argocd-apps/cert-manager-stack.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: cert-manager-stack namespace: argocd spec: project: default source: repoURL: targetRevision: HEAD path: charts/cert-manager-stack helm: valueFiles: - ../../manifests/cert-manager-stack/values.yaml destination: server: https://kubernetes.default.svc namespace: cert-manager syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true ``` ArgoCD will: 1. Read the wrapper chart from `charts/cert-manager-stack/` 2. Load cert-manager dependency from local `charts/cert-manager/` 3. Apply values from `manifests/cert-manager-stack/values.yaml` 4. Deploy everything as a unified Helm release ## 📦 Installation ### Manual Installation (for testing) ```bash # Navigate to chart directory cd charts/cert-manager-stack # Update dependencies helm dependency update # Install helm install cert-manager-stack . \ --namespace cert-manager \ --create-namespace \ --values ../../manifests/cert-manager-stack/values.yaml ``` ### GitOps Installation (recommended) 1. Update your Cloudflare API token in `manifests/cert-manager-stack/values.yaml` 2. Commit and push to Git 3. ArgoCD will automatically sync and deploy ## 🔄 Upgrading cert-manager To upgrade to a newer cert-manager version: 1. Update the local cert-manager chart in `charts/cert-manager/` 2. Edit `charts/cert-manager-stack/Chart.yaml` 3. Update the dependency version to match: ```yaml dependencies: - name: cert-manager version: "v1.21.0" # Update this to match local chart repository: "file://../cert-manager" ``` 4. Commit and push 5. ArgoCD will handle the upgrade ## ✅ Why This Approach? ### ❌ What We're NOT Doing: - Forking/modifying the upstream cert-manager chart - Manual `kubectl apply` for Secret/ClusterIssuer - Embedding resources in cert-manager's values - Using hacks like `extraObjects` ### ✅ What We ARE Doing: - **Clean dependency management** - cert-manager stays pristine - **Single Helm release** - all resources managed together - **GitOps native** - no manual steps - **Helm best practices** - proper dependency and values structure - **Easy upgrades** - just bump the version number - **Migration-ready** - clean path to External Secrets/Vault ### Benefits Over Modifying Upstream Chart: 1. **Upgradability**: Can upgrade cert-manager without merge conflicts 2. **Clarity**: Separation between upstream and custom resources 3. **Maintainability**: Upstream chart bugs/fixes don't affect custom logic 4. **Reusability**: Can apply same pattern to other charts 5. **Audibility**: Clear distinction in Git history ## 📝 Usage Examples ### Create a Certificate ```yaml apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: my-app-tls namespace: my-app spec: secretName: my-app-tls-secret issuerRef: name: letsencrypt kind: ClusterIssuer dnsNames: - myapp.example.com - "*.myapp.example.com" ``` ### Use with Ingress ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-ingress annotations: cert-manager.io/cluster-issuer: letsencrypt spec: tls: - hosts: - myapp.example.com secretName: my-app-tls rules: - host: myapp.example.com http: paths: - path: / pathType: Prefix backend: service: name: my-app-service port: number: 80 ``` ## 🔐 Migrating to External Secrets When ready to move from raw Secrets to External Secrets: 1. Create a new template `templates/cloudflare-external-secret.yaml`: ```yaml {{- if .Values.cloudflare.useExternalSecret }} apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: cloudflare-api-token namespace: cert-manager spec: secretStoreRef: name: vault-backend kind: ClusterSecretStore target: name: cloudflare-api-token data: - secretKey: api-token remoteRef: key: cloudflare/api-token {{- end }} ``` 2. Update `manifests/cert-manager-stack/values.yaml`: ```yaml cloudflare: enabled: false # Disable raw Secret useExternalSecret: true # Enable ExternalSecret secretName: cloudflare-api-token # Keep same name ``` 3. No changes needed to ClusterIssuer (references same secret name) ## 🔍 Troubleshooting ### Check cert-manager logs ```bash kubectl logs -n cert-manager deploy/cert-manager-stack-certManager ``` ### Check ClusterIssuer status ```bash kubectl describe clusterissuer letsencrypt kubectl get clusterissuer letsencrypt -o yaml ``` ### Check Certificate status ```bash kubectl describe certificate my-app-tls -n my-app kubectl get certificaterequest -n my-app ``` ### Verify Cloudflare secret ```bash kubectl get secret cloudflare-api-token -n cert-manager kubectl describe secret cloudflare-api-token -n cert-manager ``` ### Check ACME challenges ```bash kubectl get challenges --all-namespaces kubectl describe challenge -n ``` ## 📚 References - [cert-manager Documentation](https://cert-manager.io/docs/) - [Cloudflare DNS-01 Challenge](https://cert-manager.io/docs/configuration/acme/dns01/cloudflare/) - [Let's Encrypt Documentation](https://letsencrypt.org/docs/) - [Helm Dependencies](https://helm.sh/docs/helm/helm_dependency/) ## 🤝 Contributing To improve this wrapper chart: 1. Edit files in `charts/cert-manager-stack/` 2. Test with `helm template` or `helm install --dry-run` 3. Update `manifests/cert-manager-stack/values.yaml` if needed 4. Commit and create a PR ## 📄 License This wrapper chart follows the same license as cert-manager (Apache 2.0).