Saltar a contenido

Cert-Manager

Platform Comando
kubectl (Static Manifests) kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml
Helm (Recommended) helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set installCRDs=true
macOS (cmctl CLI) brew install cmctl
Linux (cmctl CLI) curl -sSL https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cmctl-linux-amd64.tar.gz \ | tar xz && sudo mv cmctl /usr/local/bin
Windows (cmctl CLI) curl.exe -L -o cmctl.exe https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cmctl-windows-amd64.exe
Verify Installation kubectl get pods -n cert-manager
# Add Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io

# Update repository
helm repo update

# Install with custom values
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.13.3 \
  --values custom-values.yaml
```## Instalación

| Comando | Descripción |
|---------|-------------|
| `kubectl get certificates` | Listar todos los certificados en el espacio de nombres actual |
| `kubectl get certificates -A` | Listar certificados en todos los namespaces |
| `kubectl describe certificate <name>` | Mostrar información detallada del certificado |
| `kubectl get certificate <name> -o yaml` | Ver certificado en formato YAML |
| `kubectl get issuer` | Listar todos los emisores en el espacio de nombres actual |
| `kubectl get clusterissuer` | Enumerar todos los emisores de clúster |
| `kubectl describe issuer <name>` | Mostrar información detallada del emisor |
| `kubectl get certificaterequest` | Listar solicitudes de certificado |
| `kubectl get order` | Ver pedidos de certificados ACME |
| `kubectl get challenge` | Ver desafíos ACME para validación de dominio |
| `kubectl logs -n cert-manager deployment/cert-manager` | Ver registros del controlador cert-manager |
| `kubectl logs -n cert-manager deployment/cert-manager-webhook` | Ver registros de webhook |
| `kubectl logs -n cert-manager deployment/cert-manager-cainjector` | Ver logs del inyector CA |
| `cmctl check api` | Verificar que la API de cert-manager esté disponible |
| `cmctl version` | Mostrar información de versión de cert-manager |
| `cmctl status certificate <name>` | Verificar estado y preparación del certificado |
| `cmctl inspect secret <secret-name>` | Inspeccionar secreto TLS creado por cert-manager |
| `cmctl renew <cert-name>` | Activar manualmente la renovación de certificado |
| `kubectl get crd \ | grep cert-manager` | Enumerar todas las Definiciones de Recursos Personalizados (Custom Resource Definitions) de cert-manager |
| `kubectl get events --field-selector involvedObject.name=<cert-name>` | Ver eventos relacionados con un certificado específico |### Configuración del Repositorio Helm

| Comando | Descripción |
|---------|-------------|
| `cmctl approve <certificaterequest-name>` | Aprobar manualmente una solicitud de certificado |
| `cmctl deny <certificaterequest-name>` | Denegar una solicitud de certificado |
| `cmctl create certificaterequest test --from-certificate-file=cert.yaml` | Crear solicitud de certificado desde archivo |
| `cmctl convert --output-format=pem --input-file=cert.yaml` | Convertir certificado a formato PEM |
| `cmctl experimental create acmeaccount --server=<url> --email=<email>` | Registro de cuenta ACME de prueba |
| `kubectl annotate certificate <name> cert-manager.io/issue-temporary-certificate="true" --overwrite` | Forzar renovación inmediata de certificado |
| `kubectl delete certificaterequest <name>` | Eliminar solicitud de certificado fallida |
| `kubectl delete order <name>` | Eliminar orden de ACME |
| `kubectl delete challenge <name>` | Eliminar desafío ACME atascado |
| `kubectl get certificate <name> -o jsonpath='{.status.conditions}'` | Extraer condiciones de estado de certificado |
| `kubectl get secret <tls-secret> -o jsonpath='{.data.tls\.crt}' \ | base64 -d \ | openssl x509 -text -noout` | Decodificar y ver detalles del certificado |
| `kubectl get secret <tls-secret> -o jsonpath='{.data.tls\.crt}' \ | base64 -d \ | openssl x509 -noout -dates` | Verificar fechas de expiración de certificados |
| `helm upgrade cert-manager jetstack/cert-manager --namespace cert-manager --version v1.13.3` | Actualizar cert-manager a nueva versión |
| `kubectl rollout restart deployment -n cert-manager` | Reiniciar todos los componentes de cert-manager |
| `kubectl scale deployment cert-manager -n cert-manager --replicas=2` | Escalar cert-manager para alta disponibilidad |
| `kubectl get certificate --watch` | Ver cambios de estado de certificados en tiempo real |
| `kubectl patch certificate <name> --type merge -p '{"spec":{"renewBefore":"720h"}}'` | Modificar ventana de renovación de certificado |
| `kubectl delete secret <tls-secret>` | Eliminar secreto de certificado (desencadena recreación) |
| `cmctl experimental install` | Instalar cert-manager usando la herramienta cmctl |
| `cmctl experimental uninstall` | Desinstalar cert-manager y limpiar recursos |
```yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
```## Comandos Básicos
```yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-staging
    solvers:
    - http01:
        ingress:
          class: nginx
