· 16 min de lectura

Bloque de Red II - Servicios


Introducción

Bueno, esto es una continuación de mi anterior post en el que explicaba cómo está configurado el bloque de red de cara a Internet, dejando como punto y aparte el servicio cloudflared, que es el conector entre mi infraestructura local y los servidores de Cloudflare que dan al exterior (Internet).

Como iréis viendo, este bloque se compone de varios servicios que he ordenado de menos a más (al decir de menos a más me refiero de más fuera de la red a más dentro), aunque este orden es relativo y también va de más importante a menos.

En el primer post de esta serie enseñé mi diagrama lógico completo. Hoy vamos a centrarnos en estos servicios:

flowchart TD %% Configuración de estilos y temas de color personalizados classDef rpi fill:#2d1b22,stroke:#ff3366,stroke-width:2px,color:#fff; classDef hp fill:#1a2b3c,stroke:#3399ff,stroke-width:2px,color:#fff; classDef npm fill:#1e272e,stroke:#0be881,stroke-width:2px,color:#fff; classDef dns fill:#2c3e50,stroke:#f5cd79,stroke-width:2px,color:#fff; classDef vpn fill:#2d2d2d,stroke:#a55ecc,stroke-width:2px,color:#fff; %% Dispositivo: Raspberry Pi 4 (Bloque Principal de Red) subgraph RPI ["Raspberry Pi 4 (stack: pistack)"] direction TB NPM["Nginx Proxy Manager
npm.jrodriiguezg.lan
(Proxy Reverso)"]:::npm PHNS1["phns1.jrodriiguezg.lan
(PiHole + Unbound / DNS 1)"]:::dns subgraph VPNs ["VPN / Acceso Externo"] direction LR Tailscale["Tailscale"]:::vpn Wireguard["Wireguard
(Deprecado)"]:::vpn end end style RPI fill:#140b0f,stroke:#ff3366,stroke-width:3px,color:#fff %% Dispositivo: HP Elitedesk (Nodo de Redundancia) subgraph HP ["HP Elitedesk"] PHNS2["phns2.jrodriiguezg.lan
(DNS 2 / Réplica)"]:::dns end style HP fill:#080e14,stroke:#3399ff,stroke-width:3px,color:#fff %% Flujos de Conectividad y Relaciones Clientes(["Clientes Externos / Remotos"]) -->|"Acceso VPN"| Tailscale Clientes -->|"Acceso VPN"| Wireguard %% Conexiones dentro del Host y de Red Local Tailscale -->|"Tráfico VPN"| NPM Wireguard -->|"Tráfico VPN"| NPM %% Resolución de DNS de clientes VPN Tailscale -->|"DNS Principal"| PHNS1 Wireguard -->|"DNS Principal"| PHNS1 %% Replicación DNS (Gravity Sync / Sincronización) PHNS1 <-->|"Sincronización DNS & Tolerancia a Fallos"| PHNS2 %% Resolución de nombres para NPM NPM -->|"Consulta DNS Primario"| PHNS1 NPM -->|"DNS Secundario (Failover)"| PHNS2

Se pueden ver en la imagen los servicios que corresponden al bloque de red. Como todo ha sido desplegado usando Docker, iré poniendo aquí todos los archivos con sus explicaciones de qué hace cada cosa por si queréis replicar el despliegue.

Si esta es vuestra primera vez leyendo este blog, os invito a ver el resto de entradas aquí:

Los compose que publique aquí estarán adaptados para desplegarse de manera independiente sin estar en el mismo stack de red

Nginx Proxy Manager

Este es el segundo servicio si seguimos el orden de más externos a más internos, y también es de vital importancia para no depender ni recordar todos los puertos de todos los servicios (también actúa como filtro de las solicitudes del exterior).

¿Qué es NPM?

Es una herramienta que proporciona una interfaz gráfica para configurar y gestionar un proxy inverso, más exactamente Nginx. Podemos verlo como el portero de mi homelab: recibe una petición y la enruta de manera silenciosa al contenedor que aloja el servicio.

