⚗️ 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

  1. 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 un ip a.
  2. Instalar nebula y nebula-cert en un nodo principal o lighthouse.
  3. Crear un certificado principal para que todos los nodos puedan autenticarse en la red. nebula-cert ca -name "Mi red distribuida".
  4. 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 acceso root.
  5. 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.

  1. Ejecutar el servicio mediante nebula -config /etc/config.yaml.

  2. Copiar o transferir el certificado principal, ca.crt, y el certificado y la llave correspondiente a los distintos equipos.

  3. 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
  1. 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.


Comentarios