⚗️ Sirviendo en la internet con una red distribuida casera
Por razones políticas y técnicas, siempre me ha interesado crear y mantener servicios en la internet que estén fuera del control de las mega empresas de computo y datos. Una de las formas más accesibles que he encontrado para lograrlo ha sido mediante instalaciones auto-gestionadas en máquinas virtuales con una dirección IP pública asociada, un servicio ofrecido por colectivas y empresas en el mundo.
Hace años adquirí la instancia más pequeña disponible en OVH (1 CPU, 2GB RAM y 20GB de disco) para comenzar alojando sitios y aplicaciones desarrolladas para fines personales: este blog, mi sitio personal, mis notas, scripts, webhooks, etc. Recientemente he tenido también la fortuna de unirme a grupos y colectivas interesadas en utilizar servicios auto-gestionados, por lo cual esta pequeña instancia llegó a sus límites. Extrañamente un incremento de sus recursos se vuelve muy caro de forma súbita, sobre todo porque lo necesario recae en el espacio de disco.
Migrar a un hospedaje completamente casero implica adquirir una dirección IP pública fija, lo cual ni siquiera es ofrecida por mi proveedor de servicios de internet en casa. En otros casos el costo de una IP fija es muy alto dado que su oferta se ha promovido con fines empresariales.
Con ambas limitantes se hizo evidente que había que crear algo distinto para poder hospedar más servicios en forma razonable.
Creando una red distribuida
La forma en que pude solventar la falta de recursos en la máquina es utilizando lo que tengo disponible en casa, concretamente: una Raspberry PI 4 (que utiliza en promedio 4W) y una mini-PC (que utiliza en promedio 12W), conectadas a través de una red privada virtual (VPN por sus siglas en inglés) a la máquina virtual de OVH, que pasa a ser un proxy para estos equipos.
Creando la VPN con Nebula
Nebula es una aplicación que permite crear VPNs de forma sencilla y nativa entre equipos con MacOS, Linux, Android y iOS, sin tener que (des)configurar firewalls para establecer dichas redes, gracias a la implementación que utiliza puertos UDP para establecer conexiones.
La seguridad de esta red se basa en el protocolo Noise y requiere que una entidad central (llamada lighthouse) funcione como autoridad central, emitiendo certificados y autenticando a otros nodos que quieren conectarse a la VPN.
Gracias a esta implementación podemos añadir nodos sin importar la red local en la que se encuentren e incluso transferirlas a otra red local sin tener que reconfigurar la conexión a la VPN.
La instalación y configuración varía dependiendo del equipo donde se esté llevando a cabo pero en líneas generales consiste en
- Decidir el rango de red que la VPN tendrá. Este paso se vuelve relevante cuando se tienen distintas redes conectadas en los equipos, algo muy común cuando se están corriendo aplicaciones que crean direcciones virtuales. Por ejemplo, yo opto por elegir en los rangos
10.10.10.1/24
. En Linux, para mayor certeza, es mejor verificar las ya existentes con unip a
. - Instalar
nebula
ynebula-cert
en un nodo principal o lighthouse. - Crear un certificado principal para que todos los nodos puedan autenticarse en la red.
nebula-cert ca -name "Mi red distribuida"
. - Crear certificados y llaves de autenticación para cada uno de los nodos que se van a conectar a la red, ejemplos:
nebula-cert sign -name "principal" -ip "10.10.10.1/24"
nebula-cert sign -name "raspberry" -ip "10.10.10.2/24"
nebula-cert sign -name "minipc" -ip "10.10.10.3/24"
En mi caso prefiero guardar todos los archivos en/etc/nebula
al ser de accesoroot
. - Crear el archivo
/etc/nebula/config.yaml
en el nodo principal. Ejemplo:
pki:
ca: /etc/nebula/ca.crt
cert: /etc/nebula/principal.crt
key: /etc/nebula/principal.key
static_host_map:
"10.10.10.1": ["<IP pública de la VM>:4242"]
lighthouse:
am_lighthouse: true
interval: 60
hosts:
listen:
host: 0.0.0.0
port: 4242
punchy:
punch: true
relay:
am_relay: false
use_relays: true
tun:
disabled: false
dev: nebula1
drop_local_broadcast: false
drop_multicast: false
tx_queue: 500
mtu: 1300
routes:
unsafe_routes:
logging:
level: info
format: text
firewall:
outbound_action: drop
inbound_action: drop
conntrack:
tcp_timeout: 12m
udp_timeout: 3m
default_timeout: 10m
outbound:
- port: any
proto: any
host: any
inbound:
- port: any
proto: icmp
host: any
- port: any
proto: tcp
host: any
Donde <IP pública de la VM> es algo que deben obtener de la configuración que su proveedor les otorga y sustituirla adecuadamente.
-
Ejecutar el servicio mediante
nebula -config /etc/config.yaml
. -
Copiar o transferir el certificado principal,
ca.crt
, y el certificado y la llave correspondiente a los distintos equipos. -
Configurar el nodo que se va a conectar a la red. Por ejemplo, esta configuración en el nodo
Raspberry
:
pki:
ca: /etc/nebula/ca.crt
cert: /etc/nebula/raspberry.crt
key: /etc/nebula/raspberry.key
static_host_map:
"10.10.10.1": ["<IP pública de la VM>:4242"]
lighthouse:
am_lighthouse: false
interval: 60
hosts:
- "10.10.10.1"
listen:
host: 0.0.0.0
port: 4242
punchy:
punch: true
relay:
am_relay: false
use_relays: true
tun:
disabled: false
dev: nebula1
drop_local_broadcast: false
drop_multicast: false
tx_queue: 500
mtu: 1300
routes:
unsafe_routes:
logging:
level: info
format: text
firewall:
outbound_action: drop
inbound_action: drop
conntrack:
tcp_timeout: 12m
udp_timeout: 3m
default_timeout: 10m
outbound:
- port: any
proto: any
host: any
inbound:
- port: any
proto: icmp
host: any
- port: any
proto: tcp
host: any
- Ejecutar el servicio en el nodo Raspberry
nebula -config /etc/nebula/config.yaml
.
Existen muchas opciones para este archivo de configuración así que lo mejor es leer la documentación del archivo.
Si todo marcha bien podrán contactarse entre nodos mediante un ping
a sus IPs de VPN.
Creando un servicio en systemd para Nebula
Una vez que la VPN ha sido probada podemos agregarla a los servicios que el equipo habilitará e iniciará en el arranque.
Para lograrlo hay que definir un archivo /etc/systemd/system/nebula.service
.
[Unit]
Description=Nebula VPN Service
After=network.target
[Service]
ExecStart=/usr/bin/nebula -config /etc/nebula/config.yml
Restart=always
User=root
Group=root
LimitNOFILE=4096
[Install]
WantedBy=multi-user.target
Posteriormente iniciarlo y activarlo en el arranque.
# Iniciar el servicio
systemctl start nebula
# Habilitar el servicio al arranque
systemctl enable nebula
# Observar su estado
systemctl status nebula
Reenviando peticiones a través de la VPN mediante Nginx.
El último paso para enrutar nuestros servicios públicos consiste en definir proxys Nginx en la VM que tengan como destino los equipos con mejores recursos conectados a través del tunel. Además, en dichos equipos se deben crear configuraciones Nginx de los sitios concretos.
Nextcloud
En mi caso el mejor ejemplo es una instalación de Nextcloud que utilizamos en la familia y que requiere más que los 20GB de almacenamiento disponibles en la VM.
El proxy Nginx en la VM se puede definir de la siguiente manera
# /etc/nginx/sites-enables/nube.sitio.net
server {
listen 80;
server_name nube.sitio.net;
location /.well-known {
alias /usr/share/nginx/html/.well-known;
}
location / {
proxy_pass http://10.10.10.2; # Reemplazar con la IP del equipo destino
proxy_set_header Host $host;
}
return 404;
}
En el equipo que ejecutará Nextcloud, se debe realizar la instalación correspondiente y posteriormente se debe crear el archivo Nginx como lo sugiere la documentación de Nextcloud.
Algo también importante para este caso es modificar la configuración de Nextcloud e indicar estamos sirviendo desde un proxy, modificando la sección trusted_domains
, trusted_proxies
y forwarded_for_headers
en el archivo config.php
.
// /var/www/nextcloud/config/config.php <<<Ejemplo de ruta>>>
<?php
$CONFIG = array (
[...]
'trusted_domains' =>
array (
0 => '10.10.10.1', // IP del proxy
1 => 'nube.sitio.net',
2 => '10.10.10.6', // IP del equipo
),
'trusted_proxies' =>
array (
0 => '10.10.10.1', // IP del proxy
),
'forwarded_for_headers' =>
array (
0 => 'HTTP_X_FORWARDED',
1 => 'HTTP_FORWARDED_FOR',
),
[...]
);
Toda configuración se realizará dependiendo del servicio o la aplicación que se quiera levantar y lo mejor es siempre verificar la documentación al respecto.
Este modelo nos permite hacer más tangibles los servicios que ofrecemos en la internet. Este es uno de los aspectos que la nube han deteriorado en nuestra relación con el cómputo: al ocultar efectivamente el equipo físico, ocultan también su impacto y las prácticas que agravan ese impacto.
Al ser un equipo conectado en casa podemos medir directamente su costo en nuestra cuenta de energía y podemos optar por apagarlo si es necesario, permitiéndonos entender que no todo lo que ofrecemos o utilizamos tiene porqué estar encendido todo el día durante todo el año.
Utilizando la red Nebula podemos también conectar sistemas Android y iOS, lo cual permitiría hospedar sitios o servicios en teléfonos que ya no utilicemos y en los que se pueda instalar dicho software.
Finalmente es bueno mencionar que tanto la información así como el cómputo se quedan en nuestras manos, deteriorando el modelo de acaparamiento y explotación de nuestra información.