¿Qué es un proxy?

Es un programa informático que actúa como intermediario entre un dispositivo y un servidor final. Cuando se envía una solicitud a un contenido, esta se dirige primero al proxy y es él quien procesa y enruta la petición al destino. Permite ocultar la IP del cliente, filtrar el acceso a cierto contenido, mejorar la velocidad mediante caché, etc.

¿Cómo se despliega?

Para el despliegue de este y de todos los servicios que vayamos viendo, yo recomiendo crear una carpeta que unifique todos los docker-compose.yaml y todas las carpetas de configuración de una manera ordenada. Por ejemplo, yo uso una carpeta docker en la raíz del home del usuario de mi homelab, pero esta decisión depende de cada uno; luego, para cada servicio creo una carpeta dentro de la anterior para separar sus archivos.

En este despliegue se mapean dos carpetas, una para los datos y otra para los certificados. Es recomendable crear las carpetas antes de desplegar el contenedor; podemos usar el siguiente comando:

mkdir docker/npm && mkdir -p docker/npm/{data,letsencrypt}

Este comando creará la carpeta npm dentro de docker y luego las dos que se mapean desde el contenedor.

Docker Compose

Todos los archivos usados en los despliegues se encuentran unificados, tanto los archivos adaptados como los originales que usé yo; aunque aquí están de igual manera, pero con la explicación:

services:
  npm: 
    image: jc21/nginx-proxy-manager:latest # Imagen de despliegue
    container_name: npm # Nombre del contenedor
    restart: unless-stopped # Esto indica que el contenedor siempre vuelva a iniciar a no ser que esté detenido
    ports:
      - "80:80"   # Trafico HTTP entrante
      - "443:443" # Trafico HTTPS entrante
      - "81:81"   # Panel de administracion web
    environment:
      - DISABLE_IPV6=true
    dns:
      - 1.1.1.1 # DNS de Cloudflare
      - 8.8.8.8 # DNS de Google
    volumes:
      - ~/docker/npm/data:/data # Mapeamos la carpeta data a la carpeta que hemos creado antes
      - ~/docker/npm/letsencrypt:/etc/letsencrypt # Mapeamos la carpeta letsencrypt a la carpeta que hemos creado antes

El contenido del documento anterior lo debemos almacenar en una archivo llamado docker-compose.yaml o compose.yaml yo lo he almacenado en la carpeta npm dentro de docker y ejecutamos el comando para levantarlo,

jrodriiguezg@blogvm:~/docker/npm
jrodriiguezg@blogvm:~/docker/npm$ docker compose up -d  
[+] up 34/34
 ✔ Image jc21/nginx-proxy-manager:latest Pulled                                                        22.0s
 ✔ Container npm                        Started                                                        0.2s
jrodriiguezg@blogvm:~/docker/npm$

En algunos sistemas linux, os puede salir un error como el siguiente:

Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint npm 
(338e9bf2d678e9d61c2d2b7e80295c6df49c21629a35e596f817e5e93bc10182): error while calling RootlessKit PortManager.AddPort(): cannot expose privileged port 80, you can add 'net.ipv4.ip_unprivileged_port_start=80' to /etc/sysctl.conf (currently 1024), 
or set CAP_NET_BIND_SERVICE on rootlesskit binary, or choose a larger port number (>= 1024): listen tcp4 0.0.0.0:80: bind: permission denied

Esto es debido a que algunas instalaciones de docker (rootless), bloquean el uso de puertos por debajo del 1024, no entremos en panico se arregla tan simple como:

 sudo nano /etc/sysctl.d/99-rootless-ports.conf

Y agregamos la siguiente linea:

net.ipv4.ip_unprivileged_port_start=20

Despues aplicamos los cambios con:

sudo sysctl --system

Configuraciones y uso

Si todos los pasos anteriores han salido bien, deberiamos ver algo como lo siguiente:

phns1

Aqui solamente debemos rellenar con nuestros datos y pulsar en Save

phns1

