Cuenta de externas, arreglos de seguridad, títulos arriba y despliegue

- Rol «externas» para la asamblea de externas: pide sin cupo, siempre con el
  colectivo de fuera y su contacto, y sus solicitudes salen en naranja hasta
  que la asamblea de la Enre las confirma.
- /pedir/ explica en una línea que los colectivos externos piden fecha en la
  asamblea de externas, los lunes de 19:00 a 20:00.
- Seguridad: argon2 limitado a dos cálculos a la vez y el intento se cuenta
  antes de calcular (60 accesos simultáneos llevaban el proceso a 3,8 GB);
  bloqueo por cuenta a 20 fallos para que no se pueda dejar fuera a la
  asamblea; avisos por sesión en vez de por URL; sig sin «..»; eventos de
  31 días como mucho; parámetros de argon2 acotados.
- Títulos pegados arriba en todas las páginas; la franja de la enredadera
  recupera ese alto para seguir llenando la pantalla.
- despliegue/: instalar.sh, cuenta.sh, servicio de systemd con el proceso
  encerrado, fragmento de nginx que además deja de servir .git y las notas
  internas, y configuración de ejemplo. Ninguna contraseña en el repositorio.
This commit is contained in:
SITO 2026-09-23 12:15:33 +02:00
parent ce3127baff
commit 7d5dbeec60
24 changed files with 606 additions and 96 deletions

69
despliegue/README.md Normal file
View file

@ -0,0 +1,69 @@
# Despliegue de /pedir/
El gestor de reservas es un programa en Go que escucha en `127.0.0.1:8090`.
nginx le pasa `/pedir/` y sigue sirviendo el resto de la web como hasta ahora.
| Qué | Dónde |
|---|---|
| programa y plantillas | `/opt/enrecal/` |
| configuración | `/etc/enrecal/config.json` |
| datos (registro, solo crece) | `/var/lib/enrecal/registro.jsonl` |
| accesos, para fail2ban | `/var/log/enrecal/auth.log` |
| servicio | `enrecal.service`, usuario `enrecal` |
## Primera vez
En el servidor, en la carpeta de la web (la que ya es un clon del repo):
git fetch && git checkout pedir-evento
sudo ./despliegue/instalar.sh
Después, dos cosas a mano:
1. Añadir esta línea dentro del `server{}` de www.laenre.net y recargar:
include /etc/nginx/snippets/enrecal.conf;
sudo nginx -t && sudo systemctl reload nginx
El fragmento también deja de servir `.git`, `gestor/`, `despliegue/`,
`README.md`, `context.txt` e `info.txt`, que ahora mismo se pueden
descargar desde la web.
2. Crear las cuentas. `cuenta.sh` para el servicio, crea la cuenta y lo
vuelve a arrancar:
sudo ./despliegue/cuenta.sh -usuario asamblea -nombre "Asamblea de La Enre" -rol enre -clave 'LA-CLAVE' -definitiva cuenta
sudo ./despliegue/cuenta.sh -usuario externas -nombre "Asamblea de externas" -rol externas cuenta
sudo ./despliegue/cuenta.sh -usuario hacklab -nombre "Hacklab La Raíz" cuenta
Sin `-clave`, se genera una al azar y se imprime: se entrega en mano y se
cambia al entrar. Con `-clave X` sin `-definitiva`, también se pide
cambiarla al entrar. Ninguna clave va en el repositorio.
Comprobar: `https://www.laenre.net/pedir/` carga y `https://www.laenre.net/.git/HEAD`
da 403.
## Actualizar
git pull
sudo ./despliegue/instalar.sh
No toca la configuración ni los datos.
## Si el servidor no tiene Go
`instalar.sh` compila si encuentra `go`. Si no, se compila en otra máquina y se
copia el binario a `despliegue/enrecal` antes de lanzar el instalador:
cd gestor && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false -o ../despliegue/enrecal .
scp ../despliegue/enrecal servidor:/ruta/de/la/web/despliegue/
## Día a día
- Otra clave para una cuenta: `sudo ./despliegue/cuenta.sh -usuario X clave`
- Registro del servicio: `journalctl -u enrecal`
- Copia de los datos: basta con copiar `/var/lib/enrecal/registro.jsonl`,
también con el servicio en marcha, porque solo se escribe al final.
- La configuración (cupo, ventana, qué finde es tranqui o con ruido) se cambia
en `/etc/enrecal/config.json` y se aplica con `sudo systemctl restart enrecal`.