```## Uso Avanzado
```yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - dns01:
        cloudDNS:
          project: my-gcp-project
          serviceAccountSecretRef:
            name: clouddns-dns01-solver
            key: key.json
```## Configuración
```yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: ca-issuer
  namespace: default
spec:
  ca:
    secretName: ca-key-pair
```### ClusterIssuer Autofirmado
```yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-com
  namespace: default
spec:
  secretName: example-com-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - example.com
  - www.example.com
  duration: 2160h      # 90 days
  renewBefore: 360h    # 15 days before expiry
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-cert
  namespace: default
spec:
  secretName: wildcard-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - "*.example.com"
  - example.com
```### Let's Encrypt Staging (Desafío HTTP-01)
```yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: vault-issuer
  namespace: default
spec:
  vault:
    server: https://vault.example.com
    path: pki/sign/example-dot-com
    auth:
      kubernetes:
        mountPath: /v1/auth/kubernetes
        role: cert-manager
        secretRef:
          name: vault-token
          key: token
# custom-values.yaml
installCRDs: true
replicaCount: 2

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 200m
    memory: 256Mi

prometheus:
  enabled: true
  servicemonitor:
    enabled: true

webhook:
  replicaCount: 2

cainjector:
  replicaCount: 2
```### Let's Encrypt Producción (Desafío DNS-01)
```bash
# Create ClusterIssuer
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: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx
EOF

# Create Ingress with TLS annotation
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    secretName: example-com-tls
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: example-service
            port:
              number: 80
EOF

# Verify certificate creation
kubectl get certificate
kubectl describe certificate example-com-tls
# Create self-signed CA
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-ca
  namespace: cert-manager
spec:
  isCA: true
  commonName: my-ca
  secretName: my-ca-secret
  privateKey:
    algorithm: ECDSA
    size: 256
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
EOF

# Create CA issuer from generated CA
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: my-ca-issuer
spec:
  ca:
    secretName: my-ca-secret
EOF

# Issue service certificates
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: service-a-cert
  namespace: default
spec:
  secretName: service-a-tls
  duration: 8760h
  renewBefore: 720h
  subject:
    organizations:
    - my-org
  commonName: service-a.default.svc.cluster.local
  dnsNames:
  - service-a.default.svc.cluster.local
  issuerRef:
    name: my-ca-issuer
    kind: ClusterIssuer
EOF
```### Emisor de CA (PKI Interno)
```bash
# Create DNS provider secret (example: Cloudflare)
kubectl create secret generic cloudflare-api-token \
  --from-literal=api-token=YOUR_CLOUDFLARE_API_TOKEN

# Create ClusterIssuer with DNS-01 solver
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-dns
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-dns
    solvers:
    - dns01:
        cloudflare:
          email: admin@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token
EOF

# Request wildcard certificate
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-example
  namespace: default
spec:
  secretName: wildcard-example-tls
  issuerRef:
    name: letsencrypt-dns
    kind: ClusterIssuer
  dnsNames:
  - "*.example.com"
  - example.com
EOF

# Monitor certificate issuance
kubectl get certificate wildcard-example -w
# Create certificate for webhook
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: webhook-cert
  namespace: webhook-system
spec:
  secretName: webhook-server-cert
  duration: 8760h
  renewBefore: 720h
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
  dnsNames:
  - webhook-service.webhook-system.svc
  - webhook-service.webhook-system.svc.cluster.local
EOF

# Reference in webhook configuration
kubectl apply -f - <<EOF
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: my-webhook
  annotations:
    cert-manager.io/inject-ca-from: webhook-system/webhook-cert
webhooks:
- name: webhook.example.com
  clientConfig:
    service:
      name: webhook-service
      namespace: webhook-system
      path: "/validate"
    caBundle: "" # Injected by cert-manager
EOF
```### Recurso de Certificado
```bash
# Create certificate with short duration for testing
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: short-lived-cert
  namespace: default
spec:
  secretName: short-lived-tls
  duration: 24h
  renewBefore: 8h
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
  dnsNames:
  - test.example.com
