Skip to content

PCI DSS Pregunta 24 — Criptografía Fuerte para Acceso Administrativo No-Consola ​

Direccionamiento corregido el 2026-08-16 · AVISO-SUBREDES-2026-08

Este documento describía subredes por función —10.100.10.0/24, 10.100.20.0/24 y similares— que no existen en la infraestructura. La red de producción es plana, 10.100.0.0/16, con todos los sistemas en 10.100.0.x.

Las referencias se han sustituido por el mecanismo de separación real: cortafuegos aplicados por etiqueta y políticas de red del orquestador con denegación por defecto. Las zonas funcionales sí existen; su direccionamiento no.

Descripción completa en Arquitectura de red — descripción verificada.

CampoValor
SolicitanteJosé David Álvarez — QSA ControlCase
Comentario QSA"Por favor proveer la evidencia de manera que se identifique que está aplicada a los activos: cde-pool-node-01, así como a los microservicios del alcance, CDE-FW"
Fecha de extracción2026-05-27
Tipo de evidenciaTLS handshakes + SSH config + DB SSL + Firewall hardening
EstadoRESUELTO — Evidencia extraída en vivo + 1 hallazgo crítico remediado en la misma sesión
Paquete adjuntoq24-crypto-admin-20260527.tar.gz

Resumen ejecutivo: Todo acceso administrativo no-consola a los componentes dentro del alcance PCI usa criptografía fuerte (TLS 1.3 / SSH OpenSSH 10 con AES-GCM y KEX post-quantum / TLS 1.2+ forzado en DB). Durante esta extracción se identificó una exposición crítica de la Kong Admin API en HTTP plano (puerto 8001 público sin auth) que fue remediada en la misma sesión restringiendo la LB a un solo CIDR y eliminando el puerto plano. Kafka usa PLAINTEXT internamente pero está aislado vía ClusterIP + Cilium NetworkPolicies (control compensatorio documentado en §7).


1. Inventario de componentes y mecanismos de admin no-consola ​

Activo del alcanceVector de admin no-consolaCifrado en tránsitoSección
cde-pool-node-01 (cde-38q702)SSH (puerto 22, defense-in-depth)OpenSSH 10.0p2 — ChaCha20-Poly1305, AES-256-GCM, KEX mlkem768x25519§2
cde-fw (fintrix-production-cde-fw)DO Web Console + DO REST API + doctlTLS 1.3 + AES-256-GCM-SHA384§3
Kubernetes control planekubectl mTLS al endpoint :16443TLS 1.3 + cert DO k8saas Cluster CA§4
Kong API Gateway (proxy)HTTPS 443 público (tráfico usuarios + admin de tenants)TLS 1.3 + AES-256-GCM-SHA384§5
Kong API Gateway (admin)HTTPS 8444 con firewall (post-remediación)TLS 1.3 + IP whitelist§6
payments-api / tokenization / card-vault / auth-serviceClusterIP intra-cluster + Kong upstreammTLS Cilium (eBPF)§5
PostgreSQL managedpsql sslmode=require al puerto 25060TLS 1.2+ forzado por DO Managed§7
KafkaCliente Kafka interno (ClusterIP)PLAINTEXT — control compensatorio §7§7

2. cde-pool-node-01 — SSH con criptografía fuerte ​

Identificadores ​

AtributoValor
Hostnamecde-38q702
Internal IP10.100.0.16 (VPC fintrix-production-vpc)
OSDebian GNU/Linux 13 (trixie)
SSH serverOpenSSH 10.0p2 Debian-7+deb13u4
OpenSSL3.5.6 (7 Apr 2026)

Algoritmos criptográficos efectivos (sshd -T) ​

CategoríaAlgoritmos permitidosCumplimiento
KEXmlkem768x25519-sha256, sntrup761x25519-sha512, curve25519-sha256, ecdh-sha2-nistp{256,384,521}✓ Sin diffie-hellman-group1-sha1, sin RSA-1024
Ciphers[email protected], aes{128,256}[email protected], aes{128,192,256}-ctr✓ AEAD prioritarios, sin DES/3DES/RC4/Blowfish
MACsumac-128-etm, hmac-sha2-{256,512}-etm, hmac-sha2-{256,512}✓ Sin MD5; hmac-sha1 aceptado por PCI v4 en modo HMAC
Host key algosssh-ed25519-cert-v01, ecdsa-sha2-nistp{256,384,521}, rsa-sha2-{256,512}✓ Sin ssh-rsa plano (SHA-1)

SSH crypto effective config

Defense-in-depth: por qué el nodo es seguro aunque PermitRootLogin yes ​

sshd -T muestra permitrootlogin yes y passwordauthentication yes (default DOKS + override de 50-cloud-init.conf). En la práctica:

  1. /root/.ssh/authorized_keys está vacío (0 bytes) — no hay clave que acepte
  2. No hay password de root en la imagen DOKS managed
  3. cde-fw filtra el puerto 22: la única regla que cubre TCP/22 es 1-65535 ← tag:compliance-scanner (la VM VAPT de ControlCase). Nadie más en internet o en la VPC puede llegar al puerto SSH

SSH defense in depth

Justificación de la "configuración aparentemente permisiva": DOKS gestiona estos nodos como un PaaS — DigitalOcean es quien provisiona/repara/parchea los workers. El cliente (Fintrixs) no necesita SSH directo para operar el cluster. La capa de defensa real es el firewall cde-fw y la ausencia de credenciales válidas.


3. cde-fw — Admin solo vía TLS 1.3 ​

El firewall NO tiene panel web propio ni protocolos administrativos vulnerables. Toda configuración se hace a través de:

MecanismoURL / ComandoCrypto
DO Web Consolehttps://cloud.digitalocean.com/networking/firewallsTLS 1.3, cert Google Trust Services WE1
DO REST APIhttps://api.digitalocean.com/v2/firewalls/{id}TLS 1.3, mismo cert
doctl CLIdoctl compute firewall ...Wrapper de la REST API, hereda TLS 1.3

Handshake TLS al endpoint de admin ​

$ openssl s_client -connect api.digitalocean.com:443 </dev/null

subject=CN=digitalocean.com
issuer=C=US, O=Google Trust Services, CN=WE1
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Protocol: TLSv1.3
Verify return code: 0 (ok)

Rechazo de HTTP plano ​

$ curl -sI http://api.digitalocean.com/v2/firewalls
HTTP/1.1 301 Moved Permanently
location: https://api.digitalocean.com/v2/firewalls

DO API TLS evidence


4. Kubernetes API — TLS 1.3 + mTLS ​

Toda administración del cluster (incluyendo aplicar manifests sobre el namespace que aloja microservicios PCI) pasa por el endpoint kubernetes.default en el puerto 16443 del control plane managed.

Handshake TLS ​

$ openssl s_client -connect 100.65.55.152:16443 -servername kubernetes.default -tls1_3

subject=C=US, ST=NY, L=New York, O=DigitalOcean, CN=kubernetes
issuer=O=DigitalOcean, CN=k8saas Cluster CA
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
Protocol: TLSv1.3

Autenticación cliente ​

  • kubectl autentica vía client certificate mTLS embebido en ~/.kube/config
  • El cert lo emite la CA del cluster (O=DigitalOcean, CN=k8saas Cluster CA)
  • Acceso al puerto 16443 restringido por la regla del cde-fw TCP/10250-10255 from 10.100.0.0/16 (solo dentro de la VPC)

K8s API TLS


5. Kong API Gateway + Microservicios PCI ​

Tráfico externo (usuarios + admin de tenants) ​

Toda llamada a payments-api, tokenization-service, card-vault-service, auth-service entra vía Kong en el puerto 443:

$ openssl s_client -connect 129.212.199.126:443 -servername api.fintrixs.com

New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Protocol: TLSv1.3
MicroservicioTipo K8sClusterIP : PuertoExposición pública directa
payments-apiClusterIP10.117.19.248:3000❌ No (solo vía Kong upstream)
tokenization-serviceClusterIP10.117.13.253:3600❌ No
card-vault-serviceClusterIP10.117.5.94:3510❌ No
auth-serviceClusterIP10.117.26.176:3700❌ No

