Phase 1.5 — auth layer (Redis sessions, allowlist, requireAuth)
Some checks failed
Docker Build / build-and-push-image (push) Failing after 18s

Adds an authentication layer in front of the bot handlers :

- Auth handler on the principal bot (@arcodange_factory_bot, slug
  factory) parses /start, /auth <code>, /whoami, /logout. On a
  successful /auth, the message containing the code is best-effort
  deleted from the user's chat (replay defense).
- Redis-backed sessions (key tg-gw:auth:<from.id>, TTL 24h, configurable
  via AUTH_SESSION_TTL). Constant-time secret compare via crypto/subtle.
- ALLOWED_USERS env (CSV of Telegram user IDs) — silent-drops anyone
  not in the list before the auth gate runs.
- New per-bot field 'requireAuth' (pointer-bool). Default = true (secure
  by default). Auto-forced to false for handler=auth (chicken-and-egg).
- Server gates: allowlist first, then requireAuth before handler dispatch.
- Fail-at-startup if a bot is configured with handler=auth or
  requireAuth: true while AUTH_SECRET is unset.

Design: factory/docs/adr/20260509-telegram-gateway-auth.md (in factory PR).
User docs: AUTH.md (new), HOWTO_ADD_BOT.md (Cas 2 updated for default
true and gated flow).

New deps: github.com/redis/go-redis/v9.

Refs ~/.claude/plans/pour-les-notifications-on-inherited-seal.md § Phase 1.5.
This commit is contained in:
2026-05-09 13:56:30 +02:00
parent 6228169ac1
commit 07115e3162
15 changed files with 679 additions and 54 deletions

106
AUTH.md Normal file
View File

@@ -0,0 +1,106 @@
[← README](README.md) · [HOWTO_ADD_BOT](HOWTO_ADD_BOT.md) · **Authentification**
> **Détails de design** : [factory ADR 20260509 — telegram-gateway auth](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/docs/adr/20260509-telegram-gateway-auth.md)
# Authentification
Le gateway est protégé en deux couches :
1. **Allowlist** (`ALLOWED_USERS`, optionnelle) — filtre la liste des Telegram user IDs autorisés à parler à n'importe quel bot. Hors-liste : silent drop, le bot ne répond rien (l'inconnu ne sait même pas que le bot existe).
2. **Session `/auth`** — par défaut, **tous les bots du gateway exigent une session active** (`requireAuth: true` par défaut). Pour ouvrir une session, il faut DM `@arcodange_factory_bot` avec `/auth <code>`. Une session dure **24 h** (configurable via `AUTH_SESSION_TTL`).
## Côté utilisateur
### 1. Ouvrir une session
1. Sur Telegram, ouvre `@arcodange_factory_bot`.
2. Envoie `/auth <code>` (le code t'a été partagé par l'admin du gateway).
3. Le bot répond `✅ Authentifié pour 24 h`. Ton message contenant le code est **automatiquement supprimé** du chat (replay defense).
4. Tu peux maintenant DM les autres bots normalement.
### 2. Vérifier sa session
```
/whoami
```
Réponse type : `user=123456 : ✅ authentifié, reste 23h59m.` ou `user=123456 : non authentifié.`.
### 3. Fermer une session
```
/logout
```
### 4. Quand un bot répond `🔒 Authentifie-toi …`
Ta session a expiré (TTL dépassé) ou tu n'as jamais fait `/auth`. Va sur `@arcodange_factory_bot`, refais `/auth <code>`.
## Côté opérateur
### Définir / changer le code `AUTH_SECRET`
```bash
# Choisir un code fort (ex : openssl rand -hex 16)
SECRET='<chosen-code>'
kubectl -n telegram-gateway patch secret telegram-gateway-bots --type=json -p="[
{\"op\":\"replace\",\"path\":\"/data/AUTH_SECRET\",\"value\":\"$(echo -n "$SECRET" | base64)\"}
]"
# Restart pour reload du Secret
kubectl -n telegram-gateway rollout restart deploy/telegram-gateway
```
> Rotation : changer `AUTH_SECRET` invalide les **futurs** `/auth` mais **pas** les sessions Redis déjà ouvertes (elles vivent jusqu'à leur TTL). Pour invalider tout : `kubectl exec -n tools redis-0 -- redis-cli --scan --pattern 'tg-gw:auth:*' | xargs -I{} kubectl exec -n tools redis-0 -- redis-cli DEL {}`.
### Définir / changer l'allowlist
```bash
kubectl -n telegram-gateway patch secret telegram-gateway-bots --type=json -p="[
{\"op\":\"replace\",\"path\":\"/data/ALLOWED_USERS\",\"value\":\"$(echo -n '12345,67890' | base64)\"}
]"
kubectl -n telegram-gateway rollout restart deploy/telegram-gateway
```
Pour **désactiver** l'allowlist (ouvrir à tout Telegram user en allowlist) :
```bash
kubectl -n telegram-gateway patch secret telegram-gateway-bots --type=json -p='[
{"op":"remove","path":"/data/ALLOWED_USERS"}
]'
kubectl -n telegram-gateway rollout restart deploy/telegram-gateway
```
### Inspecter une session Redis
```bash
USER_ID=123456
kubectl exec -n tools redis-0 -- redis-cli GET "tg-gw:auth:$USER_ID" # → "1" ou (nil)
kubectl exec -n tools redis-0 -- redis-cli TTL "tg-gw:auth:$USER_ID" # → secondes restantes
kubectl exec -n tools redis-0 -- redis-cli DEL "tg-gw:auth:$USER_ID" # → forcer logout
```
### Rendre un bot public (opt-out de l'auth)
Dans `chart/values.yaml`, ajouter `requireAuth: false` au bot concerné :
```yaml
bots:
statusbot:
handler: echo
requireAuth: false # bot public, pas de session requise
```
> Cas spécial : `handler: auth` (le bot principal `factory`) force toujours `requireAuth: false` automatiquement (sinon l'auth elle-même serait inaccessible — chicken-and-egg).
## Limites connues
- **Pas de TOTP / OTP rotatif** — un code partagé suffit pour cet usage privé. À reconsidérer si on ouvre à plus d'utilisateurs.
- **Pas de rate-limit sur `/auth`** — l'`ALLOWED_USERS` joue le rôle de garde-fou. Avec un code fort (≥ 128 bits), le bruteforce est inutile dans la fenêtre de TTL.
- **Sessions invalidées si Redis tombe** — fail-closed : tous les bots gated répondent `🔒` jusqu'à ce que Redis revienne. Acceptable.
- **`from.id`-based, pas IP-based** — Telegram n'expose pas l'IP du user au bot. Une session couvre tous les devices d'un même compte Telegram, ce qui est le comportement attendu.
## Chemin de migration vers Vault (Phase 2+)
Aujourd'hui, `AUTH_SECRET` et `ALLOWED_USERS` vivent dans un `Secret` k8s créé via `kubectl create secret`. À terme, ils basculeront dans Vault (toggle `vault.enabled: true` dans `chart/values.yaml`, provisionner `kvv2/telegram-gateway/config`). Le code n'a pas à changer — `envFrom: secretRef` consomme indifféremment un Secret manuel ou un Secret produit par VaultStaticSecret.