TL;DR — Resumen Rápido
step-ca convierte cualquier servidor en una CA privada para TLS interno, mTLS y certificados SSH. Guía completa de instalación, provisioners y ACME.
step-ca es una autoridad certificadora privada de código abierto de Smallstep que te permite gestionar tu propia PKI para TLS interno, mTLS entre microservicios y certificados SSH, sin enviar tráfico a una CA pública. Si alguna vez has tenido problemas con Let’s Encrypt en dominios privados, advertencias de certificados autofirmados o distribución de confianza en docenas de servicios, step-ca resuelve todo esto con un diseño moderno orientado a la automatización. Esta guía cubre el ciclo completo: por qué necesitas una CA privada, arquitectura, instalación, provisioners, ACME para renovación automática, certificados SSH y un despliegue HA en producción.
¿Por qué una CA Privada?
Las CAs públicas como Let’s Encrypt requieren validación de dominio por internet público. Esto excluye inmediatamente:
- Nombres de host internos —
postgres.internal,api.corpo cualquier dominio.local/.corp - mTLS para microservicios — el mTLS requiere que cada servicio presente un certificado de cliente; emitir miles de certificados de servicio de corta duración desde una CA pública es impráctico y costoso
- Redes de confianza cero — cada conexión autenticada por certificado, no por posición de red
- Entornos air-gapped — sin acceso a internet público en absoluto
- Service mesh sin sidecars — TLS nativo entre servicios usando certificados de step-ca evita la sobrecarga de un despliegue completo de Istio o Linkerd
step-ca cubre esta necesidad con un servidor compatible con ACME, tiempos de vida cortos (24 horas por defecto), daemons de renovación automática e integración de primera clase con Kubernetes mediante el webhook autocert y el step-issuer de cert-manager.
Arquitectura
┌─────────────────────────────────────────────┐
│ servidor step-ca │
│ ┌──────────┐ ┌─────────────┐ │
│ │ CA Raíz │→ │ CA Intermedia│ │
│ │(offline) │ │ │ │
│ └──────────┘ └──────┬──────┘ │
│ │ firma │
│ ┌────────────────────▼──────────────────┐ │
│ │ Provisioners │ │
│ │ JWK │ ACME │ OIDC │ AWS │ K8sSA │… │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
↑ API HTTPS (puerto 9000)
┌────┴────┐
│step CLI │ certbot Caddy Traefik cert-manager
└─────────┘
La CA raíz solo firma el certificado de la CA intermedia. En producción debes mantener la clave raíz fuera de línea (air-gapped o en un módulo de seguridad de hardware). La CA intermedia es con la que step-ca opera día a día. Los provisioners son los mecanismos de autenticación que controlan la emisión de certificados.
Instalación
Binario (Linux/macOS)
# step CLI
wget https://github.com/smallstep/cli/releases/latest/download/step_linux_amd64.tar.gz
tar xzf step_linux_amd64.tar.gz && sudo mv step /usr/local/bin/
# step-ca servidor
wget https://github.com/smallstep/certificates/releases/latest/download/step-ca_linux_amd64.tar.gz
tar xzf step-ca_linux_amd64.tar.gz && sudo mv step-ca /usr/local/bin/
macOS (Homebrew)
brew install step
brew install step-ca
Docker
docker run -v step:/home/step \
-p 9000:9000 \
-e "DOCKER_STEPCA_INIT_NAME=CA Interna" \
-e "DOCKER_STEPCA_INIT_DNS_NAMES=ca.internal,localhost" \
-e "DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT=true" \
smallstep/step-ca
Kubernetes (Helm + autocert)
helm repo add smallstep https://smallstep.github.io/helm-charts
helm install step-certificates smallstep/step-certificates \
--set ca.name="CA Interna" \
--set ca.dns="step-certificates.default.svc.cluster.local"
Configuración Inicial: step ca init
step ca init \
--name "CA Corporativa" \
--dns ca.corp.internal \
--address :9000 \
--provisioner admin@corp.internal
Esto crea bajo $(step path)/:
certs/root_ca.crt— certificado de CA raíz (distribuir a todos los clientes)certs/intermediate_ca.crt— certificado intermediosecrets/root_ca_key— clave privada de CA raíz (mantener fuera de línea)secrets/intermediate_ca_key— clave intermedia (usada por el servidor)config/ca.json— configuración del servidorconfig/defaults.json— valores predeterminados del cliente
Inicia el servidor:
step-ca $(step path)/config/ca.json
Para producción, crea una unidad systemd:
[Unit]
Description=Smallstep CA
After=network.target
[Service]
User=step
ExecStart=/usr/local/bin/step-ca /etc/step-ca/config/ca.json
Restart=always
RestartSec=5
Environment=STEPPATH=/etc/step-ca
[Install]
WantedBy=multi-user.target
Provisioners
Los provisioners son plugins de autenticación. El step ca init inicial crea un provisioner JWK. Agrega más con:
step ca provisioner add <nombre> --type <TIPO>
JWK — Interactivo / Basado en Scripts
El provisioner predeterminado. Los clientes se autentican con un JSON Web Token firmado por la clave del provisioner:
step ca certificate api.internal api.crt api.key
# solicita contraseña del provisioner
ACME — Automatizado (Caddy, Nginx, certbot)
Crea un servidor ACME interno. Cualquier cliente ACME puede solicitar certificados de tu CA privada:
step ca provisioner add acme --type ACME
Ejemplo con certbot apuntando a tu CA interna:
certbot certonly \
--server https://ca.internal:9000/acme/acme/directory \
--standalone \
-d api.internal \
--agree-tos -m admin@corp.internal
Caddy — simplemente configura el directorio ACME en el Caddyfile:
api.internal {
tls {
ca https://ca.internal:9000/acme/acme/directory
}
reverse_proxy localhost:8080
}
OIDC — Basado en Inicio de Sesión Único
Vincula la emisión de certificados a tu proveedor de identidad (Google, Azure AD, Okta):
step ca provisioner add google --type OIDC \
--client-id <GOOGLE_CLIENT_ID> \
--client-secret <SECRET> \
--configuration-endpoint https://accounts.google.com/.well-known/openid-configuration
Identidad de Instancia en la Nube (AWS / GCP / Azure)
Las VMs prueban su identidad usando el servicio de metadatos de instancia del proveedor de nube. Sin secretos compartidos:
step ca provisioner add aws --type AWS --aws-account 123456789012
step ca provisioner add gcp --type GCP --gcp-project mi-proyecto
step ca provisioner add azure --type Azure --azure-tenant <TENANT_ID>
Cuentas de Servicio de Kubernetes (K8sSA)
Los pods se autentican usando su JWT de cuenta de servicio de Kubernetes:
step ca provisioner add k8s --type K8sSA \
--public-key /var/run/secrets/kubernetes.io/serviceaccount/public-key.pub
Emisión de Certificados
Emisión Manual con step CLI
# ECDSA P-256 (predeterminado) con SANs
step ca certificate api.internal api.crt api.key \
--san api.corp \
--san 10.0.1.50
# Clave Ed25519
step ca certificate api.internal api.crt api.key --kty OKP --crv Ed25519
# RSA 4096 (para compatibilidad con sistemas heredados)
step ca certificate api.internal api.crt api.key --kty RSA --size 4096
# Certificado de corta duración (1 hora)
step ca certificate api.internal api.crt api.key --not-after 1h
Certificados SSH
step-ca emite certificados SSH de host y de usuario, permitiendo acceso sin bastión con SSO:
# Certificado de usuario SSH
step ssh certificate jcarlos@corp.internal id_ecdsa \
--provisioner google
# Certificado de host SSH
step ssh certificate --host api.internal ssh_host_ecdsa_key \
--provisioner aws
Configura sshd para confiar en tu CA:
# /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/step_user_ca.pub
HostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub
HostKey /etc/ssh/ssh_host_ecdsa_key
Renovación y Revocación
Renovación Automática (—daemon)
step ca renew --daemon api.crt api.key
# renueva al ~2/3 del ciclo de vida, reemplaza archivos en su lugar
En un servicio systemd:
ExecStartPost=/usr/local/bin/step ca renew --daemon \
/etc/tls/servicio.crt /etc/tls/servicio.key \
--exec "systemctl reload mi-servicio"
Revocación
Revocación pasiva — el predeterminado de step-ca. Los tiempos de vida cortos hacen que la revocación sea implícita.
Revocación activa — habilita CRL u OCSP en ca.json para revocación inmediata:
step ca revoke --serial 1234567890abcdef
Backends de Base de Datos y Alta Disponibilidad
| Backend | Predeterminado | HA | Caso de uso |
|---|---|---|---|
| Badger | Sí | No | Instancia única, dev/equipos pequeños |
| PostgreSQL | No | Sí | Producción multirréplica |
| MySQL | No | Sí | Producción multirréplica |
Cambia a PostgreSQL en ca.json:
"db": {
"type": "postgresql",
"dataSource": "postgresql://stepca:password@pg.internal:5432/stepca?sslmode=require"
}
Comparación: Opciones de CA Privada
| Herramienta | Servidor ACME | Certs SSH | Provisioners | HA | Facilidad | Licencia |
|---|---|---|---|---|---|---|
| step-ca | Sí (integrado) | Sí | 8 tipos | Sí (BD ext.) | Alta | Apache 2 |
| CFSSL | No | No | Ninguno | Sí | Media | BSD |
| Vault PKI | Via plugin | Via Vault SSH | Via Vault auth | Sí (Raft) | Baja | BSL |
| EJBCA | Sí | No | Muchos | Sí | Muy baja | LGPL / EE |
| Let’s Encrypt | Sí (público) | No | Solo ACME | N/A | Muy alta | N/A |
| mkcert | No | No | Ninguno | No | Muy alta | MIT |
Errores Comunes y Casos Especiales
Desfase de reloj — step-ca rechaza CSRs si el reloj del cliente difiere más de pocos minutos. Asegúrate de que NTP esté ejecutándose en todos los nodos.
Protección de la clave CA raíz — tras step ca init, mueve root_ca_key fuera de línea. La clave intermedia es todo lo que step-ca necesita para operar.
ACME y SANs de IP — los desafíos ACME HTTP-01 y DNS-01 funcionan para nombres de dominio. Para SANs de dirección IP, usa el step CLI directamente.
Transparencia de certificados — a diferencia de las CAs públicas, step-ca no envía certificados a los registros CT. Esto es deseable para infraestructura interna.
Gestión de contraseñas de provisioner — almacena las contraseñas de provisioner en un gestor de secretos (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault). Nunca las codifiques en scripts.
Resumen
- step-ca ejecuta una PKI privada completa: CA raíz, CA intermedia y API REST para emisión de certificados
- Usa certificados de corta duración (predeterminado 24h) y renovación con
--daemonen lugar de CRL/OCSP - El provisioner ACME convierte a step-ca en una CA interna compatible con Caddy, Nginx, Traefik y certbot
- Los provisioners de nube (AWS, GCP, Azure) permiten el arranque de identidad de workload sin secretos
- K8sSA y la integración con cert-manager automatizan el TLS por pod en Kubernetes sin sidecars
- Los certificados SSH de step-ca eliminan la gestión individual de known_hosts por servidor
- Para HA, cambia el backend de base de datos a PostgreSQL y ejecuta múltiples réplicas detrás de un balanceador de carga
- Mantén la clave CA raíz fuera de línea; rota la clave de CA intermedia de forma independiente si se ve comprometida