Tráfico interno (pod ↔ pod) ​

  • Cilium CNI (eBPF) maneja el data plane con identidad por pod
  • NetworkPolicy default-deny en namespaces CDE/APP (12 policies activas — ver Q12)
  • Cifrado transparente WireGuard opcional vía Cilium (revisable en cluster config)

Puerto 80 sin rutas ​

$ curl -sI http://129.212.199.126/healthz
HTTP/1.1 404 Not Found

Kong no expone ninguna ruta vía HTTP plano — todo flujo de clientes va sobre 443.

Kong + microservices TLS


6. Kong Admin LB — Hallazgo crítico y remediación inmediata ​

⚠ Hallazgo durante la extracción Q24 — Reportado, remediado y documentado en la misma sesión (2026-05-27 09:18 UTC-5)

Antes (estado vulnerable) ​

AtributoValor
LB ID1d3fff43-7664-4501-8655-851049bc4d27 (a2cf6b88050f54003bb2daf928b823ed)
IP pública129.212.199.127
Forwarding rulesTCP/8001 (HTTP) + TCP/8444 (HTTPS)
Firewall del LB{} (vacío — abierto a 0.0.0.0/0)

Verificación del riesgo:

$ curl -sI http://129.212.199.127:8001/services
HTTP/1.1 200 OK    ← Admin API expuesta SIN auth NI TLS al mundo

Cualquier atacante podría haber listado/modificado servicios, rutas, plugins, JWT secrets de Kong vía HTTP plano.

Acción de remediación aplicada ​

bash
doctl compute load-balancer update 1d3fff43-7664-4501-8655-851049bc4d27 \
    --name "a2cf6b88050f54003bb2daf928b823ed" \
    --region nyc1 \
    --forwarding-rules "entry_protocol:tcp,entry_port:8444,target_protocol:tcp,target_port:8444" \
    --allow-list "cidr:186.30.7.141/32"

Después (estado hardened) ​

json
{
  "forwarding_rules": [
    { "entry_protocol": "tcp", "entry_port": 8444, "target_protocol": "tcp", "target_port": 8444 }
  ],
  "firewall": {
    "allow": ["cidr:186.30.7.141/32"]
  }
}
TestResultado
curl -sI http://129.212.199.127:8001/ (HTTP plano)Connection timed out after 8005 ms ✓
curl -sI -k https://129.212.199.127:8444/ (otra IP)TCP RST (firewall bloquea) ✓
curl -sk https://10.117.14.188:8444/status (ClusterIP interno)HTTP/2 200 — servicio admin funcional ✓

Kong admin remediation before/after

Recomendación pendiente ​

Convertir el Service kong-kong-admin de LoadBalancer a ClusterIP y administrar Kong únicamente vía kubectl port-forward o desde un bastion privado dentro de la VPC. Documentado en tasks/todo.md como item futuro.


7. Regresión detectada 2026-08-07 y cierre definitivo (SEC-043) ​

🔴 La remediación de mayo se revirtió sola. Detectada, analizada y cerrada de forma permanente el 2026-08-07.

7.1 Qué pasó ​

Durante la reconstrucción del inventario de assets para el scan ASV (ticket 684987 v4) se re-verificó el puerto y volvía a responder:

$ curl -sk --max-time 8 -o /dev/null -w "%{http_code}\n" http://129.212.199.127:8001/
200