EOF

# Monitor renewal
kubectl get certificate short-lived-cert -w

# Force immediate renewal
kubectl annotate certificate short-lived-cert \
  cert-manager.io/issue-temporary-certificate="true" \
  --overwrite

# Check renewal history
kubectl get certificaterequest -l cert-manager.io/certificate-name=short-lived-cert

# Verify new certificate
cmctl inspect secret short-lived-tls
renewBefore### Certificado Wildcard
certmanager_certificate_expiration_timestamp_seconds
- Usar DNS-01 para wildcards y servicios internos: El desafío DNS-01 es necesario para certificados wildcard y funciona mejor para servicios no expuestos a internet
  • Implementar RBAC adecuado: Restringir el acceso a Issuers y secretos de certificados usando Kubernetes RBAC para prevenir la creación de certificados no autorizados

  • Versionar configuraciones: Almacenar manifiestos de Certificate e Issuer en Git para rastrear cambios y habilitar flujos de trabajo GitOps

  • Usar issuers separados por entorno: Crear diferentes issuers para desarrollo/staging/producción para aislar credenciales y prevenir problemas de certificados entre entornos

  • Habilitar inyección de CA para webhooks: Usar cert-manager.io/inject-ca-fromannotation para inyectar automáticamente paquetes CA en configuraciones de webhook

Problema Solución
Certificate stuck in "Pending" Check certificate request: kubectl describe certificaterequest <name>. Look for ACME challenge failures or issuer configuration errors
ACME HTTP-01 challenge failing Verify ingress is accessible: curl http://<domain>/.well-known/acme-challenge/test. Check ingress class matches solver configuration
DNS-01 challenge timeout Confirm DNS provider credentials: kubectl get secret <dns-secret> -o yaml. Verify DNS propagation: dig TXT _acme-challenge.<domain>
"Too many certificates" rate limit Cambie al servidor de pruebas de Let's Encrypt o espere 7 días. Verifique los límites de tasa: https://letsencrypt.org/docs/rate-limits/
Certificate not renewing automatically Check renewBefore setting and cert-manager logs: kubectl logs -n cert-manager deployment/cert-manager. Verify controller is running
Webhook connection failures Verify webhook service is running: kubectl get svc -n cert-manager. Check webhook certificate validity: cmctl check api
CA injection not working Ensure cainjector is running: kubectl get pods -n cert-manager. Verify annotation syntax: cert-manager.io/inject-ca-from: namespace/certificate
Certificate shows "Ready=False" Get detailed status: cmctl status certificate <name>. Check events: kubectl get events --field-selector involvedObject.name=<cert-name>
Order stuck in "Pending" Delete order to retry: kubectl delete order <order-name>. Certificate controller will create new order automatically
Secret not created after certificate ready Check secret name matches secretName in Certificate spec. Verify namespace: kubectl get secret <name> -n <namespace>
Wildcard certificate validation fails Asegúrese de que el solucionador DNS-01 esté configurado (HTTP-01 no admite comodines). Verifique los permisos del proveedor DNS para la creación de registros TXT
Certificate shows wrong issuer Delete certificate request: kubectl delete certificaterequest <name>. Update Certificate spec with correct issuerRef
High memory usage Reduce certificate count or increase resources: kubectl set resources deployment cert-manager -n cert-manager --limits=memory=512Mi
Duplicate certificates created Check for multiple Certificate resources with same secretName. Remove duplicates to prevent conflicts
ACME account registration fails Verificar formato de correo electrónico en la especificación del emisor. Comprobar que la URL del servidor ACME sea correcta. Revisar los registros de cert-manager para obtener mensajes de error detallados

Resolución de problemas

# Complete troubleshooting workflow
kubectl describe certificate <cert-name>
kubectl get certificaterequest -l cert-manager.io/certificate-name=<cert-name>
kubectl describe certificaterequest <request-name>
kubectl get order
kubectl describe order <order-name>
kubectl get challenge
kubectl describe challenge <challenge-name>
kubectl logs -n cert-manager deployment/cert-manager --tail=100
```### Secuencia de Comandos de Depuración
```bash
# Search for specific certificate errors
kubectl logs -n cert-manager deployment/cert-manager | grep "certificate=<cert-name>"

# Find ACME challenge errors
kubectl logs -n cert-manager deployment/cert-manager | grep "challenge"

# Check for rate limit errors
kubectl logs -n cert-manager deployment/cert-manager | grep "rate limit"

# Monitor certificate renewal attempts
kubectl logs -n cert-manager deployment/cert-manager -f | grep "renewal"