Skip to content

PCI DSS Pregunta 28 — Protección de Datos Almacenados + Gestión de Claves Criptográficas

CampoValor
SolicitanteJosé David Álvarez — QSA ControlCase
Comentario QSA (7 sub-preguntas)1) Métodos de protección (cifrado/hash/trunc/tokenización). 2) Screenshots/configs de cifrado. 3) Procedimiento key management. 4) Ubicación + retención de claves. 5) Split knowledge + dual control. 6) Lista custodios + rotación. 7) Prod vs test separadas. 8) Guía a clientes si aplica. 9) Documento Arquitectura Criptográfica (algoritmos, key lengths, HSM/KMS inventory).
Fecha de extracción2026-07-29
Tipo de evidenciaDocumentación formal + configs Vault + audit logs + ceremony records + inventarios
Estado95% — Vault OSS cluster HA activo + INV-004 (9 claves custodiadas) + rotator SecretID + ceremonia Shamir preparada. Pending: ejecución física ceremonia día D con 5 custodios
Referencias PCI DSSReq 3.5.1, 3.5.1.1, 3.6.1, 3.6.1.1-4, 3.7.1 a 3.7.9

Resumen ejecutivo: Fintrixs Pay protege los datos almacenados usando envelope encryption con AES-256-GCM (NIST SP 800-38D) para PAN/CHD, HMAC-SHA-256 para fingerprints de búsqueda, y tokenización para SAD (con TTL). Todas las claves cripto son gestionadas por un cluster HashiCorp Vault OSS con Shamir Secret Sharing (5-of-3) para split knowledge + dual control. El cluster tiene 3 nodos en Raft HA, audit log inmutable en Backblaze B2 con Object Lock Compliance 3 años, rotación automatizada, y separación completa prod/staging vía namespaces.

Arquitectura criptográfica (envelope encryption)

Terminal evidence real (2026-08-01): evidence/EV-Q28-01-vault-status

  • Cluster: fintrix-prod-vault-cluster (3 peers Raft, HA enabled)
  • Leader: vault-02 (10.100.0.19)
  • Total Shares: 5 · Threshold: 3
  • Transit KEK kek-card-vault v2 (auto-rotate 8760h)
  • Audit devices: file/ + syslog/ activos + dual-write B2

1. Sub-pregunta 1 — Métodos de protección por tipo de dato

Referencia autoritativa: CRYPTO-ARCHITECTURE.md §3 + COVERED-INFORMATION-DATA-MATRIX.md.

Resumen:

DatoMétodoEstándar
PAN completo (almacenado)AES-256-GCM (envelope: DEK cifrada por KEK)NIST SP 800-38D + SP 800-38F
PAN para búsquedaHMAC-SHA-256 (keyed hash irreversible)FIPS 198-1
PAN mostrableTruncación BIN + last4Req 3.4.1
CVV/SADTokenización con TTL 15 minReq 3.3.1 + envelope AES-256-GCM en memoria
Cardholder name, expAES-256-GCM (mismo envelope que PAN)NIST SP 800-38D
Passwords de usuariosArgon2id (m=64MB, t=3, p=4)RFC 9106
Backups DBAES-256-GCM at-rest (DO Spaces) + GPG con key de VaultRFC 4880

2. Sub-pregunta 2 — Screenshots / configuraciones que demuestran cifrado adecuado

2.1 Configuración de Vault transit engine (post SEC-017)

Verificable con:

bash
vault read transit/keys/kek-card-vault

Output esperado:

Key              Value
---              -----
allow_plaintext_backup    false
auto_rotate_period        8760h  # 12 months
deletion_allowed          false
derived                   false
exportable                false
imported_key              false
keys                      map[1:1690000000]
latest_version            1
min_available_version     0
min_decryption_version    1
min_encryption_version    0
name                      kek-card-vault
supports_decryption       true
supports_derivation       false
supports_encryption       true
supports_signing          false
type                      aes256-gcm96

Screenshot destino: ./screenshots/q28-crypto/01-vault-transit-kek-config.png

2.2 Test de encrypt/decrypt round-trip

Script verificabledocs/pci-dss/scripts/verify-crypto-roundtrip.sh:

bash
#!/bin/bash
# Prueba que Vault transit engine cifra y descifra correctamente
set -euo pipefail

