Casos reales, errores reales

Cada proyecto cuenta el reto, la arquitectura, mi rol y los problemas que tuve que resolver en el camino. Los errores enseñan más que los diagramas bonitos.

Freelance En desarrollo · fase 1 Oracle Cloud Infraestructura

Del cuaderno a la nube: plataforma para controlar ~3,500 máquinas tragamonedas

Un casino registraba cada corte de sus máquinas a mano, en hojas de cuaderno: SK-ID, ubicación, quién hizo el corte, el monto, el día y las ganancias. Consolidar esos datos era lento, los errores de captura eran inevitables y no había forma sencilla de auditar quién hizo qué. Con mi equipo estamos construyendo una plataforma en Oracle Cloud que captura esa información de forma digital y trazable.

Mi rol

Infraestructura y administración de sistemas. El backend y el frontend los desarrolla el resto del equipo.

  • Provisioning de la VM y de la red (VCN, subnets, gateways) en OCI
  • Instalación y actualización del runtime: Java 21 y Tomcat 11
  • Usuarios y permisos en Linux; firewall de host con iptables
  • Base de datos: usuarios con mínimo privilegio, wallet mTLS y allowlist de IPs
  • Despliegue de artefactos WAR y diagnóstico de fallas

Qué registra la plataforma

  • SK-ID de cada máquina
  • Ubicación dentro del casino
  • Responsable del corte
  • Monto, fecha y ganancias

Resultado buscado: captura en tiempo real, menos error humano y trazabilidad completa.

Arquitectura

← desliza para ver el diagrama →
Generic architecture of the slot-machine control platform on Oracle Cloud Infrastructure Operadores de corte y gerencia ~3,500 máquinas Oracle Cloud Infrastructure Always Free Tier VCN Security Lists · IGW · NAT IGW Subnet pública VM · Ubuntu 22.04 Tomcat 11 · Spring Boot (WAR) · Java 21 iptables · usuarios con mínimo privilegio BD de desarrollo · Oracle XE 21 en Docker escucha solo en localhost · volumen persistente Subnet privada workers websocket diseñado para escalar PROD Autonomous DB Transaction Processing mTLS · wallet IP allowlist backups gestionados mTLS ojdbc11
Vista genérica: se omiten región, nombres de recursos y puertos a propósito.

Decisiones de diseño

Simple a propósito

Una VM y una base de datos gestionada, dentro del Always Free Tier. El diseño contempla tres VMs (app, workers y websocket); hoy corre una y las otras se agregan cuando el proyecto escale.

Producción separada de desarrollo

Evaluamos montar la BD dentro de la VM contra usar Autonomous Database. Elegimos Autonomous para producción, por los backups gestionados y el aislamiento de fallos, y un contenedor Oracle XE solo en localhost para desarrollo, sin exposición a internet.

Seguridad por capas

Subnets pública y privada, Security Lists con ingress mínimo, firewall persistente en el host, conexión a la BD con mTLS y wallet, acceso restringido por IP y usuarios de BD con el mínimo privilegio.

Problemas que resolví

ORA-12263 · "el archivo no existe" (pero sí existía)

La app no podía abrir el wallet de la base de datos. Los certificados eran propiedad de un usuario distinto al que ejecuta el servicio (tomcat), y Oracle reporta ese problema de permisos, de forma engañosa, como un archivo inexistente. Solución: corregir el dueño y los permisos del wallet para el usuario del servicio.

Despliegue "exitoso" en 2 segundos… que nunca arrancó

El WAR se desplegaba sin error visible, pero la app no respondía. Eran dos incompatibilidades apiladas: Spring Framework 7 requiere Servlet 6.1 (Tomcat 11) y el servidor tenía Tomcat 10.1 (Servlet 6.0); además, el bytecode compilado con Java 21 no lo podía leer la JVM 17 instalada. La pista: un arranque real de Spring toma ~10 s, no 2. Solución: actualizar a Tomcat 11 y Java 21.

Stack

OCI ComputeVCN · IGW · NATAutonomous DBUbuntu 22.04iptablesDockerOracle XE 21Tomcat 11Java 21Spring Boot 4.1Hibernate 7.4HikariCPojdbc11

Impacto

Captura digital

Los cortes dejan el cuaderno y se registran en un sistema central.

Trazabilidad