$ curl -sk --max-time 8 http://129.212.199.127:8001/ | head -c 200
{"node_id":"4666227a-0934-4e9b-b214-7b0c0e6a7a7a","timers":{"running":257,"pending":1},
 "tagline":"Welcome to kong","configuration":{"prefix":"/kong_prefix",...

Puerto 8444 idéntico: HTTP 200. Es decir, el estado "hardened" documentado en §6 ya no era cierto al 2026-08-07.

7.2 Causa raíz — por qué se revirtió ​

La remediación de mayo se aplicó a nivel del Load Balancer con doctl compute load-balancer update --allow-list.

En DOKS, un LB creado por un Service type=LoadBalancer es un recurso gestionado por el cloud-controller-manager, que reconcilia continuamente el estado del LB contra la spec del Service. Como el objeto Service nunca tuvo loadBalancerSourceRanges ni las annotations equivalentes de DO, el controller consideró la allow-list manual como drift y la eliminó en la siguiente reconciliación (probablemente al re-aplicarse el chart de Helm de Kong).

Lección: en Kubernetes, cualquier hardening aplicado por fuera del objeto que lo genera es efímero. La corrección tiene que vivir en la spec del Service.

7.3 Cierre definitivo aplicado ​

Se ejecutó la recomendación que quedó pendiente en §6 — eliminar el Load Balancer por completo, no restringirlo:

bash
# Backup previo del Service
kubectl -n default get svc kong-kong-admin -o yaml \
  > backend/infra/k8s/default/kong-kong-admin-BACKUP-2026-08-07.yaml

# Fix definitivo
kubectl -n default patch svc kong-kong-admin -p '{"spec":{"type":"ClusterIP"}}'

Resultado:

$ kubectl -n default get svc kong-kong-admin
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
kong-kong-admin   ClusterIP   10.117.14.188   <none>        8001/TCP,8444/TCP   183d

DigitalOcean liberó el Load Balancer 1d3fff43-7664-4501-8655-851049bc4d27; la IP 129.212.199.127 dejó de existir.

Por qué esto no puede revertirse solo: ya no hay un LB que reconciliar. El Service es ClusterIP, que es el estado deseado declarado. Cualquier re-aplicación del chart que lo devuelva a LoadBalancer sería un cambio explícito en el values, visible en revisión de código.

7.4 Verificación posterior ​

TestResultadoEstado
curl http://129.212.199.127:8001/HTTP 000 (sin respuesta)✅
curl -k https://129.212.199.127:8444/HTTP 000 (sin respuesta)✅
kubectl port-forward svc/kong-kong-admin 18001:8001 + curl 127.0.0.1:18001/statusHTTP 200✅ admin funcional internamente
curl https://app.fintrixspay.com.co/HTTP 200✅ tráfico intacto
curl -X POST https://api.fintrixspay.com.co/auth/loginHTTP 201✅ tráfico intacto
curl https://docs.fintrixspay.com.co/HTTP 200✅

7.5 Impacto en CI/CD — ninguno ​

Los dos workflows que administran Kong ya usaban kubectl port-forward, no la IP pública:

El comentario en la línea 51 del primer workflow ya lo anticipaba: "This keeps the Admin API private; we port-forward from within the runner."

7.6 Controles de detección añadidos ​

Para que una regresión así no vuelva a pasar desapercibida entre auditorías:

ControlDescripciónEstado
Verificación en inventario ASVEl procedimiento de inventario ahora incluye probe activo de puertos administrativos sobre todas las IPs públicas✅ Aplicado (ticket 684987 v4)
Referencias en documentaciónSe corrigieron las referencias a 129.212.199.127 en .claude/skills/infrastructure-servers.md y runbooks✅ Aplicado
Alerta de servicios LoadBalancer nuevosPendiente: alerta cuando aparezca un Service type=LoadBalancer no declarado en el inventario📋 SEC-044, sprint actual

7.7 Reporte al QSA ​

Este hallazgo y su remediación se reportaron proactivamente a ControlCase en TICKET-684987-v4-ASV-assets-updated §3.1, antes de la ejecución del scan ASV.


7. PostgreSQL managed + Kafka ​

PostgreSQL — TLS 1.2+ forzado ​

$ doctl databases connection 061abee8-1d2c-49f4-be08-55f0054288cb

Host:  fintrix-production-fintrix-pci-do-user-9808531-0.h.db.ondigitalocean.com
Port:  25060             ← no es el 5432 default
SSL:   true              ← sslmode=require ENFORCED por DO
ControlValor
Puerto25060 (no estándar — reduce fingerprinting)
SSLObligatorio (sin opción de plaintext)
Cipher suitesTLS 1.2+ negociado por DO Managed
Trusted sourcesK8s cluster a19e81ac-... + CIDR etiqueta fintrix-cde (APP pool)
$ doctl databases firewalls list 061abee8-1d2c-49f4-be08-55f0054288cb
Type     Value
k8s      a19e81ac-537a-44ed-a228-8010f561a530
ip_addr  etiqueta fintrix-cde

PostgreSQL SSL required

Kafka — PLAINTEXT con control compensatorio ​

AtributoValor
ListenersPLAINTEXT://:9092, CONTROLLER://:9093
security.protocol.mapCONTROLLER:PLAINTEXT, PLAINTEXT:PLAINTEXT
Service K8sClusterIP — sin LoadBalancer ni NodePort
Acceso desde CDEOutbound cde-fw permite TCP/9092 → etiqueta fintrix-app únicamente

Justificación de negocio / control compensatorio:

  • Kafka NO entra al CDE: los eventos publicados por payments-api ya están tokenizados (ningún PAN circula por Kafka)
  • Acceso administrativo a Kafka requiere primero acceso al cluster K8s (mTLS) → segregado por NetworkPolicy
  • Microsegmentación Cilium impide acceso pod-a-pod no autorizado
  • Item planificado: migrar a SASL_SCRAM + TLS en próximo sprint (ver tasks/todo.md)

8. Auditoría de protocolos inseguros ​

VectorHallazgoConclusión
Puertos en escucha en cde-38q702ss -tlnp / -ulnp no muestra puerto 21/23/80/161/513/514✓ Ningún proceso inseguro en host
cde-fw inbound — puertos insegurosCubiertos por el rango 1-65535 allow → solo tag:compliance-scanner✓ Solo VAPT del QSA (requerido por PCI Req 11.3.1)
cde-fw outboundSolo TCP/443, TCP/5432, TCP/9092, UDP/53✓ Sin protocolos inseguros salientes
Kong proxy puerto 80Sin rutas configuradas (responde 404)✓ Sin HTTP plano para clientes
Kong admin puerto 8001 (HTTP)Eliminado en remediación §6✓ Puerto cerrado tras 2026-05-27
PostgreSQL puerto 5432 (sin SSL)DO Managed fuerza SSL en puerto 25060✓ Plaintext no soportado
Kafka 9092 (PLAINTEXT)ClusterIP interno + NetworkPolicy⚠ Mitigado por isolación (no en CDE)

Insecure protocols absent


9. Cumplimiento PCI DSS Q24 ​

RequisitoCómo se cumpleEvidencia
Listar mecanismos de admin no-consolaTabla §1 con 8 activos y sus vectores§1
Crypto fuerte (SSH/HTTPS TLS 1.2+)SSH OpenSSH 10 / TLS 1.3 en todos los endpoints públicos§2-§5
Sin SSL/TLS antiguosSolo TLS 1.3 negociado; sin SSLv2/v3, TLS 1.0/1.1Handshakes §3-§5
Documentar servicios inseguros + justificaciónKafka PLAINTEXT documentado con control compensatorio§7
Sin Telnet/FTP/HTTP planoScan §8 confirma ausencia en host y outbound§8

10. Cómo replicar la extracción ​

SSH crypto en el nodo CDE ​

bash
# Pod inspector privilegiado sobre el nodo CDE
kubectl run inspector-cde --image=nicolaka/netshoot \
  --overrides='{"spec":{"nodeSelector":{"kubernetes.io/hostname":"cde-38q702"},
                "hostNetwork":true,"hostPID":true,
                "tolerations":[{"key":"pci-scope","operator":"Equal","value":"true","effect":"NoSchedule"}],
                "containers":[{"name":"inspector","image":"nicolaka/netshoot",
                               "securityContext":{"privileged":true},
                               "command":["sleep","1800"],
                               "volumeMounts":[{"name":"host","mountPath":"/host"}]}],
                "volumes":[{"name":"host","hostPath":{"path":"/"}}]}}' --restart=Never

