Cifra y autentica la conexión entre el borde y tu origen. El borde verifica en modo full_strict los certificados de origen emitidos por la OpenWAF Origin CA, y puede presentar su propio certificado de cliente (mTLS) a los orígenes que lo exijan. Consulta SSL/TLS.
| Operación | Ruta |
|---|---|
| Listar certificados de origen | GET /v1/orgs/{org}/zones/{zone}/origin-certs |
| Emitir un certificado de origen | POST /v1/orgs/{org}/zones/{zone}/origin-certs |
| Revocar un certificado de origen | DELETE /v1/orgs/{org}/zones/{zone}/origin-certs/{cert} |
| Ver el estado del TLS al origen | GET /v1/orgs/{org}/zones/{zone}/origin-tls/status |
| Descargar la Origin CA | GET /v1/origin-tls/origin-ca.pem |
| Descargar la CA de cliente del borde | GET /v1/origin-tls/edge-client-ca.pem |
Listar certificados de origen
GET /v1/orgs/{org}/zones/{zone}/origin-certsLista los certificados de origen emitidos para la zona, del más reciente al más antiguo, con sus nombres, tipo de clave, serie, caducidad y estado (active, revoked, expired). Nunca devuelve claves privadas.
Rol necesario: readonly o superior.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
org | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, 404). |
zone | ruta | string (uuid) | Sí | Id de la zona (consulta Listar zonas). |
Respuesta
200: Devuelveitems, un array de OriginCert.
| Campo | Tipo | Descripción |
|---|---|---|
items[].id | string (uuid) | Id. |
items[].zone_id | string (uuid) | Id de la zona. |
items[].hostnames | array de string | |
items[].key_type | string | csr: se certificó la clave pública de una petición. Uno de ecdsa_p256, rsa2048, csr. |
items[].serial | string | Hexadecimal en minúsculas sin ceros a la izquierda. |
items[].fingerprint_sha256 | string | Huella SHA-256 del certificado. |
items[].not_before | string (date-time) | Inicio de la validez. |
items[].not_after | string (date-time) | Fin de la validez. |
items[].validity_days | integer | |
items[].status | string | revoked gana a expired. Uno de active, revoked, expired. |
items[].created_by | string | Quién lo creó (actor). |
items[].created_at | string (date-time) | Fecha de creación. |
items[].revoked_at | string (date-time) o null | Fecha de revocación (null si está activo). |
items[].revoked_by | string o null | Quién lo revocó. |
Errores
| Estado | Descripción |
|---|---|
404 | No existe o el token no lo ve (not_found). |
503 | unavailable: la Origin CA no está disponible ahora. |
Ejemplo
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/origin-certs" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.listOriginCerts({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.list_origin_certs(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"items": [
{
"id": "a5614527-0ce6-43be-ad1d-d012b7394867",
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"hostnames": [
"example.com",
"*.example.com"
],
"key_type": "ecdsa_p256",
"serial": "5f3a9c0e7b21d4a68c13e9f02b7d4a61",
"fingerprint_sha256": "example",
"not_before": "2026-10-27T09:30:00Z",
"not_after": "2026-10-27T09:30:00Z",
"validity_days": 0,
"status": "active",
"created_by": "token:11111111-2222-4333-8444-555555555555",
"created_at": "2026-10-27T09:30:00Z",
"revoked_at": "2026-10-27T09:30:00Z",
"revoked_by": null
}
]
}Emitir un certificado de origen
POST /v1/orgs/{org}/zones/{zone}/origin-certsEmite un certificado firmado por la OpenWAF Origin CA para nombres de la zona, para instalarlo en el servidor de origen y que el borde lo pueda verificar en modo full_strict. El certificado solo lleva nombres DNS (SAN), uso de autenticación de servidor, un número de serie aleatorio de 128 bits y el primer nombre como CN.
OpenWAF genera la clave (key_type) salvo que envíes un csr. La clave privada solo se devuelve en esta respuesta (Cache-Control: no-store) y nunca se guarda. Con dry_run=1 solo se valida la petición.
Rol necesario: tech o superior.
Controles de cambio: dry_run · X-Change-Reason (opcional)
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
org | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, 404). |
zone | ruta | string (uuid) | Sí | Id de la zona (consulta Listar zonas). |
X-Change-Reason | cabecera | string | No | Motivo del cambio (1-500 caracteres UTF-8, sin caracteres de control); por defecto, el alta de la zona. De 1 a 500 caracteres. |
dry_run | consulta | string | No | 1/true simula el cambio: responde 200 con el plan (Applied) y no guarda nada. Admite 0/false; otro valor: 400. Uno de 1, true, 0, false. |
Cuerpo de la petición
Tipo de contenido application/json. Esquema: OriginCertRequest.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
hostnames | array de string | Sí | La zona, nombres por debajo o comodines *.<nombre de la zona>; se pasan a minúsculas y sin repetir. De 1 a 100 elementos. |
key_type | string | No | Clave que genera OpenWAF (vacío con csr). Uno de ecdsa_p256, rsa2048. Por defecto ecdsa_p256. |
validity_days | integer | No | Uno de 7, 30, 90, 365, 730, 1095, 5475. Por defecto 5475. |
csr | string | No | Petición PEM (ECDSA P-256/P-384 o RSA 2048-8192): se certifica su clave pública; su sujeto y extensiones se ignoran y no se genera clave. |
Respuesta
200: Simulación. Devuelve OriginCertIssued.201: Emitido. Devuelve OriginCertIssued.
| Campo | Tipo | Descripción |
|---|---|---|
dry_run | boolean | Solo con ?dry_run=1: lo que se emitiría; sin PEM. |
certificate | OriginCert | |
certificate_pem | string | |
private_key_pem | string | PKCS#8. Solo en esta respuesta (no se guarda); se omite con csr. |
ca_pem | string | La OpenWAF Origin CA que lo firma. |
Errores
| Estado | Descripción |
|---|---|
400 | Petición mal formada (bad_request). |
403 | El rol o los límites del token no lo permiten (forbidden). |
404 | No existe o el token no lo ve (not_found). |
409 | zone_merged: la zona está fusionada en otra (solo lectura hasta que se separe o se purgue). |
422 | Error de validación (invalid) o motivo ausente/no válido (reason_required). |
503 | unavailable: la Origin CA no está disponible ahora. |
Ejemplo
curl -X POST "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/origin-certs" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Origin certificate for the web servers" \
-H "Content-Type: application/json" \
--data '{
"hostnames": [
"example.com",
"*.example.com"
],
"key_type": "ecdsa_p256",
"validity_days": 5475
}'import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.issueOriginCert({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
body: {
hostnames: [
"example.com",
"*.example.com"
],
key_type: "ecdsa_p256",
validity_days: 5475
}
}, {
reason: "Origin certificate for the web servers"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.issue_origin_cert(
zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
body={
"hostnames": [
"example.com",
"*.example.com"
],
"key_type": "ecdsa_p256",
"validity_days": 5475
},
reason="Origin certificate for the web servers",
){
"dry_run": false,
"certificate": {
"id": "a5614527-0ce6-43be-ad1d-d012b7394867",
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"hostnames": [
"example.com",
"*.example.com"
],
"key_type": "ecdsa_p256",
"serial": "5f3a9c0e7b21d4a68c13e9f02b7d4a61",
"fingerprint_sha256": "example",
"not_before": "2026-10-27T09:30:00Z",
"not_after": "2026-10-27T09:30:00Z",
"validity_days": 0,
"status": "active",
"created_by": "token:11111111-2222-4333-8444-555555555555",
"created_at": "2026-10-27T09:30:00Z",
"revoked_at": "2026-10-27T09:30:00Z",
"revoked_by": null
},
"certificate_pem": "example",
"private_key_pem": "example",
"ca_pem": "example"
}Revocar un certificado de origen
DELETE /v1/orgs/{org}/zones/{zone}/origin-certs/{cert}Revoca un certificado de origen. Los nodos del borde lo rechazan en modo full_strict desde su siguiente configuración, así que un origen que aún lo sirva recibe errores 526. Revocarlo otra vez no cambia nada. Con dry_run=1 solo se devuelve el certificado.
Rol necesario: tech o superior.
Controles de cambio: dry_run · X-Change-Reason (opcional)
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
org | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, 404). |
zone | ruta | string (uuid) | Sí | Id de la zona (consulta Listar zonas). |
cert | ruta | string (uuid) | Sí | Id del certificado. |
X-Change-Reason | cabecera | string | No | Motivo del cambio (1-500 caracteres UTF-8, sin caracteres de control); por defecto, el alta de la zona. De 1 a 500 caracteres. |
dry_run | consulta | string | No | 1/true simula el cambio: responde 200 con el plan (Applied) y no guarda nada. Admite 0/false; otro valor: 400. Uno de 1, true, 0, false. |
Respuesta
200: El certificado (revocado). Devuelve OriginCert.
| Campo | Tipo | Descripción |
|---|---|---|
id | string (uuid) | Id. |
zone_id | string (uuid) | Id de la zona. |
hostnames | array de string | |
key_type | string | csr: se certificó la clave pública de una petición. Uno de ecdsa_p256, rsa2048, csr. |
serial | string | Hexadecimal en minúsculas sin ceros a la izquierda. |
fingerprint_sha256 | string | Huella SHA-256 del certificado. |
not_before | string (date-time) | Inicio de la validez. |
not_after | string (date-time) | Fin de la validez. |
validity_days | integer | |
status | string | revoked gana a expired. Uno de active, revoked, expired. |
created_by | string | Quién lo creó (actor). |
created_at | string (date-time) | Fecha de creación. |
revoked_at | string (date-time) o null | Fecha de revocación (null si está activo). |
revoked_by | string o null | Quién lo revocó. |
Errores
| Estado | Descripción |
|---|---|
403 | El rol o los límites del token no lo permiten (forbidden). |
404 | No existe o el token no lo ve (not_found). |
Ejemplo
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/origin-certs/9e7c5a3b-1d2f-4e8a-b6c4-3a5d7f9b1c2e" \
-H "Authorization: Bearer $OPENWAF_TOKEN" \
-H "X-Change-Reason: Server decommissioned"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.revokeOriginCert({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
cert: "9e7c5a3b-1d2f-4e8a-b6c4-3a5d7f9b1c2e"
}, {
reason: "Server decommissioned"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.revoke_origin_cert(
zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
cert="9e7c5a3b-1d2f-4e8a-b6c4-3a5d7f9b1c2e",
reason="Server decommissioned",
){
"id": "a5614527-0ce6-43be-ad1d-d012b7394867",
"zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
"hostnames": [
"example.com",
"*.example.com"
],
"key_type": "ecdsa_p256",
"serial": "5f3a9c0e7b21d4a68c13e9f02b7d4a61",
"fingerprint_sha256": "example",
"not_before": "2026-10-27T09:30:00Z",
"not_after": "2026-10-27T09:30:00Z",
"validity_days": 0,
"status": "active",
"created_by": "token:11111111-2222-4333-8444-555555555555",
"created_at": "2026-10-27T09:30:00Z",
"revoked_at": "2026-10-27T09:30:00Z",
"revoked_by": null
}Ver el estado del TLS al origen
GET /v1/orgs/{org}/zones/{zone}/origin-tls/statusPara cada nombre con proxy, devuelve sus ajustes efectivos de TLS al origen, lo que muestra un handshake TLS con el origen (sin petición HTTP; las direcciones privadas, de loopback y de enlace local quedan skipped) y los errores 525/526 que ha notificado el borde en las últimas 24 horas. El resultado se guarda 60 segundos por zona.
Rol necesario: readonly o superior.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
org | ruta | string (uuid) | Sí | Id de la organización. Cada ruta comprueba que es la del token (si no, 404). |
zone | ruta | string (uuid) | Sí | Id de la zona (consulta Listar zonas). |
Respuesta
200: Devuelve OriginTLSStatus.
| Campo | Tipo | Descripción |
|---|---|---|
checked_at | string (date-time) | Última comprobación. |
hosts | array de object | |
hosts[].host | string | |
hosts[].upstream_url | string | Sin credenciales. |
hosts[].tls_verify | string | Uno de full, full_strict. |
hosts[].server_name | string | Configurado (vacío = el de siempre). |
hosts[].client_cert | boolean | |
hosts[].effective_server_name | string | SNI y nombre verificado: server_name, o el host del origen si es un nombre, o el del sitio si es una IP. |
hosts[].probe | object | |
hosts[].edge_errors_24h | integer | Eventos 525/526 del borde en las últimas 24 h. |
hosts[].last_edge_error | object o null |
Errores
| Estado | Descripción |
|---|---|
404 | No existe o el token no lo ve (not_found). |
Ejemplo
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/origin-tls/status" \
-H "Authorization: Bearer $OPENWAF_TOKEN"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.getOriginTlsStatus({
zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.get_origin_tls_status(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"){
"checked_at": "2026-10-27T09:30:00Z",
"hosts": [
{
"host": "www.example.com",
"upstream_url": "https://203.0.113.10",
"tls_verify": "full",
"server_name": "",
"client_cert": false,
"effective_server_name": "example",
"probe": {
"state": "ok",
"error": "",
"issuer": "example",
"subject": "example",
"not_after": "2026-10-27T09:30:00Z",
"days_left": 0,
"name_match": false,
"trusted_by": "public",
"requests_client_cert": false
},
"edge_errors_24h": 0,
"last_edge_error": {
"ts": "2026-10-27T09:30:00Z",
"status": 525,
"msg": ""
}
}
]
}Descargar la Origin CA
GET /v1/origin-tls/origin-ca.pemDevuelve, en PEM, todos los certificados no caducados de la OpenWAF Origin CA (varios durante una rotación). En modo full_strict el borde confía en estos certificados además de en las raíces públicas.
Rol necesario: Ninguno: público, sin token.
Respuesta
200: Cuerpo de tipotext/plain.
Errores
| Estado | Descripción |
|---|---|
503 | unavailable: la Origin CA no está disponible ahora. |
Ejemplo
curl "https://dash.openwaf.dev/v1/origin-tls/origin-ca.pem"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.getOriginCa();from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.get_origin_ca()-----BEGIN CERTIFICATE-----
MIIB…
-----END CERTIFICATE-----Descargar la CA de cliente del borde
GET /v1/origin-tls/edge-client-ca.pemDevuelve, en PEM, la CA de los certificados de cliente que el borde presenta a los orígenes (mTLS). Configúrala en un origen que exija el certificado del borde.
Rol necesario: Ninguno: público, sin token.
Respuesta
200: Cuerpo de tipotext/plain.
Errores
| Estado | Descripción |
|---|---|
503 | unavailable: la Origin CA no está disponible ahora. |
Ejemplo
curl "https://dash.openwaf.dev/v1/origin-tls/edge-client-ca.pem"import { OpenWAF } from "./openwaf";
const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token de OPENWAF_TOKEN
const result = await owf.getEdgeClientCa();from openwaf import OpenWAF
owf = OpenWAF(base_url="https://dash.openwaf.dev") # token de OPENWAF_TOKEN
result = owf.get_edge_client_ca()-----BEGIN CERTIFICATE-----
MIIB…
-----END CERTIFICATE-----