View file

@ -0,0 +1,19 @@
{
"escucha": "127.0.0.1:8090",
"base_url": "https://www.laenre.net",
"dir_datos": "/var/lib/enrecal",
"dir_plantillas": "/opt/enrecal/plantillas",
"log_auth": "/var/log/enrecal/auth.log",
"cookie_segura": true,
"local": false,
"meses_vista": 4,
"reglas": {
"meses_ventana": 3,
"cupo_dias": 3,
"dias_minimo_confirmar": 7,
"dias_maximo_pendiente": 30,
"dias_reservables": [5, 6, 7],
"ancla_finde": "2026-09-25",
"turno_ancla": "ruido"
}
}

23
despliegue/cuenta.sh Executable file
View file

@ -0,0 +1,23 @@
#!/bin/sh
# Da de alta una cuenta o le cambia la clave, con el servicio parado.
#
# El gestor lee el registro al arrancar, así que una cuenta creada con el
# servicio en marcha no se vería hasta reiniciarlo. Este script lo para, hace
# la operación como el usuario enrecal (para que el registro no cambie de dueño)
# y lo vuelve a arrancar.
#
# sudo ./despliegue/cuenta.sh -usuario hacklab -nombre "Hacklab La Raíz" cuenta
# sudo ./despliegue/cuenta.sh -usuario hacklab clave
#
# Opciones: -rol enre|interno|externas, -clave X (si no, se genera una al
# azar), -definitiva (la clave no se pide cambiar al entrar).
set -eu
if [ "$(id -u)" -ne 0 ]; then
echo "Hay que lanzarlo con sudo." >&2
exit 1
fi
systemctl stop enrecal
trap 'systemctl start enrecal' EXIT
runuser -u enrecal -- /opt/enrecal/enrecal -config /etc/enrecal/config.json "$@"

View file

@ -0,0 +1,44 @@
[Unit]
Description=Gestor de reservas de La Enredadera (/pedir/)
After=network.target
[Service]
Type=simple
User=enrecal
Group=enrecal
ExecStart=/opt/enrecal/enrecal -config /etc/enrecal/config.json servir
Restart=on-failure
RestartSec=5
# Solo puede escribir en sus datos y en su registro de accesos.
ProtectSystem=strict
ReadWritePaths=/var/lib/enrecal /var/log/enrecal
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
NoNewPrivileges=true
CapabilityBoundingSet=
AmbientCapabilities=
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectClock=true
ProtectHostname=true
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged
UMask=0027
# Tope de memoria: el gestor limita argon2 a dos cálculos a la vez (unos
# 130 MB), así que 512 MB sobran y protegen al resto de servicios de la máquina.
MemoryMax=512M
[Install]
WantedBy=multi-user.target

77
despliegue/instalar.sh Executable file
View file

