ajoute le relais TURN sur 443 pour la visio (ADR 0006), avec coturn et le routage Nginx par nom

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Adam Belkacemi 2026-10-13 11:45:53 +02:00
parent ee079ddffb
commit 45f8a69e26
19 changed files with 457 additions and 14 deletions

View file

@ -19,3 +19,8 @@ portail_port_uptime_kuma: 3001
# Étape 15 : visio (Jitsi, rôle visio), activée quand ce nom résout vers le serveur.
portail_domaine_visio: meet.syllabuslab.fr
portail_port_visio: 8090
# Bloc A (ADR 0006) : relais TURN sur 443 pour la visio (réseaux scolaires qui
# bloquent l'UDP). Dans le certificat seulement — jamais un vhost HTTPS : le
# routeur par nom (stream Nginx) l'envoie directement à coturn.
portail_domaine_turn: turn.syllabuslab.fr
portail_port_turn_tls: 5349

View file

@ -32,14 +32,22 @@
changed_when: false
failed_when: false
- name: noter si la page de statut et la visio peuvent être activées
- name: vérifier le DNS du sous-domaine du relais TURN
ansible.builtin.command: getent ahostsv4 {{ portail_domaine_turn }}
register: portail_dns_turn
changed_when: false
failed_when: false
- name: noter si la page de statut, la visio et le relais TURN peuvent être activés
ansible.builtin.set_fact:
portail_statut_actif: "{{ portail_ip_publique in portail_dns_statut.stdout }}"
portail_visio_actif: "{{ portail_ip_publique in portail_dns_visio.stdout }}"
portail_turn_actif: "{{ portail_ip_publique in portail_dns_turn.stdout }}"
portail_noms_certificat: >-
{{ [portail_domaine] + portail_domaines_alias
+ ([portail_domaine_statut] if portail_ip_publique in portail_dns_statut.stdout else [])
+ ([portail_domaine_visio] if portail_ip_publique in portail_dns_visio.stdout else []) }}
+ ([portail_domaine_visio] if portail_ip_publique in portail_dns_visio.stdout else [])
+ ([portail_domaine_turn] if portail_ip_publique in portail_dns_turn.stdout else []) }}
- name: vérifier si le certificat existe déjà
ansible.builtin.stat:

View file

@ -0,0 +1,37 @@
---
# Bloc A (ADR 0006) : partage du port 443 entre le portail et le relais TURN
# (coturn, rôle turn), par nom de domaine (SNI), sans jamais déchiffrer le
# TLS existant. N'ouvre aucun nouveau port dans nftables : toujours 443.
- name: installer le module stream de Nginx
ansible.builtin.apt:
name: libnginx-mod-stream
state: present
when: portail_turn_actif | default(false)
notify: recharger nginx
- name: router le port 443 public par nom (stream Nginx)
ansible.builtin.blockinfile:
path: /etc/nginx/nginx.conf
marker: "# {mark} ANSIBLE MANAGED BLOCK — bloc A, ADR 0006, relais TURN"
insertafter: EOF
block: |
# Le vrai TLS (nos vhosts HTTPS, désormais sur 127.0.0.1:8443) et
# coturn (127.0.0.1:{{ portail_port_turn_tls }}) se partagent le port public
# 443 : ce bloc regarde seulement le nom demandé dans la poignée de
# main TLS (SNI), jamais son contenu déchiffré, puis transmet les
# octets tels quels (aucun des deux ne voit l'autre).
stream {
map $ssl_preread_server_name $syllabus_stream_backend {
{{ portail_domaine_turn }} 127.0.0.1:{{ portail_port_turn_tls }};
default 127.0.0.1:8443;
}
server {
listen 443;
listen [::]:443;
ssl_preread on;
proxy_pass $syllabus_stream_backend;
}
}
when: portail_turn_actif | default(false)
notify: recharger nginx

View file

@ -14,5 +14,8 @@
- name: Nginx
ansible.builtin.import_tasks: 05-nginx.yml
- name: relais TURN sur 443 (bloc A, ADR 0006)
ansible.builtin.import_tasks: 07-turn-stream.yml
- name: file et planificateur (jobs Heat, expiration des sessions)
ansible.builtin.import_tasks: 06-queue-scheduler.yml

View file

@ -1,4 +1,16 @@
# Géré par Ansible (rôle portail) — ne pas modifier à la main.
{# Bloc A (ADR 0006) : quand le relais TURN est actif, le port public 443
est pris par le routeur Nginx par nom (stream, nginx.conf) — ces vhost
HTTPS n'écoutent alors plus qu'en interne, sur 127.0.0.1:8443, où ce
routeur leur transmet tout ce qui n'est pas pour coturn. #}
{%- macro listen443(default_server=False) -%}
{%- if portail_turn_actif | default(false) %}
listen 127.0.0.1:8443 ssl http2{{ ' default_server' if default_server }};
{%- else %}
listen 443 ssl http2{{ ' default_server' if default_server }};
listen [::]:443 ssl http2{{ ' default_server' if default_server }};
{%- endif -%}
{% endmacro -%}
{% macro bloc_portail() %}
root {{ portail_dossier_app }}/public;
index index.php;
@ -40,7 +52,7 @@
server {
listen 80;
listen [::]:80;
server_name {{ portail_domaine }} {{ portail_domaines_alias | join(' ') }}{% if portail_statut_actif %} {{ portail_domaine_statut }}{% endif %}{% if portail_visio_actif | default(false) %} {{ portail_domaine_visio }}{% endif %};
server_name {{ portail_domaine }} {{ portail_domaines_alias | join(' ') }}{% if portail_statut_actif %} {{ portail_domaine_statut }}{% endif %}{% if portail_visio_actif | default(false) %} {{ portail_domaine_visio }}{% endif %}{% if portail_turn_actif | default(false) %} {{ portail_domaine_turn }}{% endif %};
server_tokens off;
# Dans une location (pas au niveau server) : la location exacte que
# certbot ajoute pour le défi ACME au renouvellement reste prioritaire.
@ -61,8 +73,7 @@ server {
# www → domaine principal (une seule adresse canonique).
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
{{ listen443() }}
server_name {{ portail_domaines_alias | join(' ') }};
server_tokens off;
include snippets/syllabus-tls.conf;
@ -71,8 +82,7 @@ server {
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
{{ listen443(default_server=True) }}
server_name {{ portail_domaine }};
server_tokens off;
include snippets/syllabus-tls.conf;
@ -85,8 +95,7 @@ server {
# (tableau de bord, /socket.io, /metrics…) reste accessible par tunnel SSH
# uniquement. Chemins relevés dans server/routers/status-page-router.js.
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
{{ listen443() }}
server_name {{ portail_domaine_statut }};
server_tokens off;
include snippets/syllabus-tls.conf;
@ -123,8 +132,7 @@ server {
# X-Frame-Options et sa Permissions-Policy interdiraient l'intégration dans
# le portail et la caméra. Intégration permise au seul portail.
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
{{ listen443() }}
server_name {{ portail_domaine_visio }};
server_tokens off;
include snippets/syllabus-tls.conf;