310 lines
8.2 KiB
Markdown

# 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: <your-git-repo>
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 <challenge-name> -n <namespace>
```
## 📚 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).