Rôles web : renommer web_frontal/dorsal -> serveur_web_frontal/dorsal (convention)
Aligne sur la convention serveur_* de tous les rôles de service (vars incluses). Le modèle presence-web (pré-existant) utilisait déjà ces noms -> il colle enfin. Aussi : serveur_web_dorsal ne chown plus le code (reste root, lecture seule pour l'app = plus sûr + git idempotent, fini le 'propriété douteuse' au pull). Idempotence prouvée (2e passage changed=0). lint production. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c8ccbb5e97
commit
af13fc37c9
18 changed files with 80 additions and 88 deletions
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: Appliquer le groupe web_dorsal
|
||||
hosts: web_dorsal
|
||||
- name: Appliquer le groupe serveur_web_dorsal
|
||||
hosts: serveur_web_dorsal
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
|
||||
roles:
|
||||
- web_dorsal
|
||||
- serveur_web_dorsal
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: Appliquer le groupe web_frontal
|
||||
hosts: web_frontal
|
||||
- name: Appliquer le groupe serveur_web_frontal
|
||||
hosts: serveur_web_frontal
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
|
||||
roles:
|
||||
- web_frontal
|
||||
- serveur_web_frontal
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# web_dorsal
|
||||
# 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
|
||||
|
|
@ -6,7 +6,7 @@ Deuxième brique de la **plateforme webapp** de Set-OPS : **100 % natif** — ve
|
|||
(monregistraire : FastAPI + uvicorn + SQLite).
|
||||
|
||||
## Patron
|
||||
Pour chaque app déclarée dans `web_dorsal_apps` :
|
||||
Pour chaque app déclarée dans `serveur_web_dorsal_apps` :
|
||||
1. `git clone` (depth 1) → `/srv/webapp/<nom>` ;
|
||||
2. venv Python + `pip install -r requirements.txt` ;
|
||||
3. **unité systemd** (l'app écoute en `127.0.0.1:<port>`, user `webapp`, durcie :
|
||||
|
|
@ -16,9 +16,9 @@ Pour chaque app déclarée dans `web_dorsal_apps` :
|
|||
L'app reste **en localhost** (sûr) ; l'edge proxie `server_name` → nginx local → app.
|
||||
Exposé via `applications.yml` (port **80**, pas le port interne de l'app).
|
||||
|
||||
## Déclaration (group_vars du groupe web_dorsal)
|
||||
## Déclaration (group_vars du groupe serveur_web_dorsal)
|
||||
```yaml
|
||||
web_dorsal_apps:
|
||||
serveur_web_dorsal_apps:
|
||||
- nom: registraire
|
||||
server_name: "registraire.{{ domaine_interne }}"
|
||||
depot: "https://forge.{{ domaine_interne }}/…/monregistraire.git"
|
||||
|
|
@ -28,7 +28,7 @@ web_dorsal_apps:
|
|||
env: { IMMOS_DB_PATH: /var/lib/registraire/immos.db }
|
||||
data_dir: /var/lib/registraire # état local (SQLite), optionnel
|
||||
```
|
||||
Et une app `groupe: web_dorsal, hote: <noeud>, port: 80, expose: [registraire.<domaine>]`.
|
||||
Et une app `groupe: serveur_web_dorsal, hote: <noeud>, port: 80, expose: [registraire.<domaine>]`.
|
||||
|
||||
## BD
|
||||
Les apps à état **local** (SQLite) : `data_dir` persistant. Les apps à **BD réseau**
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
---
|
||||
# =========================================================================
|
||||
# web_dorsal — webapps DYNAMIQUES (runtime + service systemd), derriere l'edge.
|
||||
# serveur_web_dorsal — webapps DYNAMIQUES (runtime + service systemd), derriere l'edge.
|
||||
# 2e brique de la plateforme webapp : 100% natif (venv/paquets + systemd + nginx),
|
||||
# ZERO conteneur. L'app ecoute en localhost ; un vhost nginx local la fronte ;
|
||||
# l'edge proxie vers ce nginx. BD reseau (PG) via resoudre_base si l'app en a une ;
|
||||
# les apps a etat local (SQLite) utilisent un data_dir persistant.
|
||||
# Codifie depuis le spike du 2026-07-05 (monregistraire, FastAPI + uvicorn + SQLite).
|
||||
# =========================================================================
|
||||
web_dorsal_racine: "/srv/webapp"
|
||||
web_dorsal_utilisateur: "webapp"
|
||||
serveur_web_dorsal_racine: "/srv/webapp"
|
||||
serveur_web_dorsal_utilisateur: "webapp"
|
||||
|
||||
# Liste des webapps servies par ce noeud. Chaque entree :
|
||||
# nom identifiant court (dossier + service + vhost)
|
||||
|
|
@ -20,4 +20,4 @@ web_dorsal_utilisateur: "webapp"
|
|||
# requirements fichier pip (defaut: requirements.txt) ; venv cree automatiquement
|
||||
# env dict de variables d'environnement (secrets via voute)
|
||||
# data_dir repertoire d'etat persistant (optionnel ; cree, possede par webapp)
|
||||
web_dorsal_apps: []
|
||||
serveur_web_dorsal_apps: []
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
- name: Recharger nginx web_dorsal
|
||||
- name: Recharger nginx serveur_web_dorsal
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
name: "{{ item.nom }}"
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop: "{{ serveur_web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
when: not ansible_check_mode
|
||||
|
|
@ -12,15 +12,15 @@
|
|||
|
||||
- name: Utilisateur de service des webapps
|
||||
ansible.builtin.user:
|
||||
name: "{{ web_dorsal_utilisateur }}"
|
||||
name: "{{ serveur_web_dorsal_utilisateur }}"
|
||||
system: true
|
||||
home: "{{ web_dorsal_racine }}"
|
||||
home: "{{ serveur_web_dorsal_racine }}"
|
||||
shell: /usr/sbin/nologin
|
||||
create_home: false
|
||||
|
||||
- name: Creer la racine des webapps
|
||||
ansible.builtin.file:
|
||||
path: "{{ web_dorsal_racine }}"
|
||||
path: "{{ serveur_web_dorsal_racine }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
|
|
@ -29,21 +29,21 @@
|
|||
- name: Cloner / mettre a jour chaque app (clone superficiel)
|
||||
ansible.builtin.git:
|
||||
repo: "{{ item.depot }}"
|
||||
dest: "{{ web_dorsal_racine }}/{{ item.nom }}"
|
||||
dest: "{{ serveur_web_dorsal_racine }}/{{ item.nom }}"
|
||||
version: "{{ item.branche | default('main') }}"
|
||||
depth: 1
|
||||
force: true
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop: "{{ serveur_web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Redemarrer les webapps
|
||||
|
||||
- name: Creer le venv + installer les dependances de chaque app
|
||||
ansible.builtin.pip:
|
||||
requirements: "{{ web_dorsal_racine }}/{{ item.nom }}/{{ item.requirements | default('requirements.txt') }}"
|
||||
virtualenv: "{{ web_dorsal_racine }}/{{ item.nom }}/.venv"
|
||||
requirements: "{{ serveur_web_dorsal_racine }}/{{ item.nom }}/{{ item.requirements | default('requirements.txt') }}"
|
||||
virtualenv: "{{ serveur_web_dorsal_racine }}/{{ item.nom }}/.venv"
|
||||
virtualenv_command: python3 -m venv
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop: "{{ serveur_web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Redemarrer les webapps
|
||||
|
|
@ -52,24 +52,16 @@
|
|||
ansible.builtin.file:
|
||||
path: "{{ item.data_dir }}"
|
||||
state: directory
|
||||
owner: "{{ web_dorsal_utilisateur }}"
|
||||
group: "{{ web_dorsal_utilisateur }}"
|
||||
owner: "{{ serveur_web_dorsal_utilisateur }}"
|
||||
group: "{{ serveur_web_dorsal_utilisateur }}"
|
||||
mode: "0750"
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop: "{{ serveur_web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
when: item.data_dir is defined
|
||||
|
||||
- name: Donner le code au user de service
|
||||
ansible.builtin.file:
|
||||
path: "{{ web_dorsal_racine }}/{{ item.nom }}"
|
||||
owner: "{{ web_dorsal_utilisateur }}"
|
||||
group: "{{ web_dorsal_utilisateur }}"
|
||||
recurse: true
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
|
||||
# Le code reste possede par root (lecture seule pour l'app = plus sur, et git
|
||||
# reste idempotent : pas de "propriete douteuse" au pull). L'etat va dans data_dir.
|
||||
- name: Deployer l'unite systemd de chaque app
|
||||
ansible.builtin.template:
|
||||
src: app.service.j2
|
||||
|
|
@ -77,7 +69,7 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop: "{{ serveur_web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Redemarrer les webapps
|
||||
|
|
@ -89,20 +81,20 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop: "{{ serveur_web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Recharger nginx web_dorsal
|
||||
notify: Recharger nginx serveur_web_dorsal
|
||||
|
||||
- name: Activer le vhost proxy de chaque app
|
||||
ansible.builtin.file:
|
||||
src: "/etc/nginx/sites-available/{{ item.nom }}"
|
||||
dest: "/etc/nginx/sites-enabled/{{ item.nom }}"
|
||||
state: link
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop: "{{ serveur_web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Recharger nginx web_dorsal
|
||||
notify: Recharger nginx serveur_web_dorsal
|
||||
|
||||
- name: Activer et demarrer chaque webapp
|
||||
ansible.builtin.systemd:
|
||||
|
|
@ -110,7 +102,7 @@
|
|||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
loop: "{{ web_dorsal_apps }}"
|
||||
loop: "{{ serveur_web_dorsal_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
when: not ansible_check_mode
|
||||
21
roles/serveur_web_dorsal/templates/app.service.j2
Normal file
21
roles/serveur_web_dorsal/templates/app.service.j2
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Webapp {{ item.nom }} — unite generee par Set-OPS (role serveur_web_dorsal). Ne pas editer.
|
||||
[Unit]
|
||||
Description=Webapp {{ item.nom }} ({{ item.server_name }})
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ serveur_web_dorsal_utilisateur }}
|
||||
Group={{ serveur_web_dorsal_utilisateur }}
|
||||
WorkingDirectory={{ serveur_web_dorsal_racine }}/{{ item.nom }}
|
||||
{% for k, v in (item.env | default({}) | dictsort) %}
|
||||
Environment="{{ k }}={{ v }}"
|
||||
{% endfor %}
|
||||
ExecStart={{ serveur_web_dorsal_racine }}/{{ item.nom }}/{{ item.commande }}
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Front nginx local de la webapp {{ item.nom }} — genere par Set-OPS (web_dorsal).
|
||||
# Front nginx local de la webapp {{ item.nom }} — genere par Set-OPS (serveur_web_dorsal).
|
||||
# L'app ecoute en localhost ; l'edge proxie server_name -> ce vhost -> l'app.
|
||||
server {
|
||||
listen 80;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# web_frontal
|
||||
# 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
|
||||
|
|
@ -6,7 +6,7 @@ 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).
|
||||
|
||||
## Ce que le rôle fait
|
||||
Pour chaque site déclaré dans `web_frontal_sites` :
|
||||
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`.
|
||||
|
|
@ -14,17 +14,17 @@ Pour chaque site déclaré dans `web_frontal_sites` :
|
|||
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**.
|
||||
|
||||
## Déclaration (group_vars du groupe web_frontal)
|
||||
## Déclaration (group_vars du groupe serveur_web_frontal)
|
||||
```yaml
|
||||
web_frontal_sites:
|
||||
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 web_frontal_csp_defaut
|
||||
# csp: "..." # optionnel, sinon serveur_web_frontal_csp_defaut
|
||||
```
|
||||
Et dans `applications.yml` : une app `groupe: web_frontal, hote: <noeud>, port: 80,
|
||||
Et dans `applications.yml` : une app `groupe: serveur_web_frontal, hote: <noeud>, port: 80,
|
||||
expose: [alliance-boreale.<domaine>]`.
|
||||
|
||||
## Portée
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
---
|
||||
# =========================================================================
|
||||
# web_frontal — hebergement de sites web STATIQUES (nginx), derriere l'edge.
|
||||
# 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).
|
||||
# =========================================================================
|
||||
web_frontal_racine_base: "/srv/web"
|
||||
serveur_web_frontal_racine_base: "/srv/web"
|
||||
|
||||
# En-tetes de securite par defaut (sobriete + vie privee : tout local, pas de tiers).
|
||||
web_frontal_csp_defaut: >-
|
||||
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'
|
||||
|
||||
|
|
@ -20,4 +20,4 @@ web_frontal_csp_defaut: >-
|
|||
# branche branche a servir (defaut: main)
|
||||
# sous_dossier sous-dossier du depot a servir comme racine (ex: public)
|
||||
# csp Content-Security-Policy specifique (optionnel)
|
||||
web_frontal_sites: []
|
||||
serveur_web_frontal_sites: []
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
- name: Recharger nginx web_frontal
|
||||
- name: Recharger nginx serveur_web_frontal
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
|
|
@ -12,11 +12,11 @@
|
|||
ansible.builtin.file:
|
||||
path: /etc/nginx/sites-enabled/default
|
||||
state: absent
|
||||
notify: Recharger nginx web_frontal
|
||||
notify: Recharger nginx serveur_web_frontal
|
||||
|
||||
- name: Creer la racine des sites
|
||||
ansible.builtin.file:
|
||||
path: "{{ web_frontal_racine_base }}"
|
||||
path: "{{ serveur_web_frontal_racine_base }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
|
|
@ -25,11 +25,11 @@
|
|||
- name: Cloner / mettre a jour le depot de chaque site (clone superficiel, contenu courant)
|
||||
ansible.builtin.git:
|
||||
repo: "{{ item.depot }}"
|
||||
dest: "{{ web_frontal_racine_base }}/{{ item.nom }}"
|
||||
dest: "{{ serveur_web_frontal_racine_base }}/{{ item.nom }}"
|
||||
version: "{{ item.branche | default('main') }}"
|
||||
depth: 1
|
||||
force: true
|
||||
loop: "{{ web_frontal_sites }}"
|
||||
loop: "{{ serveur_web_frontal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
|
||||
|
|
@ -40,17 +40,17 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ web_frontal_sites }}"
|
||||
loop: "{{ serveur_web_frontal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Recharger nginx web_frontal
|
||||
notify: Recharger nginx serveur_web_frontal
|
||||
|
||||
- name: Activer le vhost de chaque site
|
||||
ansible.builtin.file:
|
||||
src: "/etc/nginx/sites-available/{{ item.nom }}"
|
||||
dest: "/etc/nginx/sites-enabled/{{ item.nom }}"
|
||||
state: link
|
||||
loop: "{{ web_frontal_sites }}"
|
||||
loop: "{{ serveur_web_frontal_sites }}"
|
||||
loop_control:
|
||||
label: "{{ item.nom }}"
|
||||
notify: Recharger nginx web_frontal
|
||||
notify: Recharger nginx serveur_web_frontal
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
# Site statique {{ item.nom }} — genere par Set-OPS (role web_frontal). Ne pas editer.
|
||||
# Site statique {{ item.nom }} — genere par Set-OPS (role serveur_web_frontal). 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 {{ web_frontal_racine_base }}/{{ item.nom }}{{ ('/' ~ item.sous_dossier) if (item.sous_dossier | default('')) else '' }};
|
||||
root {{ serveur_web_frontal_racine_base }}/{{ 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(web_frontal_csp_defaut)) | trim }}" always;
|
||||
add_header Content-Security-Policy "{{ (item.csp | default(serveur_web_frontal_csp_defaut)) | trim }}" always;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/css application/javascript text/html image/svg+xml;
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# Webapp {{ item.nom }} — unite generee par Set-OPS (role web_dorsal). Ne pas editer.
|
||||
[Unit]
|
||||
Description=Webapp {{ item.nom }} ({{ item.server_name }})
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ web_dorsal_utilisateur }}
|
||||
Group={{ web_dorsal_utilisateur }}
|
||||
WorkingDirectory={{ web_dorsal_racine }}/{{ item.nom }}
|
||||
{% for k, v in (item.env | default({}) | dictsort) %}
|
||||
Environment="{{ k }}={{ v }}"
|
||||
{% endfor %}
|
||||
ExecStart={{ web_dorsal_racine }}/{{ item.nom }}/{{ item.commande }}
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Loading…
Reference in a new issue