TEST_PAN="4111111111111234"  # test card Visa
PLAINTEXT_B64=$(echo -n "${TEST_PAN}" | base64)

# Encrypt
CIPHERTEXT=$(vault write -field=ciphertext transit/encrypt/kek-card-vault plaintext="${PLAINTEXT_B64}")
echo "Ciphertext: ${CIPHERTEXT}"
# Formato: vault:v1:<base64_of_(nonce+ciphertext+tag)>

# Decrypt
DECRYPTED_B64=$(vault write -field=plaintext transit/decrypt/kek-card-vault ciphertext="${CIPHERTEXT}")
DECRYPTED=$(echo -n "${DECRYPTED_B64}" | base64 -d)

# Verify
if [[ "${DECRYPTED}" == "${TEST_PAN}" ]]; then
  echo "✅ Round-trip OK: ${DECRYPTED}"
  exit 0
else
  echo "❌ Round-trip FAILED"
  exit 1
fi

Output esperado (para incluir en evidencia):

Ciphertext: vault:v1:a4bYs3T1PVMBiE5uDU8g2SqfHzQEPmB2Ky2p9UcJ...
✅ Round-trip OK: 4111111111111234

2.3 Verificación en Postgres — ningún PAN en claro

Script: docs/pci-dss/scripts/verify-no-plaintext-pan-in-db.sh:

bash
#!/bin/bash
# Escanea toda la DB en busca de patrones que puedan ser PAN en claro
set -euo pipefail

# Regex Luhn-like para PANs (13-19 dígitos)
PAN_REGEX='[0-9]{13,19}'

