DanLevy.net

Consejos esenciales de seguridad para Docker en auto‑hosting

¡Asegura tus servicios auto‑alojados, de la defensa a la monitorización!

Tabla de contenidos

🧗‍♀️ Para los valientes

Si estás auto‑alojando servicios Docker, la seguridad es tu responsabilidad de extremo a extremo—no hay un proveedor de nube que te proteja de escaneos de puertos o configuraciones descuidadas. Ya sea que estés desplegando aplicaciones en tu red doméstica o alquilando VPS en proveedores como Vultr, DigitalOcean, Linode, AWS, Azure o Google Cloud, tendrás que endurecer todo y comprobar que lo hiciste bien.

En esta guía, repasaremos la seguridad en Docker—desde técnicas menos conocidas hasta otras difíciles de ejecutar correctamente; exploraremos tokens canarios, volúmenes de solo lectura, reglas de firewall, segmentación y endurecimiento de red, incorporación de proxies autenticados y mucho más.

También compararemos redes domésticas con configuraciones en la nube pública y te mostraremos cómo montar un proxy de autenticación básica con Nginx. Al final tendrás varias opciones para mantener fuera al riff‑raff (amigos, familia e incluso, a veces, a ti mismo…).

¡Es mucho material! Pero gran parte está interrelacionada, y puedes elegir lo que sea más relevante para tu entorno. 🍀

🔄 El baile del :latest

Mantener las imágenes actualizadas es crucial para la seguridad. Sin embargo, depender de :latest puede introducir cambios incompatibles o compilaciones vulnerables sin una fase de revisión.

La forma segura de actualizar

Combina los comandos de actualización con pull o build para que refresques las imágenes de forma deliberada, y luego reinícialas en una ventana donde puedas detectar cualquier ruptura.

update-and-run.sh
#!/bin/bash
docker compose pull && \
docker compose up -d

Version Pinning vs Latest

Elegir la versión adecuada para fijar es un acto de equilibrio entre estabilidad y seguridad. Aquí hay algunas estrategias comunes:

docker-compose.yml
# ...
# Fijado de versión exacta, lo mejor para servicios críticos
image: postgres:17.2
# Fijado de versión de parche, bueno para servicios no críticos
image: postgres:17.2
# Fijado de versión mayor, perfecto para proyectos hobby
image: postgres:17
# Yolo, evitar si es posible
image: postgres:latest

Usa Dependabot o Renovate para abrir PRs de actualización revisables. Para cualquier cosa que te molestaría reconstruir a las 2 a.m., fija una versión o digest específico y deja que la automatización te indique cuándo avanzar.

¡Cuéntame cuáles son tus herramientas favoritas para mantener actualizadas las imágenes Docker!

🔐 Gestión de secretos

Hay muchas formas de gestionar secretos, pero una de las reglas más importantes a respetar es: nunca incrustes secretos en tus imágenes Docker ni los comprometas en git. Es uno de los errores de seguridad más comunes, genera un riesgo a largo plazo y es un dolor de cabeza solucionarlo.

Almacenar secretos de forma segura es un tema amplio con muchísimas opciones, desde archivos .env, Docker secrets, 1Password/Bitwarden, o un gestor de secretos como HashiCorp Vault o AWS Secrets Manager.

Tendrás que elegir el nivel de esfuerzo y seguridad “adecuado” para tu caso de uso.

💡 Asegúrate de que los secretos sean siempre únicos. Intenta que sea imposible ejecutar con valores predeterminados inseguros o codificados.

Si utilizas marcadores de posición como __WARNING_REPLACE_ME__ en tus secretos, genial, ¡quizá alguien lo note!

Por si acaso, también puedes añadir una pequeña seguridad en tiempo de ejecución con poco esfuerzo. Así es como podrías hacerlo en JavaScript, Rust y Go:

Helper commandsPersist secrets in environmentUse secrets per command
validateSecrets.js
const validateSecrets = () => {
const unsafePlaceholder = /__WARNING_REPLACE_ME__/;
const missingSecrets = Object.entries(process.env).filter(
([key, value]) => unsafePlaceholder.test(value)
);
if (missingSecrets.length) {
console.error("Unsafe secrets detected:", missingSecrets);
process.exit(1);
}
};
validateSecrets();
validate_secrets.rs
use std::env;
fn validate_secrets() {
let unsafe_placeholder = "__WARNING_REPLACE_ME__";
for (key, value) in env::vars() {
if value.contains(unsafe_placeholder) {
panic!("Unsafe secret in {}", key);
}
}
}
fn main() {
validate_secrets();
}
validate_secrets.go
package main
import (
"fmt"
"os"
"strings"
)
func validateSecrets() {
placeholder := "__WARNING_REPLACE_ME__"
for _, env := range os.Environ() {
pair := strings.SplitN(env, "=", 2)
if len(pair) == 2 && strings.Contains(pair[1], placeholder) {
panic(fmt.Sprintf("Unsafe secret in %s", pair[0]))
}
}
}
func main() {
validateSecrets()
}

*/}

Generar secretos fuertes

Aquí tienes un script pequeño para generar nuevos secretos para un archivo .env:

generate-secrets.sh
#!/bin/bash
generate_secret() {
local length=${1:-30}
local generate_length=$((length + 4))
openssl rand -base64 "$generate_length" | tr -d '+=/\n' | cut -c1-"$length"
}
[ -f .env ] && { echo ".env file already exists!"; exit 1; }
cat > .env << EOL
POSTGRES_PASSWORD=$(generate_secret)
JWT_SECRET=$(generate_secret 64)
SESSION_KEY=$(generate_secret 24)
REDIS_PASSWORD=$(generate_secret 20)
UNSAFE_PLACEHOLDER=__WARNING_REPLACE_RANDOM_TEXT__
EOL
echo "New .env file generated with secure random values!"

Tokens Canary

Canary Tokens son una forma excelente de detectar si tus secretos han sido comprometidos (y usados). Funcionan como una trampa que puedes añadir a cualquier archivo sensible, URL o token.

Considera colocarlos junto a los secretos que realmente te preocupan: archivos .env, variables de CI, gestores de contraseñas, carpetas de copias de seguridad y credenciales en la nube. No lo conviertas en un espectáculo; pon trampas donde un atacante real o un futuro tú equivocadamente los tocaría.

Hay muchos tipos de “tokens” canarios para elegir, desde tokens de AWS, números de tarjeta de crédito falsa, archivos Excel y Word, archivos Kubeconfig, credenciales VPN, ¡incluso los volcados SQL pueden llevar una trampa!

Mejores Prácticas para Tokens Canary

Actualiza de .env a Keychain de macOS

Para usuarios de Mac, una de las opciones más simples es usar Keychain.

A continuación tienes una forma sencilla de automatizar la carga de secretos desde el llavero de macOS, con soporte para TouchID y un nivel de seguridad algo mayor que los archivos .env.

Original credit: Brian Hetfield and Jan Schaumann.