# Extraer config efectiva
kubectl exec inspector-cde -- chroot /host sshd -T
kubectl exec inspector-cde -- chroot /host cat /etc/ssh/sshd_config
kubectl exec inspector-cde -- chroot /host ls /etc/ssh/sshd_config.d/
kubectl exec inspector-cde -- chroot /host ls -la /root/.ssh/

# Cleanup
kubectl delete pod inspector-cde

TLS handshakes ​

bash
# DO API (admin del cde-fw)
echo | openssl s_client -connect api.digitalocean.com:443 2>&1 | \
  grep -E 'Protocol|Cipher|subject|issuer'

# K8s API
echo | openssl s_client -connect 100.65.55.152:16443 \
  -servername kubernetes.default -tls1_3

# Kong proxy
echo | openssl s_client -connect 129.212.199.126:443 \
  -servername api.fintrixs.com

PostgreSQL SSL ​

bash
DB=061abee8-1d2c-49f4-be08-55f0054288cb
doctl databases connection $DB --format Host,Port,SSL --no-header
doctl databases firewalls list $DB

11. Paquete de evidencia descargable ​

📥 Descargar evidencia completa (q24-crypto-admin-20260527.tar.gz)

Contenido del archivo:

q24-crypto-admin/
├── 00-README.txt                       ← Resumen ejecutivo
├── 01-sshd-effective-config.txt        ← sshd -T (config runtime)
├── 02-sshd-config-file.txt             ← /etc/ssh/sshd_config
├── 03-sshd-includes-list.txt           ← sshd_config.d/ entries
├── 04-sshd-includes-content.txt        ← Cloud-init overrides
├── 05-sshd-version.txt                 ← OpenSSH 10.0p2 + OpenSSL 3.5.6
├── 06-root-ssh-dir.txt                 ← /root/.ssh listing
├── 07-root-authorized-keys.txt         ← Empty (0 bytes) ✓
├── 08-running-procs.txt                ← kubelet/sshd/containerd
├── 10-openssl-version.txt              ← OpenSSL build info
├── 11-k8s-api-tls.txt                  ← K8s API TLS handshake
├── 12-do-api-tls.txt                   ← DO API + Web Console TLS
├── 13-do-api-http-rejected.txt         ← HTTP→HTTPS 301
├── 14-kong-tls.txt                     ← Kong proxy/admin TLS
├── 16-kong-admin-lb-before.json        ← Vulnerable state (BEFORE)
├── 17-all-lbs.txt                      ← DO LB inventory
├── 18-kong-admin-lb-after.json         ← Hardened state (AFTER)
├── 19-kong-admin-lb-verification.txt   ← Post-remediation checks
├── 20-test-http-blocked.txt            ← curl HTTP 8001 timeout
├── 21b-test-https-final.txt            ← curl HTTPS 8444 OK
├── 23-microservices-tls.txt            ← Microservicios ClusterIP
├── 24-postgres-ssl.txt                 ← PostgreSQL SSL + firewall
├── 25-kafka-tls.txt                    ← Kafka listeners
└── 26-insecure-protocols.txt           ← Scan FTP/Telnet/HTTP