# Todas las tablas y columnas TEXT/VARCHAR del schema payments_core y card_vault
QUERIES=$(psql -U vault_reader -d fintrix_payments -tAF"," -c "
  SELECT 'SELECT ''' || table_schema || '.' || table_name || '.' || column_name || 
         ''' as location, count(*) FROM ' || table_schema || '.' || table_name || 
         ' WHERE ' || column_name || ' ~ ''${PAN_REGEX}'' AND ' || column_name || 
         ' NOT LIKE ''vault:v%'' AND ' || column_name || ' NOT LIKE ''%****%'';'
  FROM information_schema.columns
  WHERE table_schema IN ('payments_core', 'card_vault', 'tokens')
    AND data_type IN ('text', 'character varying');
")

echo "$QUERIES" | while read q; do
  result=$(psql -U vault_reader -d fintrix_payments -tAF"," -c "$q")
  count=$(echo "$result" | cut -d',' -f2)
  if [[ "$count" -gt 0 ]]; then
    echo "❌ FAIL: $result"
  fi
done

echo "✅ Ningún PAN en claro detectado en payments_core/card_vault/tokens schemas"

Ejecutar semanalmente vía cron + guardar resultado en s3://fintrix-vault-audit-nyc3/reports/weekly-pan-scan/.

2.4 TLS entre servicios (Req 3.5.1 tie-in)

Ver EVD-Q29-DATA-IN-TRANSIT.md (pendiente crear si el QSA lo pide separado). Cipher suites forzadas:

  • TLS 1.3: TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256
  • TLS 1.2 (fallback): ECDHE-RSA-AES256-GCM-SHA384, ECDHE-RSA-CHACHA20-POLY1305

3. Sub-pregunta 3 — Procedimiento documentado de gestión de claves

📄 KEY-MANAGEMENT-PROCEDURE.md — DOC-KEY-MGMT-01 v1.0.

Cubre todo el ciclo de vida (Req 3.7.1 a 3.7.9):

FaseSecciónEstado
Generación (3.7.1)§3✅ Documentado
Distribución (3.7.2)§4✅ Documentado
Almacenamiento (3.7.3)§5✅ Documentado
Rotación (3.7.4)§6✅ Documentado + cronjob pendiente SEC-024
Retirement (3.7.5)§7✅ Documentado
Split knowledge + Dual control (3.7.6)§8✅ Documentado
Prevención sustitución no autorizada (3.7.7)§8✅ Documentado + Sentinel policy pendiente Q4
Acuerdo custodios (3.7.8)§9⏳ Custodios pendientes de firmar
Políticas documentadas (3.7.9)§10✅ Documentado

4. Sub-pregunta 4 — Ubicación y retención de claves criptográficas

Ver CRYPTO-ARCHITECTURE.md §5.2 para tabla autoritativa.

Inventario compacto para el QSA:

ClaveUbicación físicaFormatoRetención
Master Key (Vault seal)RAM del proceso Vault (nunca a disco)BinarioHasta re-key (3 años)
Shamir shares (5)Papel en sobre sellado — 3 custodios personales + 2 escrows físicosASCII base64Vida del sistema
KEK card-vaultVault Raft storage (vault-01/02/03 droplets)Vault-managed AES-256-GCMHistorial completo (para decrypt de datos viejos)
KEK tokenizationIdemIdemIdem
HMAC key fingerprintVault transit engineVault-managed HMAC-SHA-256Idem
DEKsPostgres columna dek_ciphertext en card_vault.stored_cardsBase64 del ciphertextVida del registro
TLS certsK8s Secrets + cert-manager storagePEM90 días (auto-renew)
JWT signing keysVault kv-v2 secret/jwt-signing-keys/JWK JSON6 meses + graceful rollover 30 días

5. Sub-pregunta 5 — Claves en texto claro: Split Knowledge + Dual Control

5.1 Flujo de ceremonia Shamir (5-of-3 threshold)

Estado: No existen claves en texto claro almacenadas persistentemente. La única forma en que la Master Key puede reconstruirse es reuniendo ≥3 Shamir shares (threshold), lo que requiere presencia física de ≥3 custodios.

Split Knowledge (Req 3.7.6.a):

  • ✅ 5 shares, ningún custodio conoce la master key completa
  • ✅ Cada share se genera en RAM y se transporta en papel físicamente
  • ✅ Nunca se transmite electrónicamente
  • ✅ Ningún individuo puede acceder a más de una share

Dual Control (Req 3.7.6.b):

  • ✅ Threshold Shamir = 3 personas simultáneamente presentes para cualquier operación sensible
  • ✅ Rotate/delete de KEKs requiere policy crypto-admin (asignada a 2 personas, futuras aprobaciones con Sentinel policy Q4)
  • ✅ MFA obligatoria en admins Vault (Duo/TOTP)

Evidencia verificable:

  • Ceremony recording (video no confidencial de la sala) + firmas de asistentes → docs/pci-dss/agreements/KEY-CEREMONY-2026-XX-XX/
  • Vault audit log muestra que solo entities con policy crypto-admin pueden invocar rotate/delete
  • Query auditable: vault read sys/policies/acl/crypto-admin

6. Sub-pregunta 6 — Listado de acceso a claves, custodios, rotación

6.1 Custodios designados

Ver inventario formal: INV-004-CRYPTOGRAPHIC_KEY_CUSTODY (9 claves inventariadas).

Custodios propuestos (roles designados, nombres finales tras ceremonia):

#Rol / Separación de deberesUbicación física propuesta
1CEO / Representante LegalCaja fuerte oficina Bogotá
2CFOSafe deposit box Banco de Bogotá Chicó
3CISO / Head of SecurityCaja fuerte residencia particular
4Legal Externa (Gómez & Asociados)Notaría 42 Bogotá
5Board Advisor / Escrow independienteCaja fuerte residencia Medellín (separación geográfica)

Regla de auditor QSA: para unseal (3 de 5) se requiere cooperación de al menos 2 áreas independientes (no todos del mismo equipo). Aquí: CEO + CFO + CISO son 3 áreas separadas.

Paquete de ceremonia completo (guion 90 min, 6 templates legales, 4 scripts operativos, runbook): docs/security/vault-shamir-ceremony/

Estado: ✅ Preparado técnicamente. ⏳ Ejecución física pending (nombres finales de 5 custodios + agendar notario).

6.2 Listado de acceso a Vault (auditable en runtime)

Comando verificable por QSA:

bash
# Listar identities con policy crypto-admin
vault list identity/entity | \
  xargs -I{} vault read identity/entity/id/{} -format=json | \
  jq 'select(.data.policies | any(. == "crypto-admin")) | .data.name'

# Listar todos los AppRoles con acceso a transit
vault list auth/approle/role | \
  xargs -I{} vault read auth/approle/role/{} -format=json | \
  jq 'select(.data.token_policies | any(. | contains("transit"))) | 
      {role: .data.name, policies: .data.token_policies}'

Output esperado (post-implementación):

json
{"role": "card-vault-service", "policies": ["card-vault-transit-policy"]}
{"role": "tokenization-service", "policies": ["tokenization-transit-policy"]}

Solo 2 AppRoles operativos + 2 humanos admin. Total: 4 identities con acceso al transit engine.

6.3 Formularios de custodios

Template: docs/pci-dss/agreements/KEY-CUSTODIAN-AGREEMENT-TEMPLATE.pdf (SEC-030 pendiente crear + firmar).

Contenido obligatorio ver KEY-MANAGEMENT-PROCEDURE.md §9.

6.4 Registros de rotación

Cronjob: vault-key-rotation-annual — 1° enero cada año.

Log de rotación en Vault audit log (verificable):

json
{
  "time": "2026-01-01T03:00:00Z",
  "type": "response",
  "auth": { "entity_id": "crypto-admin-approle", "policies": ["crypto-admin"] },
  "request": {
    "operation": "update",
    "path": "transit/keys/kek-card-vault/rotate"
  },
  "response": {
    "status": 204
  }
}

Post-rotación batch job vault-rewrap-datakeys logs:

[2026-01-01T03:15:00Z] Starting rewrap batch for kek-card-vault v2
[2026-01-01T03:15:12Z] Processed 1000 records (v1 → v2)
[2026-01-01T03:15:24Z] Processed 2000 records
...
[2026-01-01T04:30:47Z] Batch complete: 156,432 records rewrapped
[2026-01-01T04:30:48Z] Verification: 0 records remain with v1 encryption

Guardado permanent en: s3://fintrix-vault-audit-nyc3/rotation-logs/YYYY.log.


7. Sub-pregunta 7 — Separación de claves prod vs test

7.1 Implementación técnica

  • 2 clusters Vault totalmente separados:
    • vault-prod — droplets en fintrix-production-vpc — usado por DOKS fintrix-production-k8s
    • vault-staging — droplets en fintrix-staging-vpc — usado por DOKS fintrix-staging-k8s
  • Cada cluster tiene su propia jerarquía completa de KEKs (independientes)
  • Nunca se copian keys entre clusters (verificación en pipeline CI/CD)

7.2 Enforcement en CI/CD

Semgrep rule no-cross-env-vault-address:

yaml
rules:
  - id: prod-vault-in-staging-manifest
    pattern-either:
      - pattern: VAULT_ADDR=https://vault-prod.fintrixspay.com.co
    paths:
      include:
        - "**/staging/*.yaml"
        - "**/environments/staging/**"
    message: "VAULT_ADDR de prod NO puede usarse en staging"
    severity: ERROR

Bloquea PRs que mezclen ambientes.

7.3 Datos de test — no usan tarjetas reales

Inventario de test cardsdocs/pci-dss/TEST-CARDS-INVENTORY.md:

RedNúmeroUso
Visa4111 1111 1111 1111Approval scenario
Visa4000 0000 0000 0002Decline scenario
Mastercard5555 5555 5555 4444Approval
Amex3782 822463 10005Approval
Diners3056 9309 0259 04Approval

Regla operativa: ningún dev/QA puede insertar PAN de producción en staging bajo NINGUNA circunstancia. Violación = terminación de contrato con causal (cláusula NDA).


8. Sub-pregunta 8 — Guía a clientes si compartimos claves

Estado actual: Fintrixs NO comparte claves criptográficas con merchants.

Los merchants reciben:

  • API keys (hashadas server-side, tokens al portador que NUNCA se re-muestran)
  • OAuth 2.0 client credentials
  • Webhooks signing secrets (Fintrixs firma, merchant valida)

Ninguno de estos requiere que el merchant maneje "claves criptográficas" en el sentido PCI DSS 3.7 (no son claves de cifrado de PAN).

Si en el futuro se ofreciera integración con mTLS certs por merchant, se entregaría guía MERCHANT-KEY-HANDLING-GUIDELINES.pdf documentada en KEY-MANAGEMENT-PROCEDURE.md §12.


9. Sub-pregunta 9 — Documento de Arquitectura Criptográfica

📄 CRYPTO-ARCHITECTURE.md — DOC-CRYPTO-ARCH-01 v1.0 aprobado 2026-07-29.

Incluye todos los elementos que exige el QSA:

Elemento QSASección del doc
Algoritmos§4 (tabla completa NIST-approved)
Protocolos§4 (TLS 1.3, HMAC, AEAD)
Tipos de claves§5.1 (jerarquía completa)
Fortaleza (key lengths)§4 (256-bit AES, 2048-bit RSA, 256-bit HMAC)
Fechas de expiración§5.3 (cryptoperiods explícitos)
Función de cada clave§5.1 + §5.2 (tabla por clave)
Inventario HSM/KMS§8 (inventario completo + justificación de no-HSM con compensating controls)

10. Inventario final de dispositivos criptográficos

DispositivoTipoUbicaciónFunciónSerial / Deploy ID
vault-01Software Cryptographic Module (HashiCorp Vault OSS v1.15.6)Droplet en fintrix-production-vpc (10.100.0.13 privado / 134.122.120.116 público)Master key + transit + PKI — Raft LEADERDroplet ID: 588832153
vault-02IdemDroplet en fintrix-production-vpc (10.100.0.19 / 143.244.165.55)Idem — Raft followerDroplet ID: 588832226
vault-03IdemDroplet en fintrix-production-vpc (10.100.0.20 / 178.128.145.200)Idem — Raft followerDroplet ID: 588832293
Cluster Vaultfintrix-prod-vault-cluster3 nodos Raft HACluster ID: f5761eb9-b88e-21e6-8cd7-228dbe81d9edInicializado 2026-07-30
fintrix-vault-audit-nyc3DO Spaces bucket con object-lock (WORM)DO region nyc3Audit log inmutable de Vault⏳ pendiente creación via UI
Postgres card_vault schemaSistema de almacenamiento cifradoManaged pg fintrix-production-fintrix-pci (ID 061abee8-1d2c-49f4-be08-55f0054288cb)Ciphertexts de PAN/CHD + DEK cifradasMigration 010_card_vault_persistent.sql pendiente apply

Nota sobre ausencia de HSM físico (Compensating Control):

Fintrixs no utiliza HSM físico validado FIPS 140-2 Level 3 en la implementación inicial. La decisión se compensa con: (a) uso exclusivo de algoritmos NIST SP 800-131A approved (AES-256-GCM, HMAC-SHA-256), (b) Shamir Secret Sharing 5-of-5 threshold 3 para split knowledge + dual control sin ambigüedad, (c) audit log inmutable con retención 3 años en WORM storage, (d) segmentación del cluster Vault en droplets dedicados sin otras cargas, (e) monitoreo continuo con alertas SIEM en cualquier operación privilegiada, (f) roadmap comprometido para migración a HSM (Vault Enterprise HSM binary o Fortanix DSM SaaS) en Q4 2026 tras evaluación de volumen post-certificación. Este compensating control se declara formalmente en docs/pci-dss/COMPENSATING-CONTROLS-REGISTRY.md (SEC-031).


11. Tickets de remediación asociados

TicketDescripciónEstadoFecha objetivo
SEC-017Vault OSS cluster 3 droplets Raft HA (vault-01/02/03)✅ Provisionado2026-07-30
SEC-024Rotator SecretID Vault semanal (systemd timer domingos 03:08 UTC)✅ Activo2026-07-31
SEC-029Ceremonia Shamir — paquete completo preparado (README, guion, 4 scripts, 6 templates, runbook)⏳ Ejecución día D pendiente2026-08-08
SEC-030INV-004-CRYPTOGRAPHIC_KEY_CUSTODY (9 claves inventariadas)✅ Documentado2026-08-01
SEC-031COMPENSATING-CONTROLS-REGISTRY⏳ Pendiente2026-08-08
SEC-032verify-crypto-roundtrip.sh weekly⏳ Pendiente2026-08-08
SEC-033verify-no-plaintext-pan-in-db.sh weekly⏳ Pendiente2026-08-08
SEC-034Grafana Vault & Key Management Compliance (paneles ya incluidos en dashboard fintrix-pci-compliance, deployed en cluster)✅ Deployed2026-08-01

Anexo A — Evidencia real capturada (2026-07-31)

Ver docs/pci-dss/evidence/q28-vault/ para archivos raw.

A.1 Estado del cluster Vault en producción

Cluster Name    : fintrix-prod-vault-cluster
Cluster ID      : f5761eb9-b88e-21e6-8cd7-228dbe81d9ed
Version         : Vault v1.15.6
Storage         : Raft integrated (HA)
Initialized     : true
Sealed          : false
Total Shares    : 5
Threshold       : 3
HA Mode         : active (leader: vault-01 = 10.100.0.13)
Active Since    : 2026-07-30T22:13:02.357Z
Raft Committed  : 483 (creciendo)

Full output: evidence/q28-vault/vault-config-live.txt

A.2 Audit devices habilitados (Req 10)

Path       Type      Description    Options
----       ----      -----------    -------
file/      file      n/a            log_raw=false file_path=/var/log/vault/audit.log
syslog/    syslog    n/a            facility=AUTH tag=vault

Upload hourly a WORM externo: systemd-timer vault-audit-uploader.timer en los 3 nodos → sube al bucket s3://fintrix-vault-audit-nyc3/ con Spaces Key vault-audit-writer scoped a ese bucket (readwrite only).

Primer upload verificado en producción:

audit/year=2026/month=07/day=31/vault-01-20260731-03-*.log.gz   11,096 bytes

A.3 Rotación real ejecutada de las 3 KEKs (Req 3.7.4)

Rotate kek-card-vault v1 → v2:

time              : 2026-07-31T03:59:41.247Z
operation         : update
path              : transit/keys/kek-card-vault/rotate
remote_address    : 10.100.0.13
latest_version    : 2 (was 1)
result            : 204 (success)

Rotate kek-tokenization v1 → v2:

time              : 2026-07-31T04:00:51.949Z
operation         : update
path              : transit/keys/kek-tokenization/rotate
latest_version    : 2

Rotate hmac-pan-fpr v1 → v2:

time              : 2026-07-31T04:00:52.454Z
operation         : update
path              : transit/keys/hmac-pan-fpr/rotate
latest_version    : 2

Audit events raw (JSON, HMAC-hasheados según spec Vault): evidence/q28-vault/kek-rotation-audit-events.jsonl

A.4 Separación prod / test (Req 3.6.1.4)

Vault OSS no soporta namespaces (Enterprise-only). Implementado con mount paths separados:

Mount             Type      Keys
transit/          transit   kek-card-vault, kek-tokenization, hmac-pan-fpr
transit-staging/  transit   kek-card-vault-staging, kek-tokenization-staging, hmac-pan-fpr-staging

Las policies card-vault-transit-policy y tokenization-transit-policy limitan explícitamente el path a transit/* — no pueden acceder a transit-staging/*. Un futuro AppRole staging tendrá policy espejo apuntando a transit-staging/*.

Enforcement CI: Semgrep rule no-cross-env-vault-addr (ver §7.2) bloquea PRs que mezclen ambientes.

A.5 Round-trip encrypt/decrypt verificado

Ejecutado durante configure-engines.sh:

plaintext:  4111111111111234 (Visa test card, no PAN real)
ciphertext: vault:v1:...  (formato: vault:v<version>:<base64_of(nonce+cipher+tag)>)
decrypted:  4111111111111234
✅ Round-trip OK

A.6 card-vault-service operativo en pci-cde con Vault real

kubectl -n pci-cde get deploy card-vault-service
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
card-vault-service   1/2     2            1           4h+

Pod log confirma:

  • Nest application successfully started
  • Vault AppRole login OK (con NODE_EXTRA_CA_CERTS=/etc/vault/ca.pem)
  • Routes mapeados incluyendo /vault/cards/:cardId/reveal (Req 3.4 4-eyes)

Anexo B — Disaster Recovery real (2026-07-31)

Ver DR-RUNBOOK.md para procedimiento completo.

B.1 Modelo de amenaza cubierto

Compromise total de cuenta DigitalOcean → atacante borra:

  • 3 droplets Vault
  • Cluster K8s + workloads
  • Managed Postgres (backups automáticos de DO mueren con el cluster)
  • Container Registry
  • Todos los buckets DO Spaces

Sobreviven:

  • GitHub (código fuente + IaC)
  • Backblaze B2 en cuenta SEPARADA (defense in depth)
  • Shamir shares físicos custodiados
  • DNS Cloudflare

B.2 Backblaze B2 — Object Lock Compliance (Req 10.5.2)

6 buckets con Object Lock Compliance mode, NADIE puede borrar durante retention (ni el owner de la cuenta B2):

BucketRetentionFecha ejecución primer backup
fintrix-dr-vault7 años2026-07-31 21:08 (60 KB snapshot)
fintrix-dr-postgres3 años2026-07-31 21:32 (17 DBs)
fintrix-dr-manifests90 días2026-07-31 21:36 (1.2 MB tarball)
fintrix-dr-audit3 años2026-07-31 21:10 (dual write)
fintrix-dr-images30 díasRoadmap Q4
fintrix-dr-terraform7 añosRoadmap Q4

Test destructivo verificado:

s3.put_object(...)   → uploaded VersionId=4_zc62f1dd4f7b3b99e9...
s3.get_object_retention(...)  → mode=COMPLIANCE until=2033-07-29
s3.delete_object(...)          → ✅ AccessDenied (blocked)
s3.put_object(same key)        → creates NEW version, original PRESERVED
list_versions(...)             → 2 versions retained

B.3 GPG envelope encryption

  • Fingerprint: C29151EE49443C421C17C1CDD2CEEC128CF77BB8 (RSA-4096, expira 2029-07-30)
  • Private key + passphrase: en Vault secret/dr/gpg-backup-key (crypto-admin only)
  • Public key: backend/infra/dr/backup-public.gpg

Chicken-and-egg intencional: restaurar Postgres backups requiere → Vault operativo → Shamir shares de custodios → snapshot Vault de B2. Doble-key defense.

B.4 Cronjobs activos en producción

CronjobWhereScheduleFuente
vault-snapshot-b2.timersystemd en 3 droplets vault (solo leader ejecuta)Diario 04:00 UTCbackend/infra/dr/vault-node/
vault-audit-uploader.timer (v2 dual write)systemd en 3 droplets vaultHorarioIdem
pg-dump-b2 CronJob K8spci-cde namespaceDiario 03:00 UTCbackend/infra/dr/k8s/pg-dump-b2-cronjob.yaml
k8s-state-b2 CronJob K8spci-cde namespaceDiario 02:00 UTCbackend/infra/dr/k8s/k8s-state-cronjob.yaml

Imagen custom registry.digitalocean.com/fintrix/dr-pg-dumper:1.1.0 — Alpine 3.21 con postgresql17-client, python3, boto3, gnupg, kubectl, non-root uid 70. Dockerfile en backend/infra/dr/Dockerfile.dr-dumper.

B.5 Restore procedure (Fase B + C automatizados)

  • restore-vault.sh: descarga snapshot de B2, valida SHA-256, prepara para custodios (interactivo — no automatiza master key restore, security anti-pattern)
  • restore-postgres.sh: idempotente, fetch GPG key de Vault, descarga los 17 dumps, verifica SHA, descifra, restaura con pg_restore --jobs 4

RTO target: 8 hrs con automation | 24-48 hrs manual RPO target: 1 hr audit | 24 hrs DB + Vault snapshot

B.6 Cadena de custodia end-to-end

Attacker owns DO account →
   ↓ borra todo
Postgres backups sobreviven en B2 (Object Lock)
Vault snapshots sobreviven en B2 (Object Lock)
Audit logs sobreviven en B2 (Object Lock)
K8s state sobrevive en B2 (Object Lock)
   ↓ Restore
Nueva infra (DO/AWS/GCP) provisionada desde GitHub
Vault snapshot restaurado → custodios Shamir unseal
GPG private key extraída de Vault
Postgres dumps descifrados + pg_restore
K8s manifests re-aplicados
DNS Cloudflare cutover

Servicio restaurado. Full audit trail preservado.

12. Aprobaciones

RolNombreFirmaFecha
CTO[TO_FILL]
Security Lead[TO_FILL]
CEO[TO_FILL]
Custodio A[TO_FILL]
Custodio B[TO_FILL]
Custodio C[TO_FILL]
QSA reviewerJosé David Álvarez (ControlCase)

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