# 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 (embedded in charts/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)
├── charts/                                      # Embedded dependencies
│   └── cert-manager/                           # cert-manager chart
│       ├── Chart.yaml
│       ├── values.yaml
│       └── templates/
├── 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 embedded `charts/cert-manager-stack/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. Replace the embedded cert-manager chart in `charts/cert-manager-stack/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 embedded chart version
       alias: certManager
   ```
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).
