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 internospostgres.internal, api.corp o 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 intermedio
  • secrets/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 servidor
  • config/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

BackendPredeterminadoHACaso de uso
BadgerNoInstancia única, dev/equipos pequeños
PostgreSQLNoProducción multirréplica
MySQLNoProducció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

HerramientaServidor ACMECerts SSHProvisionersHAFacilidadLicencia
step-caSí (integrado)8 tiposSí (BD ext.)AltaApache 2
CFSSLNoNoNingunoMediaBSD
Vault PKIVia pluginVia Vault SSHVia Vault authSí (Raft)BajaBSL
EJBCANoMuchosMuy bajaLGPL / EE
Let’s EncryptSí (público)NoSolo ACMEN/AMuy altaN/A
mkcertNoNoNingunoNoMuy altaMIT

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 --daemon en 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

Artículos Relacionados