@ -0,0 +1,77 @@
#!/bin/sh
# Instala o actualiza el gestor de /pedir/ en el servidor.
#
# sudo ./despliegue/instalar.sh
#
# Se lanza desde la raíz del repositorio. Se puede repetir: la segunda vez solo
# actualiza el programa y las plantillas, y no toca ni la configuración ni los
# datos.
set -eu
RAIZ=$(cd "$(dirname "$0")/.." && pwd)
PROG=/opt/enrecal
DATOS=/var/lib/enrecal
LOGS=/var/log/enrecal
CONF=/etc/enrecal
if [ "$(id -u)" -ne 0 ]; then
echo "Hay que lanzarlo con sudo." >&2
exit 1
fi
# 1. El programa: se compila aquí si hay Go, o se usa el que se haya copiado a
# despliegue/enrecal (ver README.md).
if command -v go >/dev/null 2>&1; then
echo "Compilando..."
(cd "$RAIZ/gestor" && CGO_ENABLED=0 go build -trimpath -buildvcs=false -o "$RAIZ/despliegue/enrecal" .)
elif [ ! -x "$RAIZ/despliegue/enrecal" ]; then
echo "No hay Go en esta máquina ni un despliegue/enrecal ya compilado." >&2
echo "Compílalo en otra máquina y cópialo (ver despliegue/README.md)." >&2
exit 1
fi
# 2. Usuario propio, sin shell ni carpeta personal.
if ! id enrecal >/dev/null 2>&1; then
useradd --system --no-create-home --shell /usr/sbin/nologin enrecal
fi
# 3. Carpetas. El programa y las plantillas fuera de la web pública.
install -d -m 0755 "$PROG" "$PROG/plantillas"
install -d -m 0750 -o enrecal -g enrecal "$DATOS" "$LOGS"
install -d -m 0755 "$CONF"
install -m 0755 "$RAIZ/despliegue/enrecal" "$PROG/enrecal"
rm -f "$PROG"/plantillas/*.tmpl.html
install -m 0644 "$RAIZ"/gestor/plantillas/*.tmpl.html "$PROG/plantillas/"
if [ ! -f "$CONF/config.json" ]; then
install -m 0644 "$RAIZ/despliegue/config.ejemplo.json" "$CONF/config.json"
echo "Configuración nueva en $CONF/config.json"
fi
# 4. nginx: el límite de intentos va en http{} (conf.d) y el resto en un
# fragmento que se incluye con una línea dentro del server{} de la web.
if [ -d /etc/nginx ]; then
install -d -m 0755 /etc/nginx/conf.d /etc/nginx/snippets
install -m 0644 "$RAIZ/despliegue/nginx-zona.conf" /etc/nginx/conf.d/enrecal-zona.conf
install -m 0644 "$RAIZ/despliegue/nginx-laenre.conf" /etc/nginx/snippets/enrecal.conf
fi
# 5. systemd.
install -m 0644 "$RAIZ/despliegue/enrecal.service" /etc/systemd/system/enrecal.service
systemctl daemon-reload
systemctl enable enrecal >/dev/null 2>&1
systemctl restart enrecal
sleep 1
systemctl --no-pager --lines=3 status enrecal || true
cat <<'FIN'
Listo. Si es la primera vez, faltan dos cosas:
1. En el server{} de www.laenre.net, añadir esta línea y recargar nginx:
include /etc/nginx/snippets/enrecal.conf;
sudo nginx -t && sudo systemctl reload nginx
2. Crear las cuentas con despliegue/cuenta.sh (ver despliegue/README.md).
FIN

View file

@ -0,0 +1,28 @@
# Se incluye dentro del server{} de www.laenre.net:
# include /etc/nginx/snippets/enrecal.conf;
# La web se despliega con git, así que en la carpeta pública están también
# .git, el código del gestor y las notas internas. Nada de eso se sirve.
location ~ /\.(?!well-known) { deny all; }
location ~ ^/(gestor|despliegue)/ { deny all; }
location ~ ^/(README\.md|context\.txt|info\.txt)$ { deny all; }
location /pedir/ {
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
# Imprescindible pisar las dos: si no, cualquiera manda su propia
# X-Real-IP y se salta el bloqueo por IP del gestor.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
client_max_body_size 16k;
}
location = /pedir/entrar {
limit_req zone=enrecal burst=5 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
client_max_body_size 16k;
}

View file

@ -0,0 +1,3 @@
# Va dentro de http{}: en Debian, /etc/nginx/conf.d/ ya se incluye ahí.
# Diez intentos de acceso por minuto y por IP en /pedir/entrar.
limit_req_zone $binary_remote_addr zone=enrecal:1m rate=10r/m;