web frontal = reverse proxy + WAF ; le dorsal porte les sites
Le frontal derive ses vhosts des expositions publiques (meme derivation que l edge), filtre par ModSecurity v3 + OWASP CRS (DetectionOnly d abord, eprouve), refuse les noms inconnus, et ne sert plus rien lui-meme. Les sites statiques passent au dorsal, relaye par l edge et par le frontal. Sondes : frontal (nouvelle), sites-servis suit le dorsal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2903d75831
commit
3fe7e3cea3
21 changed files with 401 additions and 166 deletions
|
|
@ -122,12 +122,11 @@
|
|||
| `serveur_rspamd` | ingress | 11332 | tcp | localhost | clair | Protocole milter consommé par Postfix co-localisé (analyse + signature DKIM). Local uniquement. |
|
||||
| `serveur_rspamd` | ingress | 11334 | tcp | localhost | clair | Interface de contrôle rspamd (statistiques, apprentissage) en local. |
|
||||
| `serveur_step_ca` | ingress | 8443 | tcp | flotte | tls-requis | ACME + API step-ca : chaque nœud (client_pki) émet/renouvelle ses certificats et récupère la racine. |
|
||||
| `serveur_web_dorsal` | ingress | 80 | tcp | edge | clair | Front nginx local des webapps, proxie par l'edge (TLS termine a l'edge). |
|
||||
| `serveur_web_dorsal` | ingress | 80 | tcp | edge, serveur_web_frontal | clair | Front nginx local des webapps et des sites statiques, relaye par l'edge (noms internes) et par le web frontal (noms publics). |
|
||||
| `serveur_web_dorsal` | egress | 443 | tcp | flotte | tls | git clone/pull du depot de chaque app (Forgejo souverain) au deploiement. |
|
||||
| `serveur_web_frontal` | ingress | 80 | tcp | edge | clair | Contenu statique servi au navigateur via l'edge (TLS termine a l'edge). |
|
||||
| `serveur_web_frontal` | ingress | 80 | tcp | externe | clair | HTTP public — le premier contact d'un navigateur, et le defi HTTP d'ACME ; renvoie vers HTTPS. |
|
||||
| `serveur_web_frontal` | ingress | 443 | tcp | externe | tls-requis | HTTPS public — les sites et services que le locataire publie sur l'Internet. |
|
||||
| `serveur_web_frontal` | egress | 443 | tcp | flotte | tls | git clone/pull du depot du site (Forgejo souverain) au deploiement. |
|
||||
| `serveur_web_frontal` | ingress | 80 | tcp | externe | clair | HTTP public, relaye a travers le WAF — et le defi HTTP d'ACME ; renverra vers HTTPS quand le certificat sera la. |
|
||||
| `serveur_web_frontal` | ingress | 443 | tcp | externe | tls-requis | HTTPS public — les sites et services que le locataire publie sur l'Internet (attend son certificat Let's Encrypt). |
|
||||
| `serveur_web_frontal` | egress | 80 | tcp | serveur_web_dorsal | clair | Relais vers le web dorsal, qui porte les sites et les applications publiques. |
|
||||
|
||||
## Synthèse chiffrement
|
||||
|
||||
|
|
@ -135,6 +134,6 @@
|
|||
- **n-a** : 8 flux
|
||||
- **ssh** : 8 flux
|
||||
- **starttls** : 6 flux
|
||||
- **tls** : 11 flux
|
||||
- **tls** : 10 flux
|
||||
- **tls-cible** : 2 flux
|
||||
- **tls-requis** : 36 flux
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
# serveur_web_dorsal
|
||||
|
||||
Hébergement de **webapps dynamiques** (runtime + service systemd), derrière l'edge.
|
||||
Deuxième brique de la **plateforme webapp** de Set-OPS : **100 % natif** — venv/paquets
|
||||
+ systemd + nginx, **zéro conteneur**. Codifié depuis le spike du 2026-07-05
|
||||
(monregistraire : FastAPI + uvicorn + SQLite).
|
||||
**Le serveur qui porte les sites et les applications web du locataire** : webapps
|
||||
dynamiques (runtime + service systemd) et, depuis le 2026-09-29, sites **statiques**
|
||||
(repris du web frontal). **100 % natif** — venv/paquets + systemd + nginx, **zéro
|
||||
conteneur**. Codifié depuis le spike du 2026-07-05 (monregistraire : FastAPI + uvicorn +
|
||||
SQLite ; site Alliance Boréale).
|
||||
|
||||
Il est relayé par **deux portes** : l'**edge** pour les noms internes (l'administration),
|
||||
le **web frontal** (reverse proxy + WAF) pour les noms publics (l'Internet).
|
||||
|
||||
## Patron
|
||||
Pour chaque app déclarée dans `serveur_web_dorsal_apps` :
|
||||
|
|
@ -30,6 +34,21 @@ serveur_web_dorsal_apps:
|
|||
```
|
||||
Et une app `groupe: serveur_web_dorsal, hote: <noeud>, port: 80, expose: [registraire.<domaine>]`.
|
||||
|
||||
## Sites statiques
|
||||
Pour chaque site déclaré dans `serveur_web_dorsal_sites` : `git clone`/`pull` →
|
||||
`/srv/web/<nom>`, puis un vhost nginx servant `<sous_dossier>` (ex. `public/`), avec
|
||||
en-têtes de sécurité + CSP (défaut : `self` only, aucun tiers), gzip, cache.
|
||||
```yaml
|
||||
serveur_web_dorsal_sites:
|
||||
- nom: alliance
|
||||
server_name: "alliance-boreale.chezlepro.ca" # le FQDN que le plan expose
|
||||
depot: "https://forge.{{ domaine_interne }}/alliance/site-alliance-boreale.git"
|
||||
branche: main
|
||||
sous_dossier: public
|
||||
```
|
||||
Et dans `applications.yml` : `groupe: serveur_web_dorsal, hote: <noeud>, port: 80,
|
||||
expose: [<FQDN>]`. Sonde `sites-servis` : un GET par `server_name`.
|
||||
|
||||
## BD
|
||||
Les apps à état **local** (SQLite) : `data_dir` persistant. Les apps à **BD réseau**
|
||||
(PostgreSQL) : déclarer la base dans `bases-donnees.yml` et passer le DSN via `env`
|
||||
|
|
@ -41,4 +60,5 @@ paquets de runtime (le patron systemd + nginx + git reste identique).
|
|||
|
||||
## Validation
|
||||
`ansible-lint` profil production + `--syntax-check`. Éprouvé sur web-frontal-01
|
||||
(monregistraire servi via l'edge, HTTPS step-ca, API fonctionnelle).
|
||||
(monregistraire servi via l'edge, HTTPS step-ca, API fonctionnelle) — avant que le
|
||||
frontal ne devienne le reverse proxy public.
|
||||
|
|
|
|||
|
|
@ -29,3 +29,30 @@ serveur_web_dorsal_apps: []
|
|||
# `/api/...` est parfaitement saine sans rien servir a `/`. Ce qui reste exclu : les 5xx
|
||||
# et l'absence de reponse, c'est-a-dire l'app coincee ou morte.
|
||||
serveur_web_dorsal_sonde_codes: "HTTP/1.1 200,HTTP/1.1 302,HTTP/1.1 401,HTTP/1.1 403,HTTP/1.1 404"
|
||||
|
||||
# --- LES SITES STATIQUES (2026-09-29, repris du web frontal) -------------------------------
|
||||
#
|
||||
# LE DORSAL PORTE LES SITES ; LE FRONTAL NE SERT RIEN LUI-MEME. Le web frontal est le reverse
|
||||
# proxy et le WAF devant les services web publics du locataire ; ce qui est servi — les
|
||||
# applications comme les sites statiques — vit ici. Un site statique : 100 % natif, nginx,
|
||||
# le contenu cloné d'un depot git souverain.
|
||||
serveur_web_dorsal_sites_racine: "/srv/web"
|
||||
|
||||
# En-tetes de securite par defaut (sobriete + vie privee : tout local, pas de tiers).
|
||||
serveur_web_dorsal_csp_defaut: >-
|
||||
default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self';
|
||||
object-src 'none'; base-uri 'self'; frame-ancestors 'self'
|
||||
|
||||
# Liste des sites statiques servis par ce noeud. Chaque entree :
|
||||
# nom identifiant court (dossier + vhost)
|
||||
# server_name FQDN servi — celui que le plan EXPOSE (applications.yml -> edge ou frontal)
|
||||
# depot URL du depot git du site (souverain, ex. Forgejo)
|
||||
# branche branche a servir (defaut: main)
|
||||
# sous_dossier sous-dossier du depot a servir comme racine (ex: public)
|
||||
# csp Content-Security-Policy specifique (optionnel)
|
||||
serveur_web_dorsal_sites: []
|
||||
|
||||
# LE PORT DES VHOSTS LOCAUX DES SITES. En clair sur cette machine : c'est le frontal (ou
|
||||
# l'edge, pour un nom interne) qui les recoit et les relaie. La sonde mesure ce qu'il ira
|
||||
# chercher — si le frontal est vert et le site muet, la panne est ici.
|
||||
serveur_web_dorsal_sites_port: 80
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@
|
|||
# Flux reseau du dorsal web (propriete du role). Voir docs/flux-conception.md.
|
||||
# L'app ecoute en LOCALHOST (pas de flux inter-noeud) ; le nginx local la fronte.
|
||||
flux:
|
||||
# LE DORSAL EST RELAYE PAR DEUX PORTES (2026-09-29) : l'edge pour les noms internes
|
||||
# (l'administration), le web frontal pour les noms publics (l'Internet, a travers le WAF).
|
||||
- sens: ingress
|
||||
port: 80
|
||||
protocole: tcp
|
||||
pair: edge
|
||||
pair: [edge, serveur_web_frontal]
|
||||
chiffrement: clair
|
||||
raison: "Front nginx local des webapps, proxie par l'edge (TLS termine a l'edge)."
|
||||
raison: "Front nginx local des webapps et des sites statiques, relaye par l'edge (noms internes) et par le web frontal (noms publics)."
|
||||
- sens: egress
|
||||
port: 443
|
||||
protocole: tcp
|
||||
|
|
|
|||
|
|
@ -28,3 +28,10 @@ sondes:
|
|||
jamais en « failed » — elle se coince : le processus vit, systemd la dit active, le
|
||||
port est ouvert, et plus une requete n''aboutit. Le rapport d''unites en echec ne
|
||||
verra jamais ca.'
|
||||
# SUR LE DORSAL DEPUIS LE 2026-09-29 : c'est lui qui porte les sites ; le frontal les relaie.
|
||||
- nom: sites-servis
|
||||
ttl: 5400
|
||||
raison: 'Chacun des sites statiques declares se sert-il encore ? Le contenu vient
|
||||
d''un depot git : une branche renommee, un sous-dossier deplace ou un clone vide
|
||||
laissent nginx parfaitement vert avec un site perime, vide, ou en 404. Seul un GET
|
||||
sur le `server_name` de chaque site le dit.'
|
||||
|
|
|
|||
|
|
@ -107,6 +107,49 @@
|
|||
label: "{{ item.nom }}"
|
||||
when: not ansible_check_mode
|
||||
|
||||
# LES SITES STATIQUES (2026-09-29) — repris du web frontal, qui ne sert plus rien lui-meme.
|
||||
- name: Creer la racine des sites statiques
|
||||
ansible.builtin.file:
|
||||
path: "{{ serveur_web_dorsal_sites_racine }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: serveur_web_dorsal_sites | length > 0
|
||||
|
||||
- name: Cloner / mettre a jour le depot de chaque site (clone superficiel, contenu courant)
|
||||
ansible.builtin.git:
|
||||
repo: "{{ item.depot }}"
|
||||
dest: "{{ serveur_web_dorsal_sites_racine }}/{{ item.nom }}"
|
||||
version: "{{ item.branche | default('main') }}"
|
||||
depth: 1
|
||||
force: true
|
||||
loop: "{{ serveur_web_dorsal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
|
||||
- name: Deployer le vhost de chaque site
|
||||
ansible.builtin.template:
|
||||
src: site.conf.j2
|
||||
dest: "/etc/nginx/sites-available/site-{{ item.nom }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ serveur_web_dorsal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Recharger nginx serveur_web_dorsal
|
||||
|
||||
- name: Activer le vhost de chaque site
|
||||
ansible.builtin.file:
|
||||
src: "/etc/nginx/sites-available/site-{{ item.nom }}"
|
||||
dest: "/etc/nginx/sites-enabled/site-{{ item.nom }}"
|
||||
state: link
|
||||
loop: "{{ serveur_web_dorsal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Recharger nginx serveur_web_dorsal
|
||||
|
||||
# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et
|
||||
# pousse le resultat ; les greffons standard, eux, sont poses par le porteur.
|
||||
#
|
||||
|
|
@ -127,3 +170,14 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
||||
# LE CONTENU D'UN SITE N'EST PAS DANS CE ROLE : il arrive par git depuis une forge. Une
|
||||
# branche renommee, un sous-dossier deplace ou un clone vide laissent nginx parfaitement
|
||||
# vert. Seul un GET par `server_name` le dit.
|
||||
- name: Deposer la sonde « sites-servis »
|
||||
ansible.builtin.template:
|
||||
src: sonde-sites-servis.sh.j2
|
||||
dest: /usr/local/lib/setops/sondes/sites-servis.sh
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
# Site statique {{ item.nom }} — genere par Set-OPS (role serveur_web_frontal). Ne pas editer.
|
||||
# Site statique {{ item.nom }} — genere par Set-OPS (role serveur_web_dorsal). Ne pas editer.
|
||||
# Sert derriere l'edge (TLS termine a l'edge). Racine = depot git / sous-dossier.
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ item.server_name }};
|
||||
root {{ serveur_web_frontal_racine_base }}/{{ item.nom }}{{ ('/' ~ item.sous_dossier) if (item.sous_dossier | default('')) else '' }};
|
||||
root {{ serveur_web_dorsal_sites_racine }}/{{ item.nom }}{{ ('/' ~ item.sous_dossier) if (item.sous_dossier | default('')) else '' }};
|
||||
index index.html;
|
||||
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Content-Security-Policy "{{ (item.csp | default(serveur_web_frontal_csp_defaut)) | trim }}" always;
|
||||
add_header Content-Security-Policy "{{ (item.csp | default(serveur_web_dorsal_csp_defaut)) | trim }}" always;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/css application/javascript text/html image/svg+xml;
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
# la sequence `{#`, que Jinja lirait comme un debut de commentaire. Le shell en a
|
||||
# besoin ; c'est donc Jinja qui cede. Meme geste que `client_sante/setops-sante.sh.j2`
|
||||
# et `serveur_nginx/sonde-edge.sh.j2`.)
|
||||
# GENERE par Set-OPS (role serveur_web_frontal). Ne pas editer a la main.
|
||||
# GENERE par Set-OPS (role serveur_web_dorsal). Ne pas editer a la main.
|
||||
#
|
||||
# SONDE « sites-servis » — chacun des sites declares se sert-il encore ?
|
||||
#
|
||||
|
|
@ -18,9 +18,9 @@
|
|||
# mesurerait le serveur par defaut, c'est-a-dire n'importe quoi d'autre.
|
||||
set -uo pipefail
|
||||
|
||||
PORT={{ serveur_web_frontal_sonde_port }}
|
||||
noms=({% for s in serveur_web_frontal_sites | default([]) %}'{{ s.server_name }}' {% endfor %})
|
||||
etiquettes=({% for s in serveur_web_frontal_sites | default([]) %}'{{ s.nom }}' {% endfor %})
|
||||
PORT={{ serveur_web_dorsal_sites_port }}
|
||||
noms=({% for s in serveur_web_dorsal_sites | default([]) %}'{{ s.server_name }}' {% endfor %})
|
||||
etiquettes=({% for s in serveur_web_dorsal_sites | default([]) %}'{{ s.nom }}' {% endfor %})
|
||||
|
||||
if (( ${#noms[@]} == 0 )); then
|
||||
# PAS UN VERT MUET. Un hebergeur sans site declare est un etat legitime — le dire vaut
|
||||
|
|
@ -1,37 +1,34 @@
|
|||
# serveur_web_frontal
|
||||
|
||||
Hébergement de **sites web statiques** (nginx), derrière l'edge. Première brique de
|
||||
la **plateforme webapp** de Set-OPS : **100 % natif** — pas de conteneur, pas de
|
||||
runtime, pas de base de données. Le contenu vient d'un **dépôt git souverain**
|
||||
(Forgejo). Codifié depuis le spike du 2026-07-05 (site Alliance Boréale).
|
||||
**Reverse proxy et WAF devant les services web PUBLICS du locataire.** C'est la porte
|
||||
publique : la frontière redirige l'adresse publique que le site attribue au locataire
|
||||
(80/443) vers lui ; il filtre et relaie. **Il ne sert rien lui-même** — les sites et les
|
||||
applications vivent sur le web **dorsal** (`serveur_web_dorsal`).
|
||||
|
||||
L'**edge** (`serveur_nginx`) est l'autre porte : les services **internes** (`*.internal`),
|
||||
pour la zone d'administration. Les deux ne se remplacent pas.
|
||||
|
||||
## Ce que le rôle fait
|
||||
Pour chaque site déclaré dans `serveur_web_frontal_sites` :
|
||||
1. `git clone`/`pull` du dépôt → `/srv/web/<nom>` ;
|
||||
2. vhost nginx servant `<sous_dossier>` (ex. `public/`), avec en-têtes de sécurité
|
||||
+ CSP (défaut : `self` only, aucun tiers, aucun pistage), gzip, cache, `try_files`.
|
||||
1. nginx + **ModSecurity v3** (`libnginx-mod-http-modsecurity`) + **OWASP CRS**
|
||||
(`modsecurity-crs`), tous paquets Debian, tout libre ;
|
||||
2. compose le WAF (`/etc/nginx/setops-waf/main.conf`) : moteur, mode, CRS et ses
|
||||
exclusions — l'inclusion livrée par Debian ne charge que le moteur ;
|
||||
3. un vhost par **exposition publique** : les FQDN que `applications.yml` expose dans un
|
||||
domaine dont `plan/domaines.yml` désigne ce frontal (`edge: serveur_web_frontal`) —
|
||||
la même dérivation que l'edge ;
|
||||
4. un serveur par défaut qui **refuse tout nom inconnu** (`return 444`) ;
|
||||
5. la sonde `frontal` : nginx actif et configuration valide, WAF chargé et CRS présent,
|
||||
chaque exposition publique servie à travers lui.
|
||||
|
||||
Exposé via `applications.yml` (champ `expose`) → l'edge proxie en HTTPS (TLS step-ca).
|
||||
Les SAN du cert edge et le plancher /etc/hosts sont **auto-dérivés du plan**.
|
||||
## Le WAF
|
||||
`serveur_web_frontal_waf_mode: DetectionOnly` d'abord : le CRS journalise ce qu'il aurait
|
||||
bloqué (`/var/log/nginx/modsec_audit.log`) sans rien refuser. On lit, on ajuste les
|
||||
exclusions pour les applications réelles, **puis** `On`. Éprouvé le 2026-09-29 (Debian 13,
|
||||
nginx 1.26, CRS 3.3.7) : une injection SQL est journalisée (942100) en détection, refusée
|
||||
(403) en blocage ; une requête saine passe dans les deux cas.
|
||||
|
||||
## Déclaration (group_vars du groupe serveur_web_frontal)
|
||||
```yaml
|
||||
serveur_web_frontal_sites:
|
||||
- nom: alliance
|
||||
server_name: "alliance-boreale.{{ domaine_interne }}"
|
||||
depot: "https://forge.{{ domaine_interne }}/alliance/site-alliance-boreale.git"
|
||||
branche: main
|
||||
sous_dossier: public
|
||||
# csp: "..." # optionnel, sinon serveur_web_frontal_csp_defaut
|
||||
```
|
||||
Et dans `applications.yml` : une app `groupe: serveur_web_frontal, hote: <noeud>, port: 80,
|
||||
expose: [alliance-boreale.<domaine>]`.
|
||||
|
||||
## Portée
|
||||
Statique seulement (le **frontal**). Les apps **dynamiques** (runtime + service
|
||||
systemd + BD via resoudre_base) relèveront d'un rôle *dorsal* à venir — même éthos :
|
||||
natif, systemd, zéro conteneur.
|
||||
## TLS
|
||||
En clair (80) tant que le certificat **Let's Encrypt** des noms publics n'est pas en place.
|
||||
|
||||
## Validation
|
||||
`ansible-lint` profil production + `--syntax-check`. Éprouvé sur web-frontal-01
|
||||
(site servi via l'edge, HTTPS step-ca vérifié, CSP en place).
|
||||
`ansible-lint` profil production + `--syntax-check` ; sonde `frontal`.
|
||||
|
|
|
|||
|
|
@ -1,29 +1,40 @@
|
|||
---
|
||||
# =========================================================================
|
||||
# serveur_web_frontal — hebergement de sites web STATIQUES (nginx), derriere l'edge.
|
||||
# Premiere brique de la « plateforme webapp » : 100% natif (pas de conteneur,
|
||||
# pas de runtime, pas de BD). Le contenu vient d'un depot git souverain.
|
||||
# Les apps DYNAMIQUES (service systemd + BD) viendront via un r_le dorsal.
|
||||
# Codifie depuis le spike du 2026-07-05 (site Alliance Boreale).
|
||||
# serveur_web_frontal — REVERSE PROXY + WAF devant les services web PUBLICS du locataire.
|
||||
#
|
||||
# L'EDGE ET LE FRONTAL SONT DEUX PORTES (2026-09-29). L'edge (`serveur_nginx`) expose les
|
||||
# services INTERNES (`*.internal`) a la zone d'administration. Le frontal expose ce que le
|
||||
# locataire publie sur l'Internet : la frontiere redirige l'adresse publique du locataire
|
||||
# (80/443) vers lui, il filtre (WAF) et relaie vers le service — le plus souvent le web
|
||||
# DORSAL, qui porte les sites et les applications. Le frontal ne sert rien lui-meme.
|
||||
#
|
||||
# SES VHOSTS NE SE DECLARENT PAS ICI : ils derivent des expositions dont le domaine PUBLIC
|
||||
# le designe (`plan/domaines.yml`, champ `edge: serveur_web_frontal`) — la meme derivation
|
||||
# que l'edge (`expositions_des_applications`), filtree sur ce groupe.
|
||||
# =========================================================================
|
||||
serveur_web_frontal_racine_base: "/srv/web"
|
||||
serveur_web_frontal_paquets:
|
||||
- nginx
|
||||
- libnginx-mod-http-modsecurity # le connecteur nginx de ModSecurity v3
|
||||
- modsecurity-crs # OWASP Core Rule Set
|
||||
|
||||
# En-tetes de securite par defaut (sobriete + vie privee : tout local, pas de tiers).
|
||||
serveur_web_frontal_csp_defaut: >-
|
||||
default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self';
|
||||
object-src 'none'; base-uri 'self'; frame-ancestors 'self'
|
||||
serveur_web_frontal_groupe: "serveur_web_frontal"
|
||||
serveur_web_frontal_publier_expositions: true
|
||||
|
||||
# Liste des sites statiques servis par ce noeud. Chaque entree :
|
||||
# nom identifiant court (dossier + vhost)
|
||||
# server_name FQDN servi (doit etre expose via applications.yml -> edge)
|
||||
# depot URL du depot git du site (souverain, ex. Forgejo)
|
||||
# branche branche a servir (defaut: main)
|
||||
# sous_dossier sous-dossier du depot a servir comme racine (ex: public)
|
||||
# csp Content-Security-Policy specifique (optionnel)
|
||||
serveur_web_frontal_sites: []
|
||||
# --- LE WAF --------------------------------------------------------------------------
|
||||
#
|
||||
# `DetectionOnly` D'ABORD : le CRS journalise ce qu'il aurait bloque
|
||||
# (`/var/log/nginx/modsec_audit.log`) sans rien refuser. On lit, on ajuste les exclusions
|
||||
# pour les applications reelles, PUIS on passe a `On`. Un WAF bloquant pose a l'aveugle
|
||||
# casse les formulaires legitimes, et on finit par le desactiver.
|
||||
#
|
||||
# Eprouve le 2026-09-29 sur le vrai systeme (Debian 13, nginx 1.26, CRS 3.3.7) : une
|
||||
# injection SQL en requete rend 200 et est journalisee (942100, 949110) en detection ;
|
||||
# 403 en blocage ; une requete saine passe dans les deux cas.
|
||||
serveur_web_frontal_waf_mode: "DetectionOnly" # ou "On"
|
||||
serveur_web_frontal_waf_dir: "/etc/nginx/setops-waf"
|
||||
|
||||
# LE PORT DES VHOSTS LOCAUX. Les sites statiques ecoutent en clair sur cette machine ;
|
||||
# c'est l'edge qui termine le TLS devant. La sonde mesure donc ce que l'edge ira chercher,
|
||||
# pas ce que le visiteur voit — et c'est le bon endroit : si l'edge est vert et le site
|
||||
# muet, la panne est ici.
|
||||
serveur_web_frontal_sonde_port: 80
|
||||
# Taille maximale d'une requete relayee (televersements).
|
||||
serveur_web_frontal_taille_max_corps: "16m"
|
||||
|
||||
# Un amont declare sur un de ces ports se parle en TLS (comme a l'edge).
|
||||
serveur_web_frontal_ports_tls: [443, 8443]
|
||||
|
|
|
|||
|
|
@ -1,31 +1,26 @@
|
|||
---
|
||||
# Flux reseau du frontal web (propriete du role). Voir docs/flux-conception.md.
|
||||
#
|
||||
# LE WEB FRONTAL EST LA PORTE PUBLIQUE DU LOCATAIRE (2026-09-29). L'edge expose les services
|
||||
# INTERNES a l'administration ; ce qui est publie sur l'Internet l'est ici, par l'adresse
|
||||
# publique que le site attribue au locataire (redirection a la frontiere). Il filtre (WAF)
|
||||
# et relaie : il ne sert rien lui-meme, et ne recoit plus rien de l'edge.
|
||||
flux:
|
||||
- sens: ingress
|
||||
port: 80
|
||||
protocole: tcp
|
||||
pair: edge
|
||||
chiffrement: clair
|
||||
raison: "Contenu statique servi au navigateur via l'edge (TLS termine a l'edge)."
|
||||
# LE WEB FRONTAL EST LA PORTE PUBLIQUE DU LOCATAIRE (2026-09-29). L'edge expose les services
|
||||
# INTERNES a l'administration ; ce qui est publie sur l'Internet l'est ici, par l'adresse
|
||||
# publique que le site attribue au locataire (redirection a la frontiere). Le 443 attend son
|
||||
# TLS (Let's Encrypt) : la redirection est prete avant lui.
|
||||
- sens: ingress
|
||||
port: 80
|
||||
protocole: tcp
|
||||
pair: externe
|
||||
chiffrement: clair
|
||||
raison: "HTTP public — le premier contact d'un navigateur, et le defi HTTP d'ACME ; renvoie vers HTTPS."
|
||||
raison: "HTTP public, relaye a travers le WAF — et le defi HTTP d'ACME ; renverra vers HTTPS quand le certificat sera la."
|
||||
- sens: ingress
|
||||
port: 443
|
||||
protocole: tcp
|
||||
pair: externe
|
||||
chiffrement: tls-requis
|
||||
raison: "HTTPS public — les sites et services que le locataire publie sur l'Internet."
|
||||
raison: "HTTPS public — les sites et services que le locataire publie sur l'Internet (attend son certificat Let's Encrypt)."
|
||||
- sens: egress
|
||||
port: 443
|
||||
port: 80
|
||||
protocole: tcp
|
||||
pair: flotte
|
||||
chiffrement: tls
|
||||
raison: "git clone/pull du depot du site (Forgejo souverain) au deploiement."
|
||||
pair: serveur_web_dorsal
|
||||
chiffrement: clair
|
||||
raison: "Relais vers le web dorsal, qui porte les sites et les applications publiques."
|
||||
|
|
|
|||
|
|
@ -1,33 +1,14 @@
|
|||
---
|
||||
# Supervision derivee du role. Voir docs/supervision-conception.md.
|
||||
#
|
||||
# CE QUE `serveur_nginx` NE PEUT PAS DIRE. L'edge a sa sonde, et elle repond « nginx sert
|
||||
# encore ». Ici la question est autre : CHACUN des sites declares se sert-il vraiment ?
|
||||
#
|
||||
# LA PANNE PROPRE A CE ROLE EST SILENCIEUSE, et elle vient du fait que le contenu n'est
|
||||
# pas dans le role : il arrive par `git clone`/`pull` depuis une forge. Trois facons de
|
||||
# perdre un site sans que nginx bronche :
|
||||
#
|
||||
# 1. LE DEPOT A CHANGE DE BRANCHE OU DE NOM — le pull echoue, le contenu reste celui de
|
||||
# la derniere fois qui a marche. Le site est en ligne, et vieux.
|
||||
# 2. LE SOUS-DOSSIER SERVI A BOUGE (`public/` renomme en amont) — nginx rend 404 ou 403
|
||||
# sur un repertoire qui existe mais ne contient plus d'index.
|
||||
# 3. LE CLONE EST VIDE — un depot neuf, ou une reconstruction qui n'a rien ramene. Meme
|
||||
# lecture que la forge du site le 2026-09-14 : tout repond, il n'y a rien dedans.
|
||||
#
|
||||
# Dans les trois cas l'edge est vert, le vhost existe, le port ecoute. Seul un GET sur le
|
||||
# `server_name` du site dit la verite.
|
||||
#
|
||||
# UNE SEULE SONDE POUR TOUS LES SITES, parce qu'il n'y a qu'une CAUSE D'ACTION : aller
|
||||
# voir cet hebergeur. Elle nomme celui qui manque — c'est ce dont on a besoin pour agir,
|
||||
# et une sonde par site ferait N services a definir pour un seul geste.
|
||||
#
|
||||
# TTL de 5400 s pour un porteur qui passe aux 15 min : trois passages manques avant la
|
||||
# peremption. Le silence alerte autant que l'echec.
|
||||
# LE FRONTAL EST LA PORTE PUBLIQUE : s'il tombe, tout ce que le locataire publie tombe avec
|
||||
# lui, pendant que les services derriere restent verts. Et un frontal peut tres bien
|
||||
# relayer SANS FILTRER — module WAF non charge, regles absentes — en restant « en ligne ».
|
||||
# `sites-servis` a suivi les sites sur le dorsal le 2026-09-29.
|
||||
sondes:
|
||||
- nom: sites-servis
|
||||
- nom: frontal
|
||||
ttl: 5400
|
||||
raison: 'Chacun des sites statiques declares se sert-il encore ? Le contenu vient
|
||||
d''un depot git : une branche renommee, un sous-dossier deplace ou un clone vide
|
||||
laissent nginx parfaitement vert avec un site perime, vide, ou en 404. Seul un GET
|
||||
sur le `server_name` de chaque site le dit.'
|
||||
raison: 'Le reverse proxy public tient-il, son WAF (ModSecurity + OWASP CRS) est-il
|
||||
vraiment dans le chemin, et chaque exposition publique repond-elle a travers lui ?
|
||||
Un frontal qui relaie sans filtrer reste vert ; un frontal tombe rend tout le public
|
||||
injoignable pendant que les services derriere restent sains.'
|
||||
|
|
|
|||
|
|
@ -1,66 +1,103 @@
|
|||
---
|
||||
- name: Installer nginx + git (git requis pour cloner les depots des sites)
|
||||
# serveur_web_frontal — reverse proxy + WAF des services web PUBLICS. Voir defaults/main.yml.
|
||||
- name: Installer nginx, ModSecurity et l'OWASP CRS
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- nginx
|
||||
- git
|
||||
name: "{{ serveur_web_frontal_paquets }}"
|
||||
state: present
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
|
||||
- name: Retirer le vhost nginx par defaut
|
||||
- name: Retirer le vhost nginx par defaut de Debian
|
||||
ansible.builtin.file:
|
||||
path: /etc/nginx/sites-enabled/default
|
||||
state: absent
|
||||
notify: Recharger nginx serveur_web_frontal
|
||||
|
||||
- name: Creer la racine des sites
|
||||
- name: Deployer le durcissement de base nginx
|
||||
ansible.builtin.template:
|
||||
src: durcissement.conf.j2
|
||||
dest: /etc/nginx/conf.d/99-setops-frontal.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Recharger nginx serveur_web_frontal
|
||||
|
||||
- name: Creer le repertoire du WAF
|
||||
ansible.builtin.file:
|
||||
path: "{{ serveur_web_frontal_racine_base }}"
|
||||
path: "{{ serveur_web_frontal_waf_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Cloner / mettre a jour le depot de chaque site (clone superficiel, contenu courant)
|
||||
ansible.builtin.git:
|
||||
repo: "{{ item.depot }}"
|
||||
dest: "{{ serveur_web_frontal_racine_base }}/{{ item.nom }}"
|
||||
version: "{{ item.branche | default('main') }}"
|
||||
depth: 1
|
||||
force: true
|
||||
loop: "{{ serveur_web_frontal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
|
||||
- name: Deployer le vhost de chaque site
|
||||
- name: Deployer la composition du WAF (ModSecurity + OWASP CRS)
|
||||
ansible.builtin.template:
|
||||
src: site.conf.j2
|
||||
dest: "/etc/nginx/sites-available/{{ item.nom }}"
|
||||
src: waf.conf.j2
|
||||
dest: "{{ serveur_web_frontal_waf_dir }}/main.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ serveur_web_frontal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Recharger nginx serveur_web_frontal
|
||||
|
||||
- name: Activer le vhost de chaque site
|
||||
# LES REGISTRES DU PLAN, COMME A L'EDGE : on releve d'abord ce qui existe, puis on charge.
|
||||
# `domaines.yml` peut manquer (un plan sans domaine public) : les expositions sont alors vides.
|
||||
- name: Quels registres du plan existent vraiment ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
loop:
|
||||
- "{{ setops_plan_dir }}/applications.yml"
|
||||
- "{{ setops_plan_dir }}/domaines.yml"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
register: serveur_web_frontal_registres
|
||||
when: serveur_web_frontal_publier_expositions | bool
|
||||
|
||||
- name: Charger les registres applications et domaines
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ item.item }}"
|
||||
loop: "{{ serveur_web_frontal_registres.results | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.item | basename }}"
|
||||
when:
|
||||
- serveur_web_frontal_publier_expositions | bool
|
||||
- item.stat.exists | default(false)
|
||||
|
||||
- name: Lister les expositions publiques destinees a ce frontal
|
||||
ansible.builtin.set_fact:
|
||||
serveur_web_frontal_expositions: >-
|
||||
{{ {'applications': applications | default({})}
|
||||
| expositions_des_applications({'domaines_publics': domaines_publics | default({})}, serveur_web_frontal_groupe) }}
|
||||
when: serveur_web_frontal_publier_expositions | bool
|
||||
|
||||
- name: Deployer les vhosts du frontal (derives des expositions publiques)
|
||||
ansible.builtin.template:
|
||||
src: frontal.conf.j2
|
||||
dest: /etc/nginx/sites-available/frontal.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Recharger nginx serveur_web_frontal
|
||||
|
||||
- name: Activer les vhosts du frontal
|
||||
ansible.builtin.file:
|
||||
src: "/etc/nginx/sites-available/{{ item.nom }}"
|
||||
dest: "/etc/nginx/sites-enabled/{{ item.nom }}"
|
||||
src: /etc/nginx/sites-available/frontal.conf
|
||||
dest: /etc/nginx/sites-enabled/frontal.conf
|
||||
state: link
|
||||
loop: "{{ serveur_web_frontal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Recharger nginx serveur_web_frontal
|
||||
|
||||
# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et
|
||||
# pousse le resultat ; les greffons standard, eux, sont poses par le porteur.
|
||||
#
|
||||
# LE CONTENU DE CE ROLE N'EST PAS DANS CE ROLE : il arrive par git depuis une forge. Une
|
||||
# branche renommee, un sous-dossier deplace ou un clone vide laissent nginx parfaitement
|
||||
# vert. Seul un GET par `server_name` le dit.
|
||||
- name: Valider la configuration nginx avant tout rechargement
|
||||
ansible.builtin.command: nginx -t
|
||||
changed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Activer et demarrer nginx
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
enabled: true
|
||||
state: started
|
||||
when: not ansible_check_mode
|
||||
|
||||
# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE.
|
||||
- name: Assurer le repertoire des sondes de supervision
|
||||
ansible.builtin.file:
|
||||
path: /usr/local/lib/setops/sondes
|
||||
|
|
@ -69,10 +106,10 @@
|
|||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Deposer la sonde « sites-servis »
|
||||
- name: Deposer la sonde « frontal »
|
||||
ansible.builtin.template:
|
||||
src: sonde-sites-servis.sh.j2
|
||||
dest: /usr/local/lib/setops/sondes/sites-servis.sh
|
||||
src: sonde-frontal.sh.j2
|
||||
dest: /usr/local/lib/setops/sondes/frontal.sh
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
|
|
|||
3
roles/serveur_web_frontal/templates/durcissement.conf.j2
Normal file
3
roles/serveur_web_frontal/templates/durcissement.conf.j2
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Gere par Set-OPS (role serveur_web_frontal). Ne pas editer a la main.
|
||||
server_tokens off;
|
||||
client_max_body_size {{ serveur_web_frontal_taille_max_corps }};
|
||||
41
roles/serveur_web_frontal/templates/frontal.conf.j2
Normal file
41
roles/serveur_web_frontal/templates/frontal.conf.j2
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Gere par Set-OPS (role serveur_web_frontal). Ne pas editer a la main.
|
||||
#
|
||||
# LE SERVEUR PAR DEFAUT REFUSE TOUT NOM INCONNU (meme regle que l'edge) : sans lui, nginx
|
||||
# confie un nom inconnu au premier vhost, et un balayage de l'adresse publique recevrait un
|
||||
# service qu'il n'a pas nomme.
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
return 444;
|
||||
}
|
||||
{% for expo in serveur_web_frontal_expositions | default([]) %}
|
||||
{% if expo.hote in hostvars and hostvars[expo.hote].ansible_host is defined and expo.port %}
|
||||
{% set tls = (expo.port | int) in serveur_web_frontal_ports_tls %}
|
||||
|
||||
# {{ expo.fqdn }} -> {{ expo.hote }}:{{ expo.port }} ({{ expo.application }})
|
||||
# En clair en attendant son certificat Let's Encrypt.
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name {{ expo.fqdn }};
|
||||
|
||||
modsecurity on;
|
||||
modsecurity_rules_file {{ serveur_web_frontal_waf_dir }}/main.conf;
|
||||
|
||||
location / {
|
||||
proxy_pass {{ 'https' if tls else 'http' }}://{{ hostvars[expo.hote].ansible_host }}:{{ expo.port }};
|
||||
{% if tls %}
|
||||
proxy_ssl_verify on;
|
||||
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
|
||||
proxy_ssl_name {{ expo.hote }}.{{ domaine_interne }};
|
||||
proxy_ssl_server_name on;
|
||||
{% endif %}
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
40
roles/serveur_web_frontal/templates/sonde-frontal.sh.j2
Normal file
40
roles/serveur_web_frontal/templates/sonde-frontal.sh.j2
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#!/bin/bash
|
||||
# GENERE par Set-OPS (role serveur_web_frontal). Ne pas editer a la main.
|
||||
#
|
||||
# SONDE « frontal » — le reverse proxy public tient, son WAF est charge, et chaque
|
||||
# exposition publique repond a travers lui.
|
||||
#
|
||||
# Contrat : docs/supervision-conception.md (API des greffons Nagios) — une ligne, 0/1/2.
|
||||
# Mise en defaut PAR PARAMETRE : `serveur_web_frontal_waf_mode` (une valeur que le moteur
|
||||
# refuse fait echouer `nginx -t`).
|
||||
set -uo pipefail
|
||||
|
||||
MODE='{{ serveur_web_frontal_waf_mode }}'
|
||||
EXPOSITIONS="{{ serveur_web_frontal_expositions | default([]) | map(attribute='fqdn') | unique | sort | join(' ') }}"
|
||||
|
||||
systemctl is-active --quiet nginx || { echo "Le frontal (nginx) n'est pas actif : les services publics sont injoignables."; exit 2; }
|
||||
nginx -t >/dev/null 2>&1 \
|
||||
|| { echo "Le frontal sert, mais sa configuration sur disque est INVALIDE : le prochain demarrage echouera."; exit 2; }
|
||||
|
||||
# LE WAF EST-IL VRAIMENT DANS LE CHEMIN ? Un module non charge, ou des regles absentes,
|
||||
# laissent passer tout le trafic sans rien dire : le frontal reste « vert ».
|
||||
conf=$(nginx -T 2>/dev/null)
|
||||
grep -q "ngx_http_modsecurity_module" <<<"${conf}" \
|
||||
|| { echo "Le module ModSecurity n'est pas charge : le frontal relaie SANS filtrer."; exit 2; }
|
||||
grep -q "modsecurity-crs/rules" '{{ serveur_web_frontal_waf_dir }}/main.conf' 2>/dev/null \
|
||||
|| { echo "Les regles OWASP CRS ne sont pas chargees : le WAF n'a rien a appliquer."; exit 2; }
|
||||
|
||||
servies=0; en_defaut=()
|
||||
for n in ${EXPOSITIONS}; do
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 8 -H "Host: ${n}" http://127.0.0.1/ 2>/dev/null)
|
||||
if [[ "${code}" =~ ^[234][0-9][0-9]$ ]]; then servies=$((servies + 1)); else en_defaut+=("${n}=${code:-000}"); fi
|
||||
done
|
||||
if [[ -n "${en_defaut[*]:-}" ]]; then
|
||||
echo "Le frontal repond, mais des expositions publiques ne sont PAS servies : ${en_defaut[*]} (000 = silence, 5xx = amont en defaut). WAF ${MODE}."
|
||||
exit 2
|
||||
fi
|
||||
if [[ -z "${EXPOSITIONS}" ]]; then
|
||||
echo "Frontal pret (WAF CRS en ${MODE}) : aucune exposition publique declaree pour l'instant."
|
||||
exit 0
|
||||
fi
|
||||
echo "Frontal : ${servies} exposition(s) publique(s) servie(s), WAF CRS en ${MODE}."
|
||||
12
roles/serveur_web_frontal/templates/waf.conf.j2
Normal file
12
roles/serveur_web_frontal/templates/waf.conf.j2
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Gere par Set-OPS (role serveur_web_frontal). Ne pas editer a la main.
|
||||
#
|
||||
# ModSecurity v3 + OWASP CRS. L'inclusion livree par Debian (`modsecurity_includes.conf`)
|
||||
# ne charge QUE le moteur, pas les regles : on compose ici, dans l'ordre que le CRS exige
|
||||
# — reglages du moteur, puis son mode, puis la configuration du CRS, ses exclusions
|
||||
# d'avant, ses regles, ses exclusions d'apres.
|
||||
Include /etc/nginx/modsecurity.conf
|
||||
SecRuleEngine {{ serveur_web_frontal_waf_mode }}
|
||||
Include /etc/modsecurity/crs/crs-setup.conf
|
||||
Include /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
|
||||
Include /usr/share/modsecurity-crs/rules/*.conf
|
||||
Include /etc/modsecurity/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
|
||||
|
|
@ -9,8 +9,11 @@
|
|||
graph LR
|
||||
R["<b>postfix</b>"]
|
||||
E0["externe"] -->|"25 · starttls"| R
|
||||
E1["flotte"] -->|"587 · starttls"| R
|
||||
E2["smtp"] -->|"25 · starttls"| R
|
||||
E1["externe"] -->|"465 · tls-requis"| R
|
||||
E2["externe"] -->|"587 · starttls"| R
|
||||
E3["flotte"] -->|"465 · tls-requis"| R
|
||||
E4["flotte"] -->|"587 · starttls"| R
|
||||
E5["smtp"] -->|"25 · starttls"| R
|
||||
R -.->|"sonde « file-courriel »"| ICINGA[["Icinga"]]
|
||||
```
|
||||
|
||||
|
|
@ -19,7 +22,8 @@ graph LR
|
|||
| port | depuis | chiffrement | pourquoi |
|
||||
|---|---|---|---|
|
||||
| `25` | `externe`, `client_smtp` | starttls | SMTP entrant : courrier externe (MX) et notifications internes (client_smtp). |
|
||||
| `587` | `flotte` | starttls | Soumission authentifiée (submission) pour les agents internes qui envoient du courrier. |
|
||||
| `587` | `flotte`, `externe` | starttls | Soumission authentifiée (STARTTLS obligatoire) : agents internes et clients de courriel des utilisateurs. |
|
||||
| `465` | `flotte`, `externe` | tls-requis | Soumission authentifiée en TLS direct (submissions, RFC 8314) : clients de courriel des utilisateurs. |
|
||||
|
||||
## Ce qu'il rend à la supervision
|
||||
|
||||
|
|
|
|||
|
|
@ -9,20 +9,23 @@
|
|||
graph LR
|
||||
R["<b>web_dorsal</b>"]
|
||||
E0["edge"] -->|"80 · clair"| R
|
||||
E1["web_frontal"] -->|"80 · clair"| R
|
||||
R -.->|"sonde « apps-servies »"| ICINGA[["Icinga"]]
|
||||
R -.->|"sonde « sites-servis »"| ICINGA[["Icinga"]]
|
||||
```
|
||||
|
||||
## Qui lui parle
|
||||
|
||||
| port | depuis | chiffrement | pourquoi |
|
||||
|---|---|---|---|
|
||||
| `80` | `edge` | clair | Front nginx local des webapps, proxie par l'edge (TLS termine a l'edge). |
|
||||
| `80` | `edge`, `serveur_web_frontal` | clair | Front nginx local des webapps et des sites statiques, relaye par l'edge (noms internes) et par le web frontal (noms publics). |
|
||||
|
||||
## Ce qu'il rend à la supervision
|
||||
|
||||
| sonde | TTL | ce qu'elle voit |
|
||||
|---|---|---|
|
||||
| `apps-servies` | 5400 s | Chacune des webapps declarees repond-elle encore ? Une app ne tombe presque jamais en « failed » — elle se coince : le processus vit, systemd la dit active, le port est ouvert, et plus une requete n'aboutit. Le rapport d'unites en echec ne verra jamais ca. |
|
||||
| `sites-servis` | 5400 s | Chacun des sites statiques declares se sert-il encore ? Le contenu vient d'un depot git : une branche renommee, un sous-dossier deplace ou un clone vide laissent nginx parfaitement vert avec un site perime, vide, ou en 404. Seul un GET sur le `server_name` de chaque site le dit. |
|
||||
|
||||
## Ce qu'il expose en séries
|
||||
|
||||
|
|
|
|||
|
|
@ -8,21 +8,23 @@
|
|||
```mermaid
|
||||
graph LR
|
||||
R["<b>web_frontal</b>"]
|
||||
E0["edge"] -->|"80 · clair"| R
|
||||
R -.->|"sonde « sites-servis »"| ICINGA[["Icinga"]]
|
||||
E0["externe"] -->|"443 · tls-requis"| R
|
||||
E1["externe"] -->|"80 · clair"| R
|
||||
R -.->|"sonde « frontal »"| ICINGA[["Icinga"]]
|
||||
```
|
||||
|
||||
## Qui lui parle
|
||||
|
||||
| port | depuis | chiffrement | pourquoi |
|
||||
|---|---|---|---|
|
||||
| `80` | `edge` | clair | Contenu statique servi au navigateur via l'edge (TLS termine a l'edge). |
|
||||
| `80` | `externe` | clair | HTTP public, relaye a travers le WAF — et le defi HTTP d'ACME ; renverra vers HTTPS quand le certificat sera la. |
|
||||
| `443` | `externe` | tls-requis | HTTPS public — les sites et services que le locataire publie sur l'Internet (attend son certificat Let's Encrypt). |
|
||||
|
||||
## Ce qu'il rend à la supervision
|
||||
|
||||
| sonde | TTL | ce qu'elle voit |
|
||||
|---|---|---|
|
||||
| `sites-servis` | 5400 s | Chacun des sites statiques declares se sert-il encore ? Le contenu vient d'un depot git : une branche renommee, un sous-dossier deplace ou un clone vide laissent nginx parfaitement vert avec un site perime, vide, ou en 404. Seul un GET sur le `server_name` de chaque site le dit. |
|
||||
| `frontal` | 5400 s | Le reverse proxy public tient-il, son WAF (ModSecurity + OWASP CRS) est-il vraiment dans le chemin, et chaque exposition publique repond-elle a travers lui ? Un frontal qui relaie sans filtrer reste vert ; un frontal tombe rend tout le public injoignable pendant que les services derriere restent sains. |
|
||||
|
||||
## Ce qu'il expose en séries
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ Chaque fiche est construite depuis ce que le rôle **déclare lui-même** — `m
|
|||
| [`serveur_ops`](Rôle-serveur_ops) | 1 | 2 | — |
|
||||
| [`serveur_ops_site`](Rôle-serveur_ops_site) | — | 3 | — |
|
||||
| [`serveur_ops_tenant`](Rôle-serveur_ops_tenant) | 1 | 1 | — |
|
||||
| [`serveur_postfix`](Rôle-serveur_postfix) | 2 | 1 | — |
|
||||
| [`serveur_postfix`](Rôle-serveur_postfix) | 3 | 1 | — |
|
||||
| [`serveur_postgresql`](Rôle-serveur_postgresql) | 2 | 1 | oui |
|
||||
| [`serveur_powerdns`](Rôle-serveur_powerdns) | 4 | 1 | — |
|
||||
| [`serveur_prometheus`](Rôle-serveur_prometheus) | 1 | 1 | — |
|
||||
|
|
@ -70,8 +70,8 @@ Chaque fiche est construite depuis ce que le rôle **déclare lui-même** — `m
|
|||
| [`serveur_resolveur_site`](Rôle-serveur_resolveur_site) | 2 | 1 | — |
|
||||
| [`serveur_rspamd`](Rôle-serveur_rspamd) | 2 | 1 | — |
|
||||
| [`serveur_step_ca`](Rôle-serveur_step_ca) | 1 | 1 | — |
|
||||
| [`serveur_web_dorsal`](Rôle-serveur_web_dorsal) | 1 | 1 | — |
|
||||
| [`serveur_web_frontal`](Rôle-serveur_web_frontal) | 1 | 1 | — |
|
||||
| [`serveur_web_dorsal`](Rôle-serveur_web_dorsal) | 1 | 2 | — |
|
||||
| [`serveur_web_frontal`](Rôle-serveur_web_frontal) | 2 | 1 | — |
|
||||
| [`ssh_baseline`](Rôle-ssh_baseline) | — | — | — |
|
||||
| [`ssh_hardening`](Rôle-ssh_hardening) | — | — | — |
|
||||
| [`sudo_ansible`](Rôle-sudo_ansible) | — | — | — |
|
||||
|
|
|
|||
Loading…
Reference in a new issue