Sí, en este momento estas navegando y leyendo esto desde un pequeño computador en mi casa. El costo, menos de 20 mil pesos chilenos (por el nombre de dominio), más el plan de Internet de la casa. ¿Quieres saber qué es lo que hice?
La historia del nombre del dominio
Primero que todo, un nombre de dominio es el "nombre base" de un sitio web, por ejemplo, el de este blog es qo-op.cl, y tomando en cuenta el subdominio "r" es r.qo-op.cl.
El nombre de dominio nace de una forma anecdótica conversando con mis hijos (de 15 y 12 años).
Como informático, con la idea de montar "algo" con mis hijos en casa, que les permita acceder desde cualquier lado donde estén, pensé en poner a la "casa" un dominio corto de dos caracteres, algo así como una letra y un número, en este momento encontré disponible"y4", algo fácil de recordar para ellos y que no coincidiera alguna marca.
Al presentar mi "brillante" propuesta, mi primogénita respondió: -¿Podría el nombre ser una carita?-, mi reacción interna fue "Qué?!", pero dije -Solamente se pueden usar letras y números-. El resultado "qo-op" que es como una franja horizontal de una cara con lentes y orejas.
¿Qué tecnología hay detrás?
El dominio lo adquirí en nic.cl por un poco menos de 20 mil (que se paga cada dos años), lo demás es la conexión a Internet de mi casa y un NUC (Celeron® N2830, con 8GB en RAM y un SDD de 2TB), equipo que le cambié la memoria y el disco para poder "jugar" con esto (inicialmente 4GB en RAM y disco Hdd de 420GB).
No explicaré cómo instalar Debian, pues hay muchos lugares y videos que lo explican, yo hice una netinstall y no instalé escritorio gráfico, pues la idea es que sea un servidor y no un PC de escritorio, así que no quiero que se "pierda" RAM sirviendo la interfaz gráfica. La instalación de Docker y CasaOS es cortar y pegar (quizás haga un escrito breve después), así que me detendré un poco más en cómo dejé un dominio .cl apuntando a mi casa. Y luego en cómo es la instalación de los servicios en CasaOS.
Llegando a casa con un nombre de dominio
Este nuevo nombre, qo-oq.cl, en el servidor de nombre, le creé un alias en un servicio externo, sin embargo, bajo bind9 sería "qo-op.cl IN CNAME subdominio.duckdns.org.", estoy pensando en hacer un servidor interno/externo dns en casa, pero aún no lo he hecho. Si lo hago, escribiré la experiencia.
Con un cron se invoca la URL de duckdns para ir actualizando la IP, por ejemplo, creamos el archivo duck.sh en el directorio duckdns dentro del home y lo llamamos en el cron cada 5 minutos. En ese archivo habría que reemplazar "token" por el token entregado tras el registro.
vim ~/duckdns/duck.sh
#!/bin/bash
TOK="token";
echo url="https://www.duckdns.org/update?domains=subdominio&token=$TOK&ip=" | curl -k -o ~/duckdns/duck.log -K -
crontab -e
*/5 * * * * sh ~/duckdns/duck.sh > /dev/null
Ahora, los que me conocen y saben que me gustan los archivos bash, y como en un principio no tenía un dominio apuntando a casa, había pedido los 5 subdominios que ofrece duckdns. por lo que hice un script distinto. Además, pensando en no hacer gastar de más a duckdns con peticiones innecesarias, reviso localmente si mi ip ha cambiado, cosa de solicitar solo cuando cambie. Ahora, al llevar ese registro, además se qué tan seguido cambia mi IP.
Cree en /etc el directorio duckdns y allí el duck.sh, es decir
sudo mkdir /etc/duckdns
sudo vim /etc/duckdns/duck.sh
El contenido del script es:
#!/bin/bash
#set -e
# Los siguienets dos parametros cambian de usuario a usuario
TOKEN="mi_token_debo_ingresar_aca"
SUBDOM="subdominio1 subdominio2 subdominio3 subdominio4 subdominio5"
# directorio base de duckdns
DR="/etc/duckdns"
FECHA=$(date +"%Y-%m-%d %H:%M:%S UTC:%Z")
cd $DR
# crea un archivo si es que no existe
create_if_not_exist_file() {
TestFile=$1
if [ ! -f $TestFile ]; then
touch $TestFile
fi
}
# se asegura que existan los archivos necesarios
init() {
create_if_not_exist_file $DR/duckdns.log
create_if_not_exist_file $DR/mi-ip-actual.txt
create_if_not_exist_file $DR/mi-ip.txt
for sub in $(echo $SUBDOM); do
create_if_not_exist_file $DR/duck-${sub}.log
done
}
# actualiza la ip en duckdns
actualizaDns() {
httping -c 1 www.duckdns.org 2>&1 > /dev/null
if [ $? -eq 0 ]; then
for dom in $(echo $SUBDOM); do
echo url="https://www.duckdns.org/update?domains=$dom&token=$TOKEN&ip=" | curl -s -k -o $DR/duck-${dom}.log -K -
done
return 0
else
return 1
fi
}
# proceso principal
init
ping -q -c 1 -W 1 ip.9a.cl 2>&1 > /dev/null
if [ $? -ne 0 ]; then
echo "$FECHA ERROR: Falló encontrando ip.9a.cl" >> $DR/duckdns.log
exit 1
else
curl -s https://ip.9a.cl > $DR/mi-ip.txt
IP=$(cat $DR/mi-ip.txt)
diff -N -q $DR/mi-ip-actual.txt $DR/mi-ip.txt > /dev/null
if [ $? -ne 0 ]; then
actualizaDns
if [ $? -eq 0 ]; then
echo "$FECHA INFO: Actualizando IP a $IP" >> $DR/duckdns.log
cp -au $DR/mi-ip.txt $DR/mi-ip-actual.txt
else
echo "$FECHA ERROR: Falló encontrando duckdns.org" >> $DR/duckdns.log
exit 1
fi
fi
fi
exit 0
Para que el script anterior funcione bien debe estar instalado curl y httping es decir, hay que hacer un: sudo apt install -y curl httping
La llamada por cron también cambia por:
sudo cat > /var/spool/cron/crontabs/root <<EOF
# m h dom mon dow command
*/5 * * * * sh /etc/duckdns/duck.sh 2>&1 /dev/null
EOF
Con eso cuando cambia la dirección IP de mi casa, actualiza mi subdominio de duckdns, y por el CNAME, indirectamente, mi dominio "qo-op.cl"
Finalmente el log (/etc/duckdns/duckdns.log) sera similar a:
2024-02-20 09:55:01 UTC:-03 INFO: Actualizando IP a 201.246.63.98
2024-02-28 20:50:01 UTC:-03 ERROR: Falló encontrando ip.9a.cl
2024-02-29 07:20:01 UTC:-03 ERROR: Falló encontrando duckdns.org
2024-03-02 12:55:01 UTC:-03 INFO: Actualizando IP a 201.189.202.223
2024-03-05 15:55:01 UTC:-03 INFO: Actualizando IP a 201.189.198.124
2024-03-08 18:05:01 UTC:-03 ERROR: Falló encontrando ip.9a.cl
2024-03-08 18:10:01 UTC:-03 INFO: Actualizando IP a 201.189.198.110
2024-03-09 02:15:01 UTC:-03 ERROR: Falló encontrando ip.9a.cl
2024-03-15 00:10:01 UTC:-03 INFO: Actualizando IP a 201.189.223.154
Si quieres modificar el log para que quede en /var/log/duckdns, adelante, siempre hay cosas que mejorar, pero a mi me dio flojera porque vi que los cambios son lejanos, al menos en mi caso, por lo que no agregué un logrotate ni nada de eso.
Llego a casa, ¿ahora qué?
Lo primero que debes tener en cuenta es la configuración de "tu router" o el router que instala en casa la compañía proveedora de Internet, dado a que debes dirigir puertos o la navegación completa al equipo que dejarás de servidor. Lo segundo, dado a que vas a abrir tu servidor al mundo, debes tener protección.
[Lo importante de este párrafo, mi red interna es 192.168.0.0/23] Cambié la configuración del DHCP de 192.168.1.0/24 a 192.168.0.0/23, ¿porqué? porque en casa tengo muchas IP y las quería dejar ordenadas, no tengo tanto dispositivo como para llenar las 510 IP, pero quería dividir los fonos, tablet y notebook de los "smart home", no dejan de ser 9 luces, un enchufe, una lavadora, 2 puntos de acceso wifi, 3 cámaras, una central de monitoreo de dispositivos bluetooth (temperatura, humedad, humo), un google nest y un google chromecast, más el router, es la suficiente cantidad como para tenerlos por aparte de los otros equipos que se conectan a Internet a "navegar". Como el equipo de movistar (el router) no era capaz de manejar tanto dispositivo, terminé instalando un DHCP propio. Así lograr asignar IP fija por sus mac address. Prometo que lo documentaré en otro post.
¿Porqué tanto bla bla?, para que entiendas mejor algunas reglas de mi firewall, pues dado a que estas abriendo tu equipo al mundo, le debes colocar protección. Particularmente, usé el firewall predeterminado de Debian 12 "nftables", que se plantea como el sucesor de "iptables". Debo reconocer que iptables tras tantos años lo entendía bien, el cambio no me fue fácil. Mi dispositivo de red es "enp3s0", pero puede cambiar en el tuyo.
Qué puertos estoy abriendo al mundo, el 22, el 80 y el 443. De los cuales el 80 y 443 los estoy dejando abiertos desde los servicios prestados por docker. Esto es muy importante de tener en cuenta, pues cualquier cosa que instales allí queda abierta al mundo, por ejemplo, tu impresora, tu cámara o tus respaldos. En la tabla "filter" chain INPUT está permitido todo el tráfico propio, el de mi red (192.168.0.0/23) y el de la red docker (172.16.0.0/12). En el chain FORWARD, envío el trafico al chain DOCKER-USER, en donde filtro prohibiendo todo trafico que venga desde fuera de mi red que llegue a mi tarjeta de red "enp3s0" (la que está conectada al router), aceptando previamente solo el trafico establecido y los puertos 80 y 443, que los manejo desde CasaOS.
cat /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
table ip filter {
chain INPUT {
type filter hook input priority filter; policy drop;
ct state established,related accept
iifname "lo" accept
icmp type echo-request accept
ip saddr 192.168.0.0/23 accept
ip saddr 172.16.0.0/12 accept
tcp dport { 22, 80, 443 } accept
}
chain FORWARD {
type filter hook forward priority filter; policy accept;
counter packets 187799153 bytes 64671916463 jump DOCKER-USER
}
chain OUTPUT {
type filter hook output priority filter; policy accept;
}
chain DOCKER {
}
chain DOCKER-ISOLATION-STAGE-1 {
}
chain DOCKER-ISOLATION-STAGE-2 {
}
chain DOCKER-USER {
tcp dport { 80, 443 } accept
ct state established,related accept
iifname "enp3s0" ip saddr != 192.168.0.0/23 drop
}
}
table ip nat {
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
}
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
}
chain OUTPUT {
type nat hook output priority -100; policy accept;
}
}
Otra cosa, si bien, nft quedó instalado junto con la instalación inicial de mi Debian, debí dejarlo activo, esto se hace así:
sudo systemctl enabled nftables.service
Al revisar el archivo del servicio /lib/systemd/system/nftables.service, se puede observar que el lugar desde donde carga sus reglas es /etc/nftables.conf que es el archivo que creamos allí arriba.
Servicios en CasaOS
Básicamente en CasaOS, lo primero que hice fue sacar CasaOS del puerto 80, así liberarlo para el proxy reverso. para eso fui a los ajustes (el que tiene el icono como un ecualizador), y cambié el "Language" a Español, el "Puerto de la WebUI" a 180... puede ser realmente cualquiera menos el 80 y 443 que queremos dejar libres.