Helper commandsPersist secrets in environmentUse secrets per command
keychain-secrets.sh
### Funciones para establecer y obtener variables de entorno desde el llavero de macOS ###
### Adaptado de: https://www.netmeister.org/blog/keychain-passwords.html y
Original credit: [Brian Hetfield](https://gist.github.com/bmhatfield/f613c10e360b4f27033761bbee4404fd) and [Jan Schaumann](https://www.netmeister.org/).
# Uso: get-keychain-secret VARIABLE_DE_ENTORNO
function get-keychain-secret () {
security find-generic-password -w -a ${USER} -D "environment variable" -s "${1}"
}
# Uso: set-keychain-secret VARIABLE_DE_ENTORNO
# ¡Se te pedirá que ingreses el valor del secreto!
function set-keychain-secret () {
[ -n "$1" ] || print "Falta el nombre de la variable de entorno"
# solicitar al usuario el secreto
echo -n "Introduce el secreto para ${1}"
read secret
[ -n "$secret" ] || return 1
( [ -n "$1" ] || [ -n "$secret" ] ) || return 1
security add-generic-password -U -a ${USER} -D "environment variable" -s "${1}" -w "${secret}"
}
~/code/app/.env-secrets.sh
source ~/keychain-secrets.sh
# Cargar variables de entorno en la shell actual
export AWS_ACCESS_KEY_ID=$(get-keychain-secret AWS_ACCESS_KEY_ID);
export AWS_SECRET_ACCESS_KEY=$(get-keychain-secret AWS_SECRET_ACCESS_KEY);
# Nota: Si un atacante puede ejecutar `env` en tu shell, estos secretos podrían exponerse.
~/code/app/scripts/env-run.sh
#!/usr/bin/env bash
source ~/keychain-secrets.sh
# Especificar todos los secretos para este proyecto
AWS_ACCESS_KEY_ID=$(get-keychain-secret AWS_ACCESS_KEY_ID) \
AWS_SECRET_ACCESS_KEY=$(get-keychain-secret AWS_SECRET_ACCESS_KEY) \
"$@"
# Nota: Usar un wrapper de shell ayuda a evitar que los secretos permanezcan
# en el entorno. Y es seguro comprometerlo al repositorio.
# Uso:
# ./scripts/env-run.sh docker compose up -d
# ./scripts/env-run.sh docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY ...

🌐 Riesgo de Red

Redes Personalizadas y Puertos Internos

Aislar correctamente los servicios con redes de Docker es una forma importante de reducir la superficie de ataque.

¡Cuidado al abrir agujeros en tu red! Un solo reenvío de puerto mal configurado puede terminar muy mal.

Por defecto, los servicios en una LAN privada no se exponen a Internet; debes reenviar puertos explícitamente desde tu router.

Docker en LAN

Ya seas un desarrollador que ejecuta servidores de desarrollo localmente, o estés auto‑alojando servicios desde tu red local, las suposiciones sobre el modelo de red de Docker pueden causar problemas.

A los devs a menudo les sorprende descubrir que los métodos “tradicionales” para asegurar servidores Linux (iptables, restringir opciones sysctl de tcp/ip) pueden fallar silenciosamente en hosts Docker. ¡Esto es especialmente crítico cuando auto‑alojas o trabajas en una red doméstica típica! (Para los que están al fondo: ¡esto puede permitir el acceso a contenedores de desarrollo en tu MacBook!)

⚠️ Advertencia #1: Los puertos publicados por Docker pueden eludir las reglas de firewall que creías que protegían el host, sobre todo con UFW en Ubuntu/Debian. Eso no vuelve inútiles todas las reglas de firewall, pero sí significa que “UFW dice deny” no es prueba suficiente. Ver issue #690: Docker bypasses ufw firewall rules.

⚠️ Advertencia #2: Vincular puertos a direcciones IP locales (p. ej., -p 127.0.0.1:8080:80) es el valor predeterminado correcto, pero versiones del motor Docker anteriores a 28.0.0 tenían casos en los que hosts en la misma red L2 aún podían alcanzar puertos publicados en localhost. Docker documenta la advertencia en su guía de publicación de puertos, y el hábito de verificar con nmap que se muestra a continuación sigue siendo importante.

Si esto te sorprende, ¡no estás solo!

Vincular a IPs locales sigue siendo una buena práctica y tiene un impacto significativo en entornos de nube gestionados y redes especialmente configuradas.

Ejemplo de Docker Compose

Aquí tienes un archivo docker-compose.yml de ejemplo que enlaza el servicio app a 127.0.0.1:8080 y conecta ambos contenedores a la red personalizada backend.

docker-compose.yml
networks:
backend:
services:
app:
networks:
- backend
ports:
# Enlazar a localhost si es posible
- "127.0.0.1:8080:8080"
# ... otras configuraciones
database:
image: postgres:17.1
# No se necesitan puertos; accesible dentro de la red backend.
networks:
- backend

Mejores Prácticas de Red

🛡️ Controles de Acceso

Los controles de acceso son una parte crítica para asegurar tus servicios Docker. Esto incluye limitar capacidades y permisos de los contenedores, restringir el acceso al socket de Docker y más.

Limitando Capacidades de Contenedor

Otra práctica sólida de control de acceso es limitar las capacidades de tus contenedores. Esto reduce el radio de explosión de varias amenazas, desde escalada de privilegios hasta secuestro de tráfico. No es un campo de fuerza, pero elimina permisos que la mayoría de los contenedores nunca necesitó.

¿Qué son las capacidades? Permisos o habilidades nombradas definidas por el kernel de Linux. (La página del manual de capabilities contiene la lista completa.) Incluyen cosas como CAP_CHOWN (cambiar la propiedad de archivos), CAP_NET_ADMIN (configurar interfaces de red), CAP_KILL (matar cualquier proceso) y muchas más.

Las dos formas de determinar las capacidades necesarias son:

  1. Prueba y error: Este método más lento pero efectivo te hace comenzar sin capacidades y luego añadirlas una a una hasta que tu aplicación funcione.
  2. Buscar trabajo previo: Busca “project-name cap_drop Dockerfile” o “project-name cap_drop docker-compose.yml” para ver si otros ya lo hicieron. Un LLM puede sugerir un punto de partida, pero trátalo como una conjetura hasta que pruebes el contenedor y leas la documentación de la imagen.

Mejores Prácticas de Capacidades

Example: Drop/Limit Capabilities
services:
database:
image: postgres:17.1
networks: [ db-network ]
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_READ_SEARCH
- FOWNER
- SETGID
- SETUID
db-admin:
image: dpage/pgadmin4:4.1
networks: [ db-network ]
ports:
- "8081:80"
# ... otras configuraciones
networks:
db-network:

Ahora tus servicios pueden comunicarse entre sí a través de la red db-network. Docker Compose creará esa red automáticamente.

Usa la opción --external/external: para unirte a una red preexistente. Omitela para crear una red nueva.

Acceso al Socket de Docker

⚠️ Advertencia: docker.sock es básicamente acceso de administrador al host

⚠️ La opción :ro no afecta la I/O enviada a través del socket!

Solo garantiza que la ruta del socket se monte como solo‑lectura. Las llamadas API enviadas por ese socket aún pueden crear contenedores, montar rutas del host y hacer otras cosas muy emocionantes que probablemente no pretendías delegar.

Mejores Prácticas para el Socket

¡Bloqueando Países!

A veces útil, pero no constituye una verdadera barrera de seguridad.

Hablando de la entidad geopolítica, no de la música…

Si alojas aplicaciones mayormente para tu familia y amigos locales, puedes bloquear el tráfico de los países de los que no esperas recibir visitas. O bien permitir solo el tráfico de los países que sí esperas. Reduce el ruido; no detiene VPNs, proxies, botnets ni a nadie con paciencia.

Revisa este script para bloquear todo el tráfico procedente de China:

block-china.sh
curl -fsSL https://www.ipdeny.com/ipblocks/data/countries/cn.zone | \
while read line; do ufw deny from $line to any; done
block-china.sh
curl -fsSL https://www.ipdeny.com/ipblocks/data/countries/cn.zone | \
while read line; do ufw deny from $line to any; done

De forma similar, puedes permitir solo el tráfico proveniente de EE. UU.:

allow-usa.sh
curl -fsSL https://www.ipdeny.com/ipblocks/data/countries/us.zone | \
while read line; do ufw allow from $line to any; done

Endurecimiento del host proxy de CloudFlare

Si tu servidor doméstico está detrás de una IP de CloudFlare (proxy), puedes restringir el acceso únicamente a las IPs de CloudFlare y a tu red local.

Esto es algo parecido al bloqueo por país anterior, pero con un control mucho más estricto.

whitelist-ingress-from-cloudflare.sh
ufw default deny incoming # ¡Bloquear todo el tráfico entrante!
ufw default allow outgoing # Permitir todo el tráfico saliente
ufw allow ssh # Permitir SSH
# Permitir acceso para la subred local (preferiblemente una DMZ/VLAN dedicada para los servicios alojados)
ufw allow from 10.0.0.0/8 to any port 443

Permitir IPs de CloudFlare

curl -fsSL https://www.cloudflare.com/ips-v4 |
while read line; do ufw allow from $line to any port 443; done

Añadir soporte IPv6

curl -fsSL https://www.cloudflare.com/ips-v6 | \

while read line; do ufw allow from $line to any port 443; done

Para probar cambios basados en geolocalización, una VPN con puntos de salida en el país deseado puede ser útil. Consulta más detalles en la sección [Monitoring & Verification](#-monitoring--verification).
### Seguridad a nivel de aplicación
Una vez que tu [network and host are security hardened,](#-network-hazard) puede que descubras que aún queda trabajo por hacer.
Ahora debemos considerar la capa “aplicación” de los propios servicios.
<p class="inset">¿Esa base de datos tiene una contraseña válida? ¿Este contenedor automatiza HTTPS/certificados? ¿La aplicación incluye autenticación incorporada? ¿Hay límites sobre qué correos pueden registrarse? ¿Existen credenciales predeterminadas o variables de entorno que cambiar?</p>
La única forma de _saberlo_ es revisarlo. En este caso, comienza con el `README` y otros archivos clave como `docker-compose.yml`, `Dockerfile` y `.env.*`. Tanto en el proyecto como, idealmente, en sus servicios de apoyo (p. ej., Postgres, Redis, etc.).
#### Proxy inverso
Otra capa de defensa es la autenticación básica. No la uses sin HTTPS. Para servicios heredados, colocar autenticación básica delante de una ruta de administración suele ser suficiente para detener peticiones casuales y rastreadores no autenticados que intenten acceder directamente.
```nginx
# /etc/nginx/conf.d/secure-admin.conf
location /admin {
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://internal_admin:80;
proxy_set_header X-Real-IP $remote_addr;
}

Genera credenciales:

Terminal window
htpasswd -c /etc/nginx/.htpasswd admin

Con un proxy de autenticación básica, los atacantes enfrentan un obstáculo adicional —nombre de usuario y contraseña— antes de llegar a tu servicio interno.

Otra opción es usar un servicio como Traefik o Caddy que pueda automatizar HTTPS y autenticación básica por ti.

Si deseas gestionar muchos dominios y servicios mediante una GUI, te recomendaría Nginx Proxy Manager.

🔍 Monitoreo y Verificación

Este es el paso más importante y más pasado por alto. Puedes contar con el mejor firewall, la mejor red y las mejores prácticas, pero si no verificas, no sabes si están funcionando.

Además, conocer solo un puñado de comandos —o saber dónde buscarlos— puede marcar la diferencia entre prevenir una brecha o no. Sentirte como un hacker es solo un extra. (Para detalles y ejemplos, avanza a la sección de Monitoring & Verification.)

No confíes, verifica dos veces

Verifica tus puertos

⚠️ IMPORTANTE: No escanees hosts que no poseas.

Ya sea que estés en una red doméstica o en un VPS, querrás saber qué puertos están abiertos al mundo.

Hay 2 formas de hacerlo:

Probando fuera de tu red

Necesitarás tu IP (pública) actual, que puedes obtener fácilmente con servicios como ifconfig.me: curl https://ifconfig.me. O buscarla en el panel de control de tu proveedor de hosting.

Get Public IP
curl -fsSL https://ifconfig.me
# --> CURRENT PUBLIC IP

Una vez que tengas tu IP pública, ahora debes conectarte a una red externa. Puedes usar la computadora de un amigo, un teléfono/punto de acceso 5G, o un servidor dedicado.

nmap External Scan
target_host="$(curl -fsSL https://ifconfig.me)"
# Nota: Asegúrate de que `target_host` sea la IP deseada
# Escanear puertos específicos:
nmap -A -p 80,443,8080 --open --reason $target_host
# Top 100 puertos:
nmap -A --top-ports 100 --open --reason $target_host
# Todos los puertos
nmap -A -p1-65535 --open --reason $target_host
#### Prueba dentro de tu red
Practica usando `nmap`, escanea tu red local o uno de tus servidores, revisa tu router, impresora, nevera inteligente.
{/* Mientras los escaneos de puertos son una constante, podrían violar la CFAA (Computer Fraud and Abuse Act) en EE. UU. Así que, escanea solo lo que posees. */}
#### Comandos de escaneo de ejemplo
```bash
# Escanea tu localhost en busca de todos los puertos abiertos
nmap -sT localhost
# Escanea la IP privada de tu máquina para descubrir servicios
nmap -sV 192.168.1.10
# Encuentra detalles de servicios en tu red
nmap -sn 192.168.0.0/24
nmap -sn 10.0.0.0/24
# O en una red Docker 172.18.0.1/16
nmap -sn 172.18.0.1/16
nmap Scan
% nmap -A --open --reason 192.168.0.87
Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-06 13:51 MST
Nmap scan report for dev02.local (192.168.0.87)
Host is up, received syn-ack (0.0067s latency).
Not shown: 995 closed tcp ports (conn-refused)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|_ 256 {FINGERPRINT} (ED25519)
80/tcp open http syn-ack Caddy httpd
|_http-server-header: Caddy
|_http-title: Dev02.DanLevy.net
443/tcp open ssl/https syn-ack
|_http-title: Dev02.DanLevy.net
1234/tcp open http syn-ack Node.js Express framework
|_http-cors: GET POST PUT DELETE PATCH
|_http-title: Dev02.DanLevy.net (application/json; charset=utf-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.36 seconds

Ver puertos abiertos

Familiarízate con lsof; está disponible en macOS y Linux. Muestra el estado granular de la red y la actividad de disco.

lsof Commands
# Monitorea un puerto específico
sudo lsof -i:80 -Pn

Monitorar conexiones ESTABLISHED

sudo lsof -i -Pn | grep ESTABLISHED

Ver LISTEN

sudo lsof -i -Pn | grep LISTEN

para ver nombres de red en lugar de direcciones IP (puede ser muy lento hacer búsquedas DNS inversas)

sudo lsof -i -P | grep LISTEN

Monitorar todas las conexiones de red

sudo watch -n1 “lsof -i -Pn”

#### Salida de ejemplo
![nmap scan for listeners](../lsof-scan-listen.webp)
### Monitoreo de archivos
Para identificar qué **procesos** están consumiendo más **ancho de banda del disco**, puedes usar `iotop`:
```bash
sudo iotop

Para ver cambios individuales de archivos, puedes usar inotifywait en Linux o fswatch en macOS:

Esto puede ser útil para detectar comportamientos no autorizados o extraños por carpeta o a nivel del sistema.

Terminal window
# Monitorar todos los cambios de archivos en un directorio
sudo inotifywait -m /path/to/directory

En macOS puedes usar fswatch:

Instálalo con brew install fswatch

Terminal window
fswatch -r /path/to/directory
## ⏰ Consejos que a menudo se pasan por alto
1. **Limitación de velocidad** para intentos de autenticación y cualquier otro punto crítico. Ya sea mediante el módulo `limit_req` de Nginx o `fail2ban` para acceso SSH, throttlear ataques de fuerza bruta es _probablemente_ una buena idea. Digo _probablemente_ porque en la era del IPv6 y los botnets baratos, ya no es lo que solía ser.
2. **Usar volúmenes de solo lectura** siempre que sea posible:
```yaml
services:
webapp:
volumes:
- ./config:/config:ro

Combinado con otras buenas prácticas (usuarios sin privilegios, permisos mínimos en carpetas), la opción de montaje :ro brinda salvaguardas adicionales contra cambios accidentales y algunos intentos de escritura desde dentro del contenedor. No protege al host de un proceso que ya posee privilegios más amplios.

  1. Auditar el acceso a contenedores de forma regular.
    Si un contenedor no necesita un secreto, puerto o montaje, ¡elimínalo!

  2. Cuidado con el Wi‑Fi de mala calidad
    Seguro que nunca compartirías la contraseña de tu Wi‑Fi, sobre todo con desconocidos, ¿verdad? Bueno, salvo algunos amigos… Vale, quizá también con la familia. Nunca sabes qué aplicaciones tienen y cuáles podrían divulgar tu SSID y contraseña al mundo.

Red doméstica vs. Proveedor público vs. Túnel

  1. Aislamiento virtual/DMZ: Para servidores caseros, colócalos en una VLAN o DMZ separada si es posible. Así mantienes tus dispositivos internos fuera del alcance de una posible compromisión desde el servidor.

    • Usa un router o VLAN independiente para tu servidor doméstico.
    • Usa una red Wi‑Fi separada para tu servidor doméstico.
    • Usa una subred distinta para tu servidor doméstico.
  2. Proveedores de nube: Hetzner, Vultr, DigitalOcean, Linode, AWS, Azure y Google Cloud ofrecen distintas funcionalidades de firewall.

    • Algunos proveedores y servicios bloquean puertos por defecto. Otros ofrecen opciones opt‑in o complementos. Revisa la documentación de tu proveedor.
    • Muchos proveedores brindan servicios avanzados de monitoreo y detección de amenazas.
  3. VPNs y túneles: Considera usar una solución tipo VPN o un servicio de túnel para conectar servicios de forma segura a través de Internet sin exponerlos al público.

    • TailScale, ngrok, ZeroTier.
    • WireGuard, OpenVPN.

🚀 Lista de verificación para producción

📚 Lecturas adicionales

Gracias

Un agradecimiento a algunos Redditors atentos:

¡Gracias por leer! Espero que esta guía te haya sido útil. Si tienes preguntas o sugerencias, contáctame en mis redes sociales abajo, o simplemente pulsa el enlace Edit on GitHub para abrir un PR. ❤️