Cada corte queda ligado a una máquina, una ubicación y un responsable.

Menos error humano

La captura en tiempo real elimina la transcripción de cuadernos a hojas de cálculo.

Cliente 100% satisfecho con el trabajo realizado hasta ahora. Estamos midiendo el antes y el después (tiempo por corte, errores de captura y tiempo de consolidación) para publicar cifras reales.

En producción Self-hosting DevSecOps

hack-core.com: self-hosting seguro en una Raspberry Pi 4

Quería publicar mi sitio desde casa, con HTTPS de punta a punta y sin exponer mi red. El problema: mi ISP residencial bloquea los puertos entrantes 80 y 443. La solución fue sacar el tráfico por un túnel saliente y cifrar cada salto.

← desliza para ver el diagrama →
hack-core.com architecture: visitor to Cloudflare edge, outbound tunnel to cloudflared on a Raspberry Pi, then HTTPS to Nginx Visitante browser Cloudflare Edge DDoS · TLS 1.3 · CDN Zero Trust Tunnel HTTPS Sin port forwarding el tráfico entra por el túnel Raspberry Pi 4 · Docker Compose red bridge interna 172.20.0.0/24 · sin puertos publicados Tunnel saliente · QUIC cloudflared 4 conexiones nginx:443 Let's Encrypt · HTTP/2 CSP · HSTS · headers cap_drop: ALL HTTPS certbot chequeo c/12 h · DNS-01 certs Cloudflare API · DNS TXT
El navegador habla TLS con Cloudflare; Cloudflare llega al Pi por un túnel saliente; cloudflared habla HTTPS con Nginx.

Tres contenedores

Docker Compose con Nginx (Alpine, cap_drop: ALL, volúmenes de solo lectura), Certbot con el plugin DNS de Cloudflare y cloudflared. Ningún puerto publicado en el host.

Certificados sin puerto 80

El challenge HTTP de Let's Encrypt fallaba por el bloqueo del ISP. Lo cambié a DNS-01: Certbot crea un registro TXT vía la API de Cloudflare y revisa la renovación cada 12 horas.

Headers de seguridad

HSTS, CSP restrictiva, X-Frame-Options, Referrer-Policy y Permissions-Policy, además de bloqueo de user agents de escáneres y de archivos sensibles.

Problemas que resolví

nginx: [emerg] "ssl_prefer_server_ciphers" directive is duplicate

El archivo options-ssl-nginx.conf de Certbot ya define protocolos, ciphers y caché de sesión. Mi config los repetía y Nginx no arrancaba. Lo resolví dejando esas directivas solo en el include.

502 · x509: certificate is valid for hack-core.com, not nginx

cloudflared se conecta al contenedor por su nombre interno, que no coincide con el certificado. Activé No TLS Verify solo en ese salto interno: el tráfico sigue cifrado dentro de la red de Docker.

403 Forbidden · drwx------

La carpeta del sitio tenía permisos 700 y Nginx, que corre con otro usuario dentro del contenedor, no podía leerla. Solución: chmod 755 al directorio.

Error 1033 · lookup on 127.0.0.11:53: server misbehaving

Meses después, el DNS embebido de Docker dejó de resolver los hosts de Cloudflare y el túnel entró en crash loop. Lo resolví con resolvers explícitos (1.1.1.1 y 8.8.8.8) en el servicio.

Stack

Raspberry Pi 4Docker ComposeNginx 1.25 AlpineLet's EncryptCertbot DNS-01Cloudflare TunnelWeb3FormsHTML · CSS · JS
Open source · MIT Ethical hacking

hack-core/home: recursos de ethical hacking para aprender en casa

Un repositorio pensado para quien empieza: notas, herramientas en Docker y scripts de Python para practicar en laboratorios locales, rangos de entrenamiento y sistemas propios.

Lab de Kali en Docker

Un contenedor basado en Kali Linux listo para hacer fuzzing en laboratorios locales con ffuf y SecLists.

Scripts de Python

Herramientas ligeras y fáciles de leer para practicar seguridad web, con instrucciones de instalación.

Ejercicios base

Ejercicios de Python (loops, funciones, clases y condicionales) para repasar lo básico.

⚠️ Todo el material es solo para laboratorios propios o sistemas donde tengas autorización.

Ver en GitHub ↗