12. Conclusión para el QSA ​

  1. cde-pool-node-01 (cde-38q702) — Acceso admin SSH usa OpenSSH 10 con cipher suites AES-GCM/ChaCha20-Poly1305 y KEX híbrido post-quantum. Defensa en profundidad: firewall restringe a VAPT tag, no hay keys autorizadas, no hay password de root.
  2. cde-fw — Solo administrable vía TLS 1.3 (DO Web Console, REST API, doctl). HTTP plano se redirige forzadamente a HTTPS.
  3. Microservicios del alcance — payments-api, tokenization-service, card-vault-service, auth-service son ClusterIP intra-cluster, acceso externo solo vía Kong proxy TLS 1.3 + AES-256-GCM-SHA384.
  4. PostgreSQL — SSL forzado por DO Managed en puerto 25060 con trusted sources whitelist.
  5. Kafka — PLAINTEXT pero aislado vía ClusterIP + NetworkPolicy + outbound restringido en cde-fw; no transporta cardholder data (control compensatorio documentado).
  6. Hallazgo de la sesión: Kong Admin LB exponía HTTP plano (puerto 8001) sin autenticación. Remediado en la misma sesión restringiendo a un único CIDR de admin y eliminando el puerto plano. Evidencia before/after en §6.

Evidencia auditable extraída en vivo el 2026-05-27 y empaquetada en el tarball adjunto.


Historial de revisiones ​

FechaRevisorCambios
2026-05-27Gabriel Ureña (CTO Fintrixs)Creación inicial — respuesta a solicitud QSA Q24 + remediación Kong Admin LB

Documentación Confidencial — Solo para uso interno y auditoría PCI DSS