Luego, instalé un "Nginx proxy manager". Para eso hice click en la "App Store" y en el buscador puse "proxy".


Luego puse "instalar" en el botón de "Nginx proxy manager" (en el mio dice Abrir porque ya está instalado). Desde allí, se puede dirigir la configuración de cualquier servicio web que se instale desde el interior de nuestro "servidor" hacia Internet, por ejemplo, yo instalé ghost, que es este blog, el cual desde "Nginx proxy manager" lo apunto así:

Importante, la dirección r.qo-op.cl la agregué también en el servidor de nombres con un CNAME a qo-op.cl.
Una de las grandes ventajas de "Nginx proxy manager" es que está integrado con "Let's Encrypt" para tener Certificados SSL y así tu página este cifrada y con acceso https.
Algo que me encantó de CasaOS es que no está cerrado solamente a instalar aplicaciones desde el "App Store", sino que mediante el botón "+" puedes hacer una instalación de cualquier docker-compose, por ejemplo, para mi ghost, instalé desde el "App Store" la base de datos MariaDB y mediante el docker-compose ghost.
name: ghost
services:
ghost:
cpu_shares: 90
command: []
container_name: ghost
deploy:
resources:
limits:
memory: 512M
environment:
- database__client=mysql
- database__connection__database=ghost
- database__connection__host=192.168.0.255
- database__connection__password=**********
- database__connection__user=********
- mail__options__auth__pass=******
- mail__options__auth__user=******
- mail__options__host=******
- mail__options__port=***
- mail__options__service=*******
- mail__transport=SMTP
- url=https://r.qo-op.cl
- admin__url=http://192.168.0.255:2368
hostname: ghost
image: ghost:latest
ports:
- target: 2368
published: "2368"
protocol: tcp
restart: unless-stopped
volumes:
- type: bind
source: /DATA/AppData/ghost/content
target: /var/lib/ghost/content
devices: []
cap_add: []
network_mode: bridge
privileged: false
x-casaos:
author: Roy
category: Blog
hostname: ""
icon: https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/ghost.png
index: /
port_map: 2368
scheme: http
store_app_id: ghost
title:
custom: Ghost
Mi archivo es este, pero en vez de los ***** tiene los valores de configuración. Mi Host o servidor, tiene la IP 192.168.0.255, y sí, en una red /23, esa dirección es válida.
Éxito en su implementación.
Member discussion: