Qué se mantiene igual que Let’s Encrypt

Si alguna vez has obtenido un certificado Let’s Encrypt, ya puedes usar la CA interna. El protocolo es idéntico — ACME RFC 8555. Los clientes que ya conoces funcionan sin modificación.

Igual:

  • Retos HTTP-01 y DNS-01.
  • Registro de cuenta y gestión de clave.
  • Duración por defecto de 90 días.
  • Renovación automática a los 60 días.
  • Certificados wildcard vía DNS-01.

Qué es distinto

Dos pequeñas diferencias, ambas pasos de configuración únicos:

  1. La URL del directorio ACME apunta a la CA interna, no a https://acme-v02.api.letsencrypt.org/directory.
  2. El certificado raíz de la CA interna debe ser confiado por el cliente que hace la solicitud. Esto se gestiona normalmente vía configuración (Ansible, Puppet, MDM) al aprovisionar el host.

Esa es toda la diferencia. Todo lo demás es el mismo flujo Let’s Encrypt que ya conoces.

Apuntar tu cliente a la CA interna

El endpoint ACME interno se publica en una URL interna estable (típicamente https://acme.example.internal/acme/acme/directory, pero la ruta exacta depende de tu despliegue). A continuación recetas para clientes comunes.

certbot

certbot --server https://acme.example.internal/acme/acme/directory \
        --email ops@example.internal \
        --no-eff-email \
        --agree-tos \
        certonly --standalone -d service.example.internal

Para renovación, el flag --server se persiste en renewal/*.conf, así que el cron periódico certbot renew lo recoge automáticamente.

acme.sh

acme.sh --register-account \
        --server https://acme.example.internal/acme/acme/directory \
        -m ops@example.internal

acme.sh --issue \
        --server https://acme.example.internal/acme/acme/directory \
        --standalone \
        -d service.example.internal

lego

lego --server=https://acme.example.internal/acme/acme/directory \
     --email=ops@example.internal \
     --domains=service.example.internal \
     --http run

Caddy

En tu Caddyfile:

{
  acme_ca https://acme.example.internal/acme/acme/directory
  email ops@example.internal
}

service.example.internal {
  reverse_proxy localhost:8080
}

Caddy obtiene y renueva el certificado automáticamente.

Traefik

En tu configuración dinámica:

certificatesResolvers:
  internal:
    acme:
      caServer: "https://acme.example.internal/acme/acme/directory"
      email: ops@example.internal
      storage: /etc/traefik/acme.json
      httpChallenge:
        entryPoint: web

Referenciar certResolver: internal desde las definiciones de router.

Instalar el certificado raíz en los clientes

Para que los certificados emitidos por la CA sean confiados sin advertencias, la raíz de la CA debe estar en el store de confianza del sistema de cada host que vaya a ver los certificados.

Linux (familia Debian/Devuan)

sudo cp internal-ca-root.crt /usr/local/share/ca-certificates/internal-ca-root.crt
sudo update-ca-certificates

Linux (familia RHEL)

sudo cp internal-ca-root.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract

OpenBSD / FreeBSD

# OpenBSD
sudo install -m 0644 internal-ca-root.crt /etc/ssl/internal-ca-root.crt
sudo cat /etc/ssl/cert.pem internal-ca-root.crt > /etc/ssl/cert.pem.new
sudo mv /etc/ssl/cert.pem.new /etc/ssl/cert.pem

macOS

sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain internal-ca-root.crt

Windows

certutil.exe -addstore -f "ROOT" internal-ca-root.crt

Para navegadores que mantienen sus propios stores de confianza (Firefox), la raíz debe importarse adicionalmente vía Ajustes → Privacidad → Certificados → Autoridades.

Esta instalación normalmente se gestiona por gestión de configuración. Hacerlo a mano está bien para un caso puntual pero no escala a una flota.

Certificados wildcard

Los wildcards se emiten vía reto DNS-01 — el cliente prueba el control de un dominio creando un registro TXT. Esto funciona para dominios solo internos porque la CA interna puede validar registros TXT en cualquier servidor DNS que controles.

Ejemplo con acme.sh usando el proveedor BIND nsupdate:

acme.sh --issue \
        --server https://acme.example.internal/acme/acme/directory \
        --dns dns_nsupdate \
        -d '*.example.internal' \
        -d 'example.internal'

Necesitarás que el proveedor nsupdate esté configurado contra tu DNS interno — ver la documentación acme.sh para las variables de entorno específicas.

Renovación

La renovación funciona igual que con Let’s Encrypt: el cliente comprueba la validez restante y renueva cuando está por debajo del umbral (60 días para un cert de 90 días).

Para la mayoría de despliegues, el cron de renovación existente (certbot renew, acme.sh --cron, la renovación automática de Caddy) funciona tal cual.

Si ves fallos de renovación:

  1. ¿Sigue siendo alcanzable la URL del directorio? Un cambio de red puede haber roto la ruta del cliente a la CA.
  2. ¿Ha expirado o rotado el certificado raíz? Las raíces típicamente son válidas 10 años; los intermedios por periodos más cortos. Una rotación que no propagó a todos los clientes se manifestará como fallos de renovación.
  3. ¿Sigue siendo apropiado el tipo de reto? Un servicio que antes era alcanzable para HTTP-01 puede ahora estar detrás de un load balancer que rompe la respuesta del reto.

Límites y políticas

La CA interna aplica políticas por cuenta — qué dominios cada cuenta puede solicitar certificados, qué duración máxima se permite, qué tipos de reto se aceptan.

Si una solicitud que funcionaba ayer falla de repente con un error policy denied, la política ha cambiado (o tu vinculación de cuenta). Contacta con tu operador de CA. El servicio Operación es la vía de escalado si tu operador de CA somos nosotros.