- 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.
99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
// Red
|
|
Escucha string `json:"escucha"` // siempre local: nginx hace de puerta
|
|
BaseURL string `json:"base_url"` // https://www.laenre.net, para comprobar el Origen
|
|
|
|
// Rutas
|
|
DirDatos string `json:"dir_datos"` // registro.jsonl
|
|
DirPlantillas string `json:"dir_plantillas"` // plantillas de /pedir/
|
|
DirEstatico string `json:"dir_estatico"` // raiz de la web, solo en modo local
|
|
LogAuth string `json:"log_auth"` // el registro que lee fail2ban
|
|
|
|
// Comportamiento
|
|
CookieSegura bool `json:"cookie_segura"` // false solo para probar sin TLS
|
|
Local bool `json:"local"` // sirve tambien la web estatica
|
|
MesesVista int `json:"meses_vista"` // meses que pinta /pedir/
|
|
|
|
Reglas Reglas `json:"reglas"`
|
|
}
|
|
|
|
func ConfigPorDefecto() Config {
|
|
return Config{
|
|
Escucha: "127.0.0.1:8090",
|
|
BaseURL: "https://www.laenre.net",
|
|
DirDatos: "/var/lib/enrecal",
|
|
DirPlantillas: "/opt/enrecal/plantillas",
|
|
DirEstatico: "/var/www/laenre",
|
|
LogAuth: "/var/log/enrecal/auth.log",
|
|
CookieSegura: true,
|
|
MesesVista: 4,
|
|
Reglas: ReglasPorDefecto(),
|
|
}
|
|
}
|
|
|
|
func CargarConfig(ruta string) (Config, error) {
|
|
c := ConfigPorDefecto()
|
|
if ruta == "" {
|
|
return c, nil
|
|
}
|
|
b, err := os.ReadFile(ruta)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
dec := json.NewDecoder(bytes.NewReader(b))
|
|
dec.DisallowUnknownFields()
|
|
if err := dec.Decode(&c); err != nil {
|
|
return c, fmt.Errorf("%s: %w", ruta, err)
|
|
}
|
|
return c, c.Validar()
|
|
}
|
|
|
|
func (c Config) Validar() error {
|
|
if c.Reglas.CupoDias < 1 {
|
|
return fmt.Errorf("cupo_dias tiene que ser 1 o mas")
|
|
}
|
|
if c.Reglas.MesesVentana < 1 {
|
|
return fmt.Errorf("meses_ventana tiene que ser 1 o mas")
|
|
}
|
|
if c.Reglas.DiasMinimoConfirmar < 0 {
|
|
return fmt.Errorf("dias_minimo_confirmar no puede ser negativo")
|
|
}
|
|
if c.Reglas.DiasMaximoPendiente < 1 {
|
|
return fmt.Errorf("dias_maximo_pendiente tiene que ser 1 o mas")
|
|
}
|
|
if len(c.Reglas.DiasReservables) == 0 {
|
|
return fmt.Errorf("dias_reservables no puede estar vacio")
|
|
}
|
|
if _, err := c.Reglas.AnclaFinde.Time(); err != nil {
|
|
return fmt.Errorf("ancla_finde: %w", err)
|
|
}
|
|
if c.Reglas.TurnoAncla != Tranqui && c.Reglas.TurnoAncla != Ruido {
|
|
return fmt.Errorf("turno_ancla solo puede ser %q o %q", Tranqui, Ruido)
|
|
}
|
|
if c.MesesVista < 1 {
|
|
return fmt.Errorf("meses_vista tiene que ser 1 o mas")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
inactividadSesion = 8 * time.Hour
|
|
maxIntentos = 5
|
|
// Por cuenta el tope es más alto que por IP: si fuera 5, cualquiera podría
|
|
// dejar a la asamblea sin entrar fallando 5 veces cada cuarto de hora. Con
|
|
// argon2 y claves de 12 caracteres, 20 intentos cada 10 minutos no bastan
|
|
// para adivinar nada.
|
|
maxIntentosCuenta = 20
|
|
ventanaIntentos = 10 * time.Minute
|
|
esperaBloqueo = 15 * time.Minute
|
|
)
|