diff --git a/playbooks/groupes/web_frontal.yml b/playbooks/groupes/web_frontal.yml new file mode 100644 index 0000000..00f7c78 --- /dev/null +++ b/playbooks/groupes/web_frontal.yml @@ -0,0 +1,15 @@ +--- +- name: Appliquer le groupe web_frontal + hosts: web_frontal + become: true + gather_facts: true + + pre_tasks: + - name: Vérifier que la cible est Debian + ansible.builtin.assert: + that: + - ansible_facts.distribution == "Debian" + fail_msg: "Ce playbook est prévu pour Debian." + + roles: + - web_frontal diff --git a/roles/web_frontal/README.md b/roles/web_frontal/README.md new file mode 100644 index 0000000..1ce51f1 --- /dev/null +++ b/roles/web_frontal/README.md @@ -0,0 +1,37 @@ +# 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). + +## Ce que le rôle fait +Pour chaque site déclaré dans `web_frontal_sites` : +1. `git clone`/`pull` du dépôt → `/srv/web/` ; +2. vhost nginx servant `` (ex. `public/`), avec en-têtes de sécurité + + CSP (défaut : `self` only, aucun tiers, aucun pistage), gzip, cache, `try_files`. + +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) +```yaml +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 +``` +Et dans `applications.yml` : une app `groupe: web_frontal, hote: , port: 80, +expose: [alliance-boreale.]`. + +## 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. + +## 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). diff --git a/roles/web_frontal/defaults/main.yml b/roles/web_frontal/defaults/main.yml new file mode 100644 index 0000000..af26064 --- /dev/null +++ b/roles/web_frontal/defaults/main.yml @@ -0,0 +1,23 @@ +--- +# ========================================================================= +# 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" + +# En-tetes de securite par defaut (sobriete + vie privee : tout local, pas de tiers). +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' + +# 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) +web_frontal_sites: [] diff --git a/roles/web_frontal/handlers/main.yml b/roles/web_frontal/handlers/main.yml new file mode 100644 index 0000000..b0d666e --- /dev/null +++ b/roles/web_frontal/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: Recharger nginx web_frontal + ansible.builtin.systemd: + name: nginx + state: reloaded + when: not ansible_check_mode diff --git a/roles/web_frontal/meta/empreinte.yml b/roles/web_frontal/meta/empreinte.yml new file mode 100644 index 0000000..31417b9 --- /dev/null +++ b/roles/web_frontal/meta/empreinte.yml @@ -0,0 +1,6 @@ +--- +# Empreinte de dimensionnement (propriete du role). Site statique servi par +# nginx : tres leger (pas de runtime, pas de BD). +coeurs: 1 +memoire_mo: 512 +disque_go: 10 diff --git a/roles/web_frontal/meta/flux.yml b/roles/web_frontal/meta/flux.yml new file mode 100644 index 0000000..f069086 --- /dev/null +++ b/roles/web_frontal/meta/flux.yml @@ -0,0 +1,15 @@ +--- +# Flux reseau du frontal web (propriete du role). Voir docs/flux-conception.md. +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)." + - sens: egress + port: 443 + protocole: tcp + pair: flotte + chiffrement: tls + raison: "git clone/pull du depot du site (Forgejo souverain) au deploiement." diff --git a/roles/web_frontal/tasks/main.yml b/roles/web_frontal/tasks/main.yml new file mode 100644 index 0000000..8c94b63 --- /dev/null +++ b/roles/web_frontal/tasks/main.yml @@ -0,0 +1,53 @@ +--- +- name: Installer nginx + ansible.builtin.apt: + name: nginx + state: present + update_cache: true + cache_valid_time: 3600 + +- name: Retirer le vhost nginx par defaut + ansible.builtin.file: + path: /etc/nginx/sites-enabled/default + state: absent + notify: Recharger nginx web_frontal + +- name: Creer la racine des sites + ansible.builtin.file: + path: "{{ web_frontal_racine_base }}" + state: directory + owner: root + group: root + mode: "0755" + +- name: Cloner / mettre a jour le depot de chaque site + ansible.builtin.git: + repo: "{{ item.depot }}" + dest: "{{ web_frontal_racine_base }}/{{ item.nom }}" + version: "{{ item.branche | default('main') }}" + force: true + loop: "{{ web_frontal_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/{{ item.nom }}" + owner: root + group: root + mode: "0644" + loop: "{{ web_frontal_sites }}" + loop_control: + label: "{{ item.nom }}" + notify: Recharger nginx 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_control: + label: "{{ item.nom }}" + notify: Recharger nginx web_frontal diff --git a/roles/web_frontal/templates/site.conf.j2 b/roles/web_frontal/templates/site.conf.j2 new file mode 100644 index 0000000..1f513e0 --- /dev/null +++ b/roles/web_frontal/templates/site.conf.j2 @@ -0,0 +1,31 @@ +# Site statique {{ item.nom }} — genere par Set-OPS (role 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 '' }}; + 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; + + gzip on; + gzip_types text/css application/javascript text/html image/svg+xml; + gzip_min_length 1024; + + location /assets/ { + expires 7d; + add_header Cache-Control "public"; + } + + location / { + try_files $uri $uri/ $uri.html =404; + } + + error_page 404 /404.html; + + access_log /var/log/nginx/{{ item.nom }}.access.log; + error_log /var/log/nginx/{{ item.nom }}.error.log; +}