Para finalmente acceder a la consola de Nginx Proxy Manager, aqui os voy a enseñar como agregar un host proxy, si desplegais el siguiente servicio os animo a explorar el resto de sus funciones

phns1

Para agregar un Proxy host, debemos pulsar en donde dice Proxy host (por si habia dudas).

Se nos abrira la siguiente ventana donde pulsaremos en Add Proxy Host

phns1

Yo como tal no he agregado nada, ya que esta instalacion solo es para enseñar todo en una instalacion limpia y ahora mismo no dispongo de un dominio para esto, pero os explico que se debe poner en cada lugar.

phns1
  1. Domain Names: Aqui va el dominio o subdominio que vayamos a usa, nginx escuchas las peticiones entrantes y compara si coinciden con el valor aqui definido.
  2. Scheme: Indicamos el tipo de protocolo que se usa en el servicio INTERNO (normalmente HTTP); Forward Hostname / IP: Direccion donde se aloja el servicio final; Forward Port: Puerto en el que responde el servicio
  3. Access List: Aqui podemos configurar el control de acceso hacia ese dominio, podemos solicitar usuario/contraseña o permitir ciertas IPs.
  4. Cache Assets: Le dice a Nginx que almacene en cache recursos estaticos, acelerando la carga y reduciendo el esfuerzo del servidor final.
  5. Block Common Exploits: Son un conjunto de reglas de seguridad preventivas a nivel de servidor web para bloquear patrones de ataques comunes (como inyecciones SQL o exploits conocidos). Es una capa de protección básica que se recomienda dejar siempre activa.
  6. Websockets Support: Permite el paso de tráfico de WebSockets, necesario para conexiones persistentes y bidireccionales en tiempo real.
  7. Save: Guardar la configuracion.

Pi-hole y Unbound

Estos dos servicios son los terceros más importantes, sobre todo a nivel de resolución interna de la red; porque sin ellos (sobre todo Pi-hole), ya que se encarga de resolver los dominios que hay indicados en el NPM.

¿Qué es Pi-hole?

Es un bloqueador de anuncios y rastreadores a nivel de red. Funciona como un “portero” que filtra el tráfico de Internet de todo antes de que carguen la publicidad, mejorando la velocidad, la privacidad y la seguridad de la red doméstica.

¿Cómo funcionan sus listas de bloqueo?

Funcionan como una lista negra de control de acceso. En vez de filtrar el contenido de una página web cuando ya se está descargando, Pi-hole intercepta el tráfico en el DNS.

Las listas de bloqueo son simples archivos de texto que contienen miles de direcciones web (dominios) conocidos por servir publicidad, rastreadores, virus o estafas.

Cuando una página web solicita la dirección IP de un dominio, Pi-hole compara si ese dominio está en sus listas y, si está, detiene la solicitud.

¿Dónde puedo encontrar listas de bloqueo?

Se encuentran en repositorios web gestionados por la comunidad, normalmente GitHub, aunque hay otros como:

  • The Firebog
  • OISD
  • HaGeZi DNS Blocklists

Pequeño tour por la interfaz

Yo, aunque tengo bastantes listas de bloqueo, no suelen bloquear mucho, ya que las páginas en las que se encuentra este tipo de contenido malicioso o no accedo a ellas o las bloqueo mediante extensiones del navegador. La interfaz tiene bastantes cosas, por lo que solo enseñaré las que me parecen más importantes; un tour completo es algo que me podéis pedir en los comentarios.

Lo primero es el panel principal (dashboard), que muestra un resumen de todas las solicitudes que se han hecho, las que se han bloqueado, el total de dominios en listas y unas gráficas de actividad por horas.

phns1

Después tenemos la pestaña querys, donde podemos ver las solicitudes DNS que se han hecho y a qué dominio, así como si se han o no bloqueado.

querys

En la pestaña listas, tenemos las listas de hosts que hemos configurado y desde aquí es desde donde se configuran más listas y se activan o desactivan.

blocklist

Y ya finalmente, lo que yo más uso: el DNS (Local DNS Settings). Aquí declaro el dominio interno y la IP donde está, aunque todo apunta al NPM, ya que es él quien hace la redirección.

phns1-dns

unbound

¿Qué es Unbound?

Es un servidor DNS validador, recursivo y de alto rendimiento. En el contexto de redes domésticas y servidores como Pi-hole, se utiliza para eliminar por completo la necesidad de depender de servidores DNS de terceros (como Google 8.8.8.8 o Cloudflare 1.1.1.1), aumentando la privacidad y seguridad.

¿DNS Recursivo?

Es un servidor que actúa como intermediario encargado de buscar la dirección IP de una página web cuando intentamos acceder a ella. Funciona como una carrera de relevos: cuando hacemos una consulta DNS, por ejemplo, en un navegador, esta va siguiendo una serie de pasos que hace el servidor DNS, preguntando a diferentes servidores para ver dónde está la IP del servidor.

Despliegue de Pi-hole y Unbound

Estos servicios pueden ser desplegados por separado, pero ya que en mi caso van juntos, el compose desplegará ambos. El mapeo de carpetas de estos servicios no es necesario, ya que no se suelen tocar mucho sus ficheros; pero si quisiéramos mapear, lo haríamos como en el servicio anterior dentro de una carpeta docker:

mkdir ~/docker/pihole && mkdir -p ~/docker/pihole/{unbound,etc,dnsmasq.d}

Docker Compose

version: '3.8'
services:
  unbound:
    image: mvance/unbound:latest
    container_name: unbound
    restart: unless-stopped
    network_mode: host
    volumes:
      - ~/docker/pihole/unbound:/opt/unbound/etc/unbound

  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    restart: unless-stopped
    network_mode: host
    depends_on:
      - unbound
    environment:
      - TZ=Europe/Madrid
      - FTLCONF_webserver_port=8080
      - FTLCONF_webserver_tls_port=8443
      - WEBPASSWORD=tu_contraseña_segura
    ports:
      - "53:53/tcp" # DNS
      - "53:53/udp" # DNS
      - "8080:8080" # Web 
    volumes:
      - ~/docker/pihole/etc:/etc/pihole
      - ~/docker/pihole/dnsmasq:/etc/dnsmasq.d
    cap_add:
      - NET_ADMIN

Igual que anteriormente ponemos el anterior compose en un fichero denominado docker-compose.yaml o compose.yaml

jrodriiguezg@blogvm:~/docker/pihole
jrodriiguezg@blogvm:~/docker/pihole$ docker compose up -d  
[+] up 10/10
✔ Image mvance/unbound:latest Pulled                                                                                                                                                                                                   6.6s
✔ Container unbound           Started                                                                                                                                                                                                  0.2s
✔ Container pihole            Running                                                                                                                                                                                                  0.0s 

Configuraciones

La única configuración aquí, que en verdad es opcional pero nos asegura que todo funcione mejor, sería bajar el fichero root.hints, que guarda la ubicación de los servidores root:

sudo wget https://www.internic.net/domain/named.root -O ~/docker/pihole/unbound/root.hints

Para comprobar que todo funciona, podemos hacer un dig al puerto en el que está unbound para ver si resuelve, o al puerto en el que está Pi-hole:

# Dig a Unbound
dig @127.0.0.1 -p 5335 jrodriiguezg.link

# Dig a Pi-hole
dig @127.0.0.1 -p 53 jrodriiguezg.link

(Hacer esto desde el servidor donde se alojan estos servicios, no desde el cliente).

Si todo ha salido bien al acceder a la url http://IP_LOCAL:8080/admin, debemos ver algo como lo siguiente, aqui iniciamos sesion con la contraseña que hemos definido en el nuestro docker compose

phns1-dns

Si no poneis contraseña y se os olvida seguramente sea esta: tu_contraseña_segura

Finalmente accedemos al panel de pihole,

phns1-dns

Como anteriormente se ha hecho un tour por la interfaz, nos saltamos ahora repetir pasos, lo mejor para aprender es que uno mismo investigue el sistema.

Replicación (phns2)

Yo dispongo de un segundo servidor Pi-hole en un host diferente para que, si el primero deja de responder, el segundo le coja el relevo. Esto lo he hecho con una herramienta que clona la base de datos automáticamente mediante cron; la herramienta es la siguiente: Gravity Sync.

Tailscale / Wireguard

Estos dos servicios no afectan al funcionamiento de la red; son simplemente para tener acceso a la infraestructura desde fuera de la red local, pero como si estuviera en ella. En el caso de Wireguard, a día de hoy ya no se usa, por lo que no voy a explicar mucho y solo os daré el docker-compose. Saltando directamente a explicar Tailscale.

Wireguard

Yo no usé Wireguard directamente, sino que usé wg-easy, que nos da una interfaz web para crear los clientes de Wireguard.

Docker Compose

version: '3.8'

services:
  wireguard:
    image: ghcr.io/wg-easy/wg-easy:latest
    container_name: wireguard
    restart: unless-stopped
    # Funciona directamente sobre la red del host
    network_mode: host
    environment:
      WG_HOST: "AQUI_IP_PUBLICA" # Si no tenemos IP pública wireguard no funcionará
      PASSWORD_HASH: 'HASH_DE_CONTRASEÑA'
      WG_DEFAULT_DNS: "127.0.0.1" # DNS que va a usar wireguard
      WG_DEFAULT_ADDRESS: "10.8.0.x" # Rango de IPs de wireguard
      PORT: 51821
      WG_PORT: 51820 # Este puerto se debe abrir en el router
    volumes:
      - ~/docker/wireguard/etc:/etc/wireguard
      - /lib/modules:/lib/modules:ro 
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1

Para generar el hash de la contraseña necesitamos bcrypt; podemos generarlo con una imagen de Docker y el siguiente comando: docker run --rm -it python:alpine sh -c "pip install bcrypt && python -c \"import bcrypt; print(bcrypt.hashpw(b'AQUI_LA_CONTRASEÑA', bcrypt.gensalt(12)).decode())\"", cambiando AQUI_LA_CONTRASEÑA por la contraseña.

Tailscale

¿Qué es?

Es una herramienta que nos permite crear VPNs de malla de forma fácil. A diferencia de los protocolos VPN tradicionales que requieren un servidor, con Tailscale los dispositivos se conectan directamente entre sí sin depender de un servidor. A nivel bajo, usa el protocolo Wireguard para el cifrado y transmisión de datos.

¿Qué es una VPN?

Es una tecnología que permite crear una conexión segura y cifrada entre varios dispositivos, creando entre ellos una red privada.

Configuración y despliegue

El despliegue es bastante simple, ya que es de los pocos paquetes que se instalan a nivel de sistema sin usar Docker. Tendríamos que dirigirnos al dashboard de Tailscale y crearnos una cuenta en login.tailscale.com; una vez allí, pulsamos en Add Device para añadir un servidor o un cliente.

tailscale

Si pulsamos en Servidor, nos pedirá una serie de datos, como:

  • Ephemeral: Si queremos que al desconectarse el servidor, este desaparezca de la red.
  • Use as exit node: Si queremos que todo el tráfico de red salga por este nodo.
  • Reusable: Para que la clave API se pueda usar en más dispositivos.
  • Auth Key Expiration: Esto es para darle caducidad a la API (si caduca, el dispositivo sigue funcionando).

Después, solo pulsamos en Generate Install Script:

tailscale2

Y nos devolverá un script como el siguiente, que tenemos que copiar y pegar en nuestra terminal:

tailscale3

Configuración de clientes

Para los clientes, el proceso es más de lo mismo, pero pulsaremos en Client device en vez de Linux server. Ya depende del SO a usar, la página nos dará un enlace de descarga y la guía de instalación y configuración.

tailscale4

Y hasta aquí este tercer post, saludos a quien lo lea.

Próximamente entraremos en el bloque de administración


Comentarios