From 454ca95edfbafe4f4597303fc42a9f6ad268392e Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 14:20:32 +0100
Subject: [PATCH 01/49] Rename nginx-reverseproxy to nginx

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 plays/reverse-proxy.yml                                         | 2 +-
 roles/{nginx-reverseproxy => nginx}/handlers/main.yml           | 0
 roles/{nginx-reverseproxy => nginx}/tasks/main.yml              | 0
 .../templates/letsencrypt/dhparam.j2                            | 0
 .../templates/nginx/sites-available/redirect.j2                 | 0
 .../templates/nginx/sites-available/reverseproxy.j2             | 0
 .../nginx/sites-available/reverseproxy_redirect_dname.j2        | 0
 .../templates/nginx/snippets/options-proxypass.conf.j2          | 0
 .../templates/nginx/snippets/options-ssl.conf.j2                | 0
 .../templates/update-motd.d/05-service.j2                       | 0
 .../templates/www/html/50x.html.j2                              | 0
 11 files changed, 1 insertion(+), 1 deletion(-)
 rename roles/{nginx-reverseproxy => nginx}/handlers/main.yml (100%)
 rename roles/{nginx-reverseproxy => nginx}/tasks/main.yml (100%)
 rename roles/{nginx-reverseproxy => nginx}/templates/letsencrypt/dhparam.j2 (100%)
 rename roles/{nginx-reverseproxy => nginx}/templates/nginx/sites-available/redirect.j2 (100%)
 rename roles/{nginx-reverseproxy => nginx}/templates/nginx/sites-available/reverseproxy.j2 (100%)
 rename roles/{nginx-reverseproxy => nginx}/templates/nginx/sites-available/reverseproxy_redirect_dname.j2 (100%)
 rename roles/{nginx-reverseproxy => nginx}/templates/nginx/snippets/options-proxypass.conf.j2 (100%)
 rename roles/{nginx-reverseproxy => nginx}/templates/nginx/snippets/options-ssl.conf.j2 (100%)
 rename roles/{nginx-reverseproxy => nginx}/templates/update-motd.d/05-service.j2 (100%)
 rename roles/{nginx-reverseproxy => nginx}/templates/www/html/50x.html.j2 (100%)

diff --git a/plays/reverse-proxy.yml b/plays/reverse-proxy.yml
index 04c3fb38..c81106c4 100755
--- a/plays/reverse-proxy.yml
+++ b/plays/reverse-proxy.yml
@@ -6,4 +6,4 @@
     mirror: '{{ glob_mirror.name }}'
   roles:
     - certbot
-    - nginx-reverseproxy
+    - nginx
diff --git a/roles/nginx-reverseproxy/handlers/main.yml b/roles/nginx/handlers/main.yml
similarity index 100%
rename from roles/nginx-reverseproxy/handlers/main.yml
rename to roles/nginx/handlers/main.yml
diff --git a/roles/nginx-reverseproxy/tasks/main.yml b/roles/nginx/tasks/main.yml
similarity index 100%
rename from roles/nginx-reverseproxy/tasks/main.yml
rename to roles/nginx/tasks/main.yml
diff --git a/roles/nginx-reverseproxy/templates/letsencrypt/dhparam.j2 b/roles/nginx/templates/letsencrypt/dhparam.j2
similarity index 100%
rename from roles/nginx-reverseproxy/templates/letsencrypt/dhparam.j2
rename to roles/nginx/templates/letsencrypt/dhparam.j2
diff --git a/roles/nginx-reverseproxy/templates/nginx/sites-available/redirect.j2 b/roles/nginx/templates/nginx/sites-available/redirect.j2
similarity index 100%
rename from roles/nginx-reverseproxy/templates/nginx/sites-available/redirect.j2
rename to roles/nginx/templates/nginx/sites-available/redirect.j2
diff --git a/roles/nginx-reverseproxy/templates/nginx/sites-available/reverseproxy.j2 b/roles/nginx/templates/nginx/sites-available/reverseproxy.j2
similarity index 100%
rename from roles/nginx-reverseproxy/templates/nginx/sites-available/reverseproxy.j2
rename to roles/nginx/templates/nginx/sites-available/reverseproxy.j2
diff --git a/roles/nginx-reverseproxy/templates/nginx/sites-available/reverseproxy_redirect_dname.j2 b/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
similarity index 100%
rename from roles/nginx-reverseproxy/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
rename to roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
diff --git a/roles/nginx-reverseproxy/templates/nginx/snippets/options-proxypass.conf.j2 b/roles/nginx/templates/nginx/snippets/options-proxypass.conf.j2
similarity index 100%
rename from roles/nginx-reverseproxy/templates/nginx/snippets/options-proxypass.conf.j2
rename to roles/nginx/templates/nginx/snippets/options-proxypass.conf.j2
diff --git a/roles/nginx-reverseproxy/templates/nginx/snippets/options-ssl.conf.j2 b/roles/nginx/templates/nginx/snippets/options-ssl.conf.j2
similarity index 100%
rename from roles/nginx-reverseproxy/templates/nginx/snippets/options-ssl.conf.j2
rename to roles/nginx/templates/nginx/snippets/options-ssl.conf.j2
diff --git a/roles/nginx-reverseproxy/templates/update-motd.d/05-service.j2 b/roles/nginx/templates/update-motd.d/05-service.j2
similarity index 100%
rename from roles/nginx-reverseproxy/templates/update-motd.d/05-service.j2
rename to roles/nginx/templates/update-motd.d/05-service.j2
diff --git a/roles/nginx-reverseproxy/templates/www/html/50x.html.j2 b/roles/nginx/templates/www/html/50x.html.j2
similarity index 100%
rename from roles/nginx-reverseproxy/templates/www/html/50x.html.j2
rename to roles/nginx/templates/www/html/50x.html.j2
-- 
GitLab


From 51c54e4b86f46c393252787f012969865eaba90f Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 14:26:56 +0100
Subject: [PATCH 02/49] Install reverse proxy sites only if necessary

---
 roles/nginx/tasks/main.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 5a23f992..0ff8bdc6 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -21,6 +21,7 @@
     dest: /etc/letsencrypt/dhparam
 
 - name: Copy reverse proxy sites
+  when: nginx.reverseproxy_sites|length + nginx.redirect_sites|length > 0
   template:
     src: "nginx/sites-available/{{ item }}.j2"
     dest: "/etc/nginx/sites-available/{{ item }}"
@@ -30,7 +31,8 @@
     - redirect
   notify: Reload nginx
 
-- name: Activate sites
+- name: Activate reverse proxy sites
+  when: nginx.reverseproxy_sites|length + nginx.redirect_sites|length > 0
   file:
     src: "/etc/nginx/sites-available/{{ item }}"
     dest: "/etc/nginx/sites-enabled/{{ item }}"
-- 
GitLab


From f09ec69ef1ef47ab708a79167f9a657e58e78990 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 14:28:28 +0100
Subject: [PATCH 03/49] Remove unused role nginx-rtmp

---
 plays/nginx_rtmp.yml                          |   7 -
 roles/nginx-rtmp/files/index.html             | 166 ------------------
 roles/nginx-rtmp/files/no-stream.jpg          | Bin 112140 -> 0 bytes
 roles/nginx-rtmp/handlers/main.yml            |   7 -
 roles/nginx-rtmp/tasks/main.yml               |  59 -------
 .../nginx/modules-available/60-rtmp.conf.j2   |  18 --
 .../templates/nginx/sites-available/stream.j2 |  40 -----
 7 files changed, 297 deletions(-)
 delete mode 100755 plays/nginx_rtmp.yml
 delete mode 100644 roles/nginx-rtmp/files/index.html
 delete mode 100644 roles/nginx-rtmp/files/no-stream.jpg
 delete mode 100644 roles/nginx-rtmp/handlers/main.yml
 delete mode 100644 roles/nginx-rtmp/tasks/main.yml
 delete mode 100644 roles/nginx-rtmp/templates/nginx/modules-available/60-rtmp.conf.j2
 delete mode 100644 roles/nginx-rtmp/templates/nginx/sites-available/stream.j2

diff --git a/plays/nginx_rtmp.yml b/plays/nginx_rtmp.yml
deleted file mode 100755
index b515bb23..00000000
--- a/plays/nginx_rtmp.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env ansible-playbook
----
-- hosts: nginx_rtmp
-  vars:
-    nginx_rtmp: "{{ glob_nginx_rtmp | default({}) | combine(loc_nginx_rtmp | default({})) }}"
-  roles:
-    - nginx-rtmp
diff --git a/roles/nginx-rtmp/files/index.html b/roles/nginx-rtmp/files/index.html
deleted file mode 100644
index 24c9c749..00000000
--- a/roles/nginx-rtmp/files/index.html
+++ /dev/null
@@ -1,166 +0,0 @@
-<!DOCTYPE html>
-<html lang="fr">
-<head>
-  <meta charset="UTF-8">
-  <title>Crans Stream</title>
-  <link href="lib/bootstrap4/css/bootstrap.min.css" rel="stylesheet">
-  <link href="//unpkg.com/video.js@7/dist/video-js.min.css" rel="stylesheet">
-  <style>
-  body, html {
-    height: 100%;
-  }
-  .video-js .vjs-big-play-button {
-    display: none;
-  }
-  </style>
-</head>
-<body class="bg-dark">
-  <div class="container-fluid h-100 p-0">
-      <div class="row align-items-center h-100 mx-0">
-        <div class="col-lg-8">
-          <video id="my-video" class="video-js embed-responsive shadow-lg rounded-sm"></video>
-          <p class="text-right text-white">
-            <a class="text-white text-decoration-none" href="#" id="refreshStream">
-              <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-arrow-repeat" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
-                <path fill-rule="evenodd" d="M2.854 7.146a.5.5 0 0 0-.708 0l-2 2a.5.5 0 1 0 .708.708L2.5 8.207l1.646 1.647a.5.5 0 0 0 .708-.708l-2-2zm13-1a.5.5 0 0 0-.708 0L13.5 7.793l-1.646-1.647a.5.5 0 0 0-.708.708l2 2a.5.5 0 0 0 .708 0l2-2a.5.5 0 0 0 0-.708z"/>
-                <path fill-rule="evenodd" d="M8 3a4.995 4.995 0 0 0-4.192 2.273.5.5 0 0 1-.837-.546A6 6 0 0 1 14 8a.5.5 0 0 1-1.001 0 5 5 0 0 0-5-5zM2.5 7.5A.5.5 0 0 1 3 8a5 5 0 0 0 9.192 2.727.5.5 0 1 1 .837.546A6 6 0 0 1 2 8a.5.5 0 0 1 .501-.5z"/>
-              </svg>
-              Rafraîchir
-            </a>
-            —
-            <a class="text-white text-decoration-none" href="#" id="linkStream">
-              <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-box-arrow-up-right" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
-                <path fill-rule="evenodd" d="M1.5 13A1.5 1.5 0 0 0 3 14.5h8a1.5 1.5 0 0 0 1.5-1.5V9a.5.5 0 0 0-1 0v4a.5.5 0 0 1-.5.5H3a.5.5 0 0 1-.5-.5V5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 0 0-1H3A1.5 1.5 0 0 0 1.5 5v8zm7-11a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-1 0V2.5H9a.5.5 0 0 1-.5-.5z"/>
-                <path fill-rule="evenodd" d="M14.354 1.646a.5.5 0 0 1 0 .708l-8 8a.5.5 0 0 1-.708-.708l8-8a.5.5 0 0 1 .708 0z"/>
-              </svg>
-              Ouvrir le flux dans une application externe
-            </a>
-            —
-            <a class="text-white text-decoration-none" href="#aboutModal" data-toggle="modal">
-              <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-info-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
-                <path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
-                <path d="M8.93 6.588l-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588z"/>
-                <circle cx="8" cy="4.5" r="1"/>
-              </svg>
-              À propos
-            </a>
-          </p>
-        </div>
-        <div class="col-lg-4 h-100 px-0">
-          <div class="embed-responsive h-100 shadow-lg rounded-left">
-            <iframe src="https://irc.crans.org/web/?join=video_NOM&nick=viewer1&password=&realname=Viewer1"></iframe>
-          </div>
-        </div>
-      </div>
-  </div>
-
-  <div class="modal fade" id="aboutModal" tabindex="-1">
-    <div class="modal-dialog-centered modal-dialog modal-lg">
-      <div class="modal-content">
-        <div class="modal-header">
-          <h4 class="modal-title" id="exampleModalLabel">À propos</h4>
-          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-            <span aria-hidden="true">&times;</span>
-          </button>
-        </div>
-        <div class="modal-body">
-          <p>Crans Stream est un service maintenu par le <a href="https://crans.org/">Crans</a> permettant de diffuser un contenu vidéo. Il a pour but d'être utilisé pour diffuser des séminaires ou évènements.</p>
-
-          <h4>Comment je diffuse ?</h4>
-          <p>Pour diffuser un contenu vous devez être adhérent Crans et être sur le campus de Cachan.</p>
-          <h5>Avec Open Broadcaster Software</h5>
-          <p>
-            <a href="https://obsproject.com/">Open Broadcaster Software (OBS)</a>
-            est une solution libre et open source de diffusion vidéo.
-            Pour diffuser sur cette plateforme, allez dans l'onglet « Stream (flux) » des paramètres :
-          </p> 
-          <ul>
-            <li><b>Serveur :</b> <code>rtmps://stream.adm.crans.org:1935/live</code>,</li>
-            <li><b>Clé de stream :</b> votre identifiant.</li>
-          </ul>
-          <h5>Avec FFmpeg</h5>
-          <p>
-            <code>
-              ffmpeg -re -i mavideo.webm -vcodec libx264 -vprofile baseline
-              -acodec aac -strict -2 -f flv
-              rtmps://stream.adm.crans.org:1935/live/identifiant
-            </code>
-          </p>
-
-          <h4>Mentions légales</h4>
-          <p>
-            Le service de diffusion vidéo du Crans est un service d'hébergement
-            au sens de l'article 6, I, 2e de la loi 2004-575 du 21 juin 2004.
-            Conformément aux dispositions de l'article 6, II du même,
-            l'association Crans conserve les données de nature à permettre
-            l'identification des auteurs du contenu diffusé.
-            Ce service est hébergé par l'association Crans, au
-            61 Avenue du Président Wilson, 94235 Cachan Cedex, France.
-          </p>
-          <p>
-            <b>En cas de réclamation sur le contenu diffusé</b> de type
-            <code>https://stream.crans.org/identifiant</code>,
-            l'auteur peut être contacté par courrier à l'adresse
-            <code>identifiant@crans.org</code>.
-            La loi vous autorise à contacter directement l'hébergeur à
-            l'adresse suivante :
-            <pre>Association Crans - ENS Paris-Saclay<br/>Notification de Contenus Illicites<br/>61, Avenue du Président Wilson<br/>94235 Cachan Cedex<br/>France</pre>
-            Vous pouvez également envoyer directement vos réclamations par
-            courrier électronique à l'adresse <code>bureau[at]crans.org</code>.
-          </p>
-        </div>
-      </div>
-    </div>
-  </div>
-
-  <script src="lib/jquery/jquery.min.js"></script>
-  <script src="lib/bootstrap4/js/bootstrap.min.js"></script>
-  <script src="//unpkg.com/video.js@7/dist/video.min.js"></script>
-  <script>
-  // Get current stream
-  let streamId = window.location.pathname.split("/").pop()
-  if (streamId.length === 0) {
-    streamId = "crans"  // default on crans account
-  }
-  const streamUrl = `/hls/${streamId}.m3u8`
-  document.getElementById("linkStream").href = streamUrl
-
-  // Create player
-  const player = videojs('my-video', {
-    controls: true,
-    autoplay: true,
-    preload: 'auto',
-    muted: true,
-    fluid: true,
-    aspectRatio: '16:9'
-  })
-
-  function loadStream() {
-    fetch(streamUrl, {
-      method: 'HEAD',
-       cache: 'no-cache'
-    }).then((response) => {
-     if (response.ok) {
-        // Stream exists, load!
-        player.poster()
-        player.src({
-          type: 'application/x-mpegURL',
-           src: streamUrl
-        })
-      } else {
-        // Stream does not exist, alert!
-        player.poster('/no-stream.jpg')
-      }
-    })
-  }
-
-  $(document).ready(function () {
-    loadStream()
-
-    document.getElementById("refreshStream").addEventListener("click", function(){
-      loadStream()
-    })
-  })
-  </script>
-</body>
-</html>
diff --git a/roles/nginx-rtmp/files/no-stream.jpg b/roles/nginx-rtmp/files/no-stream.jpg
deleted file mode 100644
index af44c5a43ea0399422d9fe21c302a262c53f7a3e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 112140
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
BUG4w?

diff --git a/roles/nginx-rtmp/handlers/main.yml b/roles/nginx-rtmp/handlers/main.yml
deleted file mode 100644
index 3b8f2d69..00000000
--- a/roles/nginx-rtmp/handlers/main.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-
-- name: restart nginx
-  service:
-    name: nginx
-    state: restarted
-  become: true
diff --git a/roles/nginx-rtmp/tasks/main.yml b/roles/nginx-rtmp/tasks/main.yml
deleted file mode 100644
index a60c62cb..00000000
--- a/roles/nginx-rtmp/tasks/main.yml
+++ /dev/null
@@ -1,59 +0,0 @@
----
-
-- name: Install nginx with rtmp module
-  apt:
-    name:
-      - nginx-full
-      - libnginx-mod-rtmp
-      - libjs-bootstrap4
-  register: apt_result
-  retries: 3
-  until: apt_result is succeeded
-  become: yes
-
-- name: Copy module and site configuration files
-  template:
-    src: "{{ item }}.j2"
-    dest: "/etc/{{ item }}"
-  loop:
-    - nginx/modules-available/60-rtmp.conf
-    - nginx/sites-available/stream
-  notify: restart nginx
-
-- name: Enable NGINX site
-  file:
-    src: /etc/nginx/sites-available/stream
-    dest: /etc/nginx/sites-enabled/stream
-    state: link
-  notify: restart nginx
-
-- name: Enable RTMP module
-  file:
-    src: /etc/nginx/modules-available/60-rtmp.conf
-    dest: /etc/nginx/modules-enabled/60-rtmp.conf
-    state: link
-  notify: restart nginx
-
-- name: Create site folder
-  file:
-    path: /var/www/stream/hls
-    state: directory
-
-- name: Copy index.html and assets
-  copy:
-    src: "{{ item }}"
-    dest: "/var/www/stream/{{ item }}"
-  loop:
-    - index.html
-    - no-stream.jpg
-
-- name: Link javascript libs
-  file:
-    src: /usr/share/javascript
-    dest: /var/www/stream/lib
-    state: link
-
-- name: Configure tmpfs
-  lineinfile:
-    path: /etc/fstab
-    line: tmpfs /var/www/stream/hls tmpfs rw,noexec,nodev,nosuid 0 0
diff --git a/roles/nginx-rtmp/templates/nginx/modules-available/60-rtmp.conf.j2 b/roles/nginx-rtmp/templates/nginx/modules-available/60-rtmp.conf.j2
deleted file mode 100644
index e780fb6c..00000000
--- a/roles/nginx-rtmp/templates/nginx/modules-available/60-rtmp.conf.j2
+++ /dev/null
@@ -1,18 +0,0 @@
-{{ ansible_header | comment }}
-
-rtmp {
-    server {
-        listen 1935;
-        chunk_size 4096;
-        application live {
-            live on;
-            hls on;
-            hls_path /var/www/stream/hls/;
-            hls_fragment 3;
-            hls_playlist_length 20;
-
-            record off;
-        }
-    }
-}
-
diff --git a/roles/nginx-rtmp/templates/nginx/sites-available/stream.j2 b/roles/nginx-rtmp/templates/nginx/sites-available/stream.j2
deleted file mode 100644
index 2a074c8d..00000000
--- a/roles/nginx-rtmp/templates/nginx/sites-available/stream.j2
+++ /dev/null
@@ -1,40 +0,0 @@
-{{ ansible_header | comment }}
-
-server {
-    listen 80;
-    listen [::]:80;
-
-    server_name {{ nginx_rtmp.uri }};
-
-    root /var/www/stream;
-    index index.html;
-
-    location / {
-        try_files $uri $uri/ /index.html;
-    }
-
-    location /hls {
-        # Disable cache
-        add_header Cache-Control no-cache;
-
-        # CORS setup
-        add_header 'Access-Control-Allow-Origin' '*' always;
-        add_header 'Access-Control-Expose-Headers' 'Content-Length';
-
-        # allow CORS preflight requests
-        if ($request_method = 'OPTIONS') {
-            add_header 'Access-Control-Allow-Origin' '*';
-            add_header 'Access-Control-Max-Age' 1728000;
-            add_header 'Content-Type' 'text/plain charset=UTF-8';
-            add_header 'Content-Length' 0;
-            return 204;
-        }
-
-        types {
-            application/dash+xml mpd;
-            application/vnd.apple.mpegurl m3u8;
-            video/mp2t ts;
-        }
-    }
-}
-
-- 
GitLab


From 84fb96eab62d2042b56495bcae839c6851747dcb Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 15:59:28 +0100
Subject: [PATCH 04/49] Create generic Nginx template

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 plays/mailman.yml                             |   1 +
 plays/mirror.yml                              |   1 +
 roles/nginx-mailman/tasks/main.yml            |  15 ---
 .../nginx/snippets/fastcgi-mailman.conf.j2~   |  18 ----
 .../nginx/snippets/options-ssl.conf.j2        |  17 ---
 .../templates/update-motd.d/05-service.j2     |   3 -
 roles/nginx-pubftp/tasks/main.yml             |  14 ---
 .../nginx/sites-available/service.j2          | 102 ++++++++++++++++++
 .../templates/nginx/snippets/fastcgi.conf.j2} |   0
 9 files changed, 104 insertions(+), 67 deletions(-)
 delete mode 100644 roles/nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2~
 delete mode 100644 roles/nginx-mailman/templates/nginx/snippets/options-ssl.conf.j2
 delete mode 100755 roles/nginx-mailman/templates/update-motd.d/05-service.j2
 create mode 100644 roles/nginx/templates/nginx/sites-available/service.j2
 rename roles/{nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2 => nginx/templates/nginx/snippets/fastcgi.conf.j2} (100%)

diff --git a/plays/mailman.yml b/plays/mailman.yml
index 6a84058b..56008f2a 100755
--- a/plays/mailman.yml
+++ b/plays/mailman.yml
@@ -24,6 +24,7 @@
         trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
   roles:
     - mailman
+    - nginx
     - nginx-mailman
 
 # Deploy Mailman3
diff --git a/plays/mirror.yml b/plays/mirror.yml
index d776c8c8..b7a1f219 100755
--- a/plays/mirror.yml
+++ b/plays/mirror.yml
@@ -74,4 +74,5 @@
   roles:
     - ftpsync
     - rsync-mirror
+    - nginx
     - nginx-pubftp
diff --git a/roles/nginx-mailman/tasks/main.yml b/roles/nginx-mailman/tasks/main.yml
index e2036b6b..2e4cef6e 100644
--- a/roles/nginx-mailman/tasks/main.yml
+++ b/roles/nginx-mailman/tasks/main.yml
@@ -1,13 +1,4 @@
 ---
-- name: Install NGINX
-  apt:
-    update_cache: true
-    name:
-      - nginx
-  register: apt_result
-  retries: 3
-  until: apt_result is succeeded
-
 - name: Copy configuration files
   template:
     src: "{{ item.src }}"
@@ -35,9 +26,3 @@
     force: true
   when: not ansible_check_mode
   notify: Reload nginx
-
-- name: Indicate role in motd
-  template:
-    src: update-motd.d/05-service.j2
-    dest: /etc/update-motd.d/05-nginx-mailman
-    mode: 0755
diff --git a/roles/nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2~ b/roles/nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2~
deleted file mode 100644
index 3ce2f923..00000000
--- a/roles/nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2~
+++ /dev/null
@@ -1,18 +0,0 @@
-{{ ansible_header | comment }}
-
-# regex to split $uri to $fastcgi_script_name and $fastcgi_path
-fastcgi_split_path_info (^/[^/]*)(.*)$;
-
-# check that the PHP script exists before passing it
-try_files $fastcgi_script_name =404;
-
-# Bypass the fact that try_files resets $fastcgi_path_info
-# see: http://trac.nginx.org/nginx/ticket/321
-set $path_info $fastcgi_path_info;
-fastcgi_param PATH_INFO $path_info;
-
-# Let NGINX handle errors
-fastcgi_intercept_errors on;
-
-include /etc/nginx/fastcgi.conf;
-fastcgi_pass unix:/var/run/fcgiwrap.socket;
\ No newline at end of file
diff --git a/roles/nginx-mailman/templates/nginx/snippets/options-ssl.conf.j2 b/roles/nginx-mailman/templates/nginx/snippets/options-ssl.conf.j2
deleted file mode 100644
index 79d75395..00000000
--- a/roles/nginx-mailman/templates/nginx/snippets/options-ssl.conf.j2
+++ /dev/null
@@ -1,17 +0,0 @@
-{{ ansible_header | comment }}
-
-ssl_certificate {{ nginx.ssl.cert }};
-ssl_certificate_key {{ nginx.ssl.key }};
-ssl_session_timeout 1d;
-ssl_session_cache shared:MozSSL:10m;
-ssl_session_tickets off;
-ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
-ssl_protocols TLSv1.2 TLSv1.3;
-
-ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
-ssl_prefer_server_ciphers off;
-
-# Enable OCSP Stapling, point to certificate chain
-ssl_stapling on;
-ssl_stapling_verify on;
-ssl_trusted_certificate {{ nginx.ssl.trusted_cert }};
diff --git a/roles/nginx-mailman/templates/update-motd.d/05-service.j2 b/roles/nginx-mailman/templates/update-motd.d/05-service.j2
deleted file mode 100755
index 82373d0b..00000000
--- a/roles/nginx-mailman/templates/update-motd.d/05-service.j2
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/tail +14
-{{ ansible_header | comment }}
-> NGINX a été déployé sur cette machine. Voir /etc/nginx/.
diff --git a/roles/nginx-pubftp/tasks/main.yml b/roles/nginx-pubftp/tasks/main.yml
index 2d5ccd7a..d4e0a1f9 100644
--- a/roles/nginx-pubftp/tasks/main.yml
+++ b/roles/nginx-pubftp/tasks/main.yml
@@ -1,12 +1,4 @@
 ---
-- name: Install NGINX
-  apt:
-    update_cache: true
-    name: nginx
-  register: apt_result
-  retries: 3
-  until: apt_result is succeeded
-
 - name: Copy configuration files
   template:
     src: "{{ item.src }}"
@@ -20,9 +12,3 @@
       dest: /pubftp/.html/FOOTER.html
     - src: html/style.min.css.j2
       dest: /pubftp/.html/style.min.css
-
-- name: Indicate role in motd
-  template:
-    src: update-motd.d/05-service.j2
-    dest: /etc/update-motd.d/05-nginx-pubftp
-    mode: 0755
diff --git a/roles/nginx/templates/nginx/sites-available/service.j2 b/roles/nginx/templates/nginx/sites-available/service.j2
new file mode 100644
index 00000000..a7b3bacb
--- /dev/null
+++ b/roles/nginx/templates/nginx/sites-available/service.j2
@@ -0,0 +1,102 @@
+{{ ansible_header | comment }}
+
+{% for upstream in nginx.upstreams -%}
+upstream {{ upstream.name }} {
+    # Path of the server
+    server {{ upstream.server }};
+}
+{% endfor -%}
+
+{% if nginx.default_ssl_host -%}
+# Redirect all services to the main site
+server {
+    listen 443 default_server ssl;
+    listen [::]:443 default_server ssl;
+    include "/etc/nginx/snippets/options-ssl.conf";
+
+    server_name {{ ngix.default_ssl_host }};
+    charset utf-8;
+
+    # Hide Nginx version
+    server_tokens off;
+
+    location / {
+        return 302 https://{{ nginx.default_ssl_host }}$request_uri;
+    }
+}
+{% endif -%}
+
+{% if nginx.default_host -%}
+# Redirect all services to the main site
+server {
+    listen 80 default_server;
+    listen [::]:80 default_server;
+
+    server_name {{ nginx.default_host }};
+    charset utf-8;
+
+    # Hide Nginx version
+    server_tokens off;
+
+    location / {
+        return 302 http://{{ nginx.default_host }}$request_uri;
+    }
+}
+{% endif -%}
+
+{% for server in nginx.servers %}
+{% if server.ssl -%}
+# Redirect HTTP to HTTPS
+server {
+    listen 80 default;
+    listen [::]:80 default;
+
+    server_name {{ server.server_name|join:" " }};
+    charset utf-8;
+
+    # Hide Nginx version
+    server_tokens off;
+
+    location / {
+        return 302 https://{{ server.server_name }}$request_uri;
+    }
+}
+{% endif -%}
+
+server {
+    {% if server.ssl -%}
+    listen 443 default_server ssl;
+    listen [::]:443 default_server ssl;
+    include "/etc/nginx/snippets/options-ssl.conf";
+    {% else -%}
+    listen 80 default;
+    listen [::]:80 default;
+    {% endif -%}
+
+    server_name {{ server.server_name }};
+    charset utf-8;
+
+    # Hide Nginx version
+    server_tokens off;
+
+    {% if server.root -%}
+    root {{ server.root }};
+    {% endif -%}
+    {% if server.index -%}
+    index {{ server.index }};
+    {% endif -%}
+
+    {% if server.access_log -%}
+    access_log {{ server.access_log }};
+    {% endif -%}
+    {% if server.error_log -%}
+    error_log {{ server.error_log }};
+    {% endif -%}
+
+    {% for location in server.locations -%}
+    location {{ location.filter }} {
+        {{ location.params|join:"\n        "|unsafe }}
+    }
+    {% endfor -%}
+}
+{% endfor %}
diff --git a/roles/nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2 b/roles/nginx/templates/nginx/snippets/fastcgi.conf.j2
similarity index 100%
rename from roles/nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2
rename to roles/nginx/templates/nginx/snippets/fastcgi.conf.j2
-- 
GitLab


From 244e1c284b006682a1301fa8d4f0c2e5f1a21d9e Mon Sep 17 00:00:00 2001
From: ynerant <ynerant@crans.org>
Date: Wed, 17 Feb 2021 20:28:26 +0100
Subject: [PATCH 05/49] Cransible mailman nginx configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/mailman.yml                        | 54 +++++++++++++++++++
 hosts                                         |  3 ++
 .../nginx/sites-available/service.j2          | 16 +++---
 3 files changed, 66 insertions(+), 7 deletions(-)
 create mode 100644 group_vars/mailman.yml

diff --git a/group_vars/mailman.yml b/group_vars/mailman.yml
new file mode 100644
index 00000000..ee9fc899
--- /dev/null
+++ b/group_vars/mailman.yml
@@ -0,0 +1,54 @@
+---
+loc_nginx:
+  default_server: lists.crans.org
+  default_ssl_server: lists.crans.org
+  servers:
+    - server_name:
+      - lists.crans.org
+      ssl: true
+      root: "/usr/lib/cgi-bin/mailman/"
+      index:
+        - index.htm
+        - index.html
+      locations:
+        - filter: "/error/"
+          params:
+            - "internal"
+            - "alias /var/www"
+        - filter: "/create"
+          params:
+            - "default_type text/html"
+            - "alias /etc/mailman/create.txt"
+        - filter: "~ ^/$"
+          params:
+            - "return 302 https://lists.crans.org/listinfo"
+        - filter: "~ ^/admin"
+          params:
+            - "satisfy any"
+            - "include \"/etc/nginx/snippets/fastcgi.conf\""
+            - "allow 185.230.76.0/22"
+            - "allow 2a0c:700:0::/40"
+            - "deny all"
+            - "auth_basic \"On n'aime pas les spambots, donc on a mis un mot de passe. Le login est Stop et le mot de passe est Spam.\""
+            - "auth_basic_user_file /etc/nginx/passwd"
+            - "error_page 401 /error/custom_401.html"
+        - filter: "~ ^/admin"
+          params:
+            - "satisfy any"
+            - "include \"/etc/nginx/snippets/fastcgi.conf\""
+            - "allow 185.230.76.0/22"
+            - "allow 2a0c:700:0::/40"
+            - "deny all"
+            - "auth_basic \"On n'aime pas les spambots, donc on a mis un mot de passe. Le login est Stop et le mot de passe est Spam.\""
+            - "auth_basic_user_file /etc/nginx/passwd"
+            - "error_page 401 /error/custom_401.html"
+        - filter: "/images/mailman"
+          params:
+            - "alias /usr/share/images/mailman"
+        - filter: "/robots.txt"
+          params:
+            - "alias /var/www/robots.txt"
+        - filter: "/archives"
+          params:
+            - "alias /var/lib/mailman/archives/public"
+            - "autoindex on"
diff --git a/hosts b/hosts
index 397f791c..92e55030 100644
--- a/hosts
+++ b/hosts
@@ -77,6 +77,9 @@ sputnik.adm.crans.org
 [linx]
 linx.adm.crans.org
 
+[mailman]
+redisdead.adm.crans.org
+
 [monitoring]
 monitoring.adm.crans.org
 
diff --git a/roles/nginx/templates/nginx/sites-available/service.j2 b/roles/nginx/templates/nginx/sites-available/service.j2
index a7b3bacb..13569dcc 100644
--- a/roles/nginx/templates/nginx/sites-available/service.j2
+++ b/roles/nginx/templates/nginx/sites-available/service.j2
@@ -14,32 +14,32 @@ server {
     listen [::]:443 default_server ssl;
     include "/etc/nginx/snippets/options-ssl.conf";
 
-    server_name {{ ngix.default_ssl_host }};
+    server_name {{ ngix.default_ssl_server }};
     charset utf-8;
 
     # Hide Nginx version
     server_tokens off;
 
     location / {
-        return 302 https://{{ nginx.default_ssl_host }}$request_uri;
+        return 302 https://{{ nginx.default_ssl_server }}$request_uri;
     }
 }
 {% endif -%}
 
-{% if nginx.default_host -%}
+{% if nginx.default_server -%}
 # Redirect all services to the main site
 server {
     listen 80 default_server;
     listen [::]:80 default_server;
 
-    server_name {{ nginx.default_host }};
+    server_name {{ nginx.default_server }};
     charset utf-8;
 
     # Hide Nginx version
     server_tokens off;
 
     location / {
-        return 302 http://{{ nginx.default_host }}$request_uri;
+        return 302 http://{{ nginx.default_server }}$request_uri;
     }
 }
 {% endif -%}
@@ -83,7 +83,7 @@ server {
     root {{ server.root }};
     {% endif -%}
     {% if server.index -%}
-    index {{ server.index }};
+    index {{ server.index|join:" " }};
     {% endif -%}
 
     {% if server.access_log -%}
@@ -95,7 +95,9 @@ server {
 
     {% for location in server.locations -%}
     location {{ location.filter }} {
-        {{ location.params|join:"\n        "|unsafe }}
+        {% for param in params -%}
+        {{ param }};
+        {% endfor -%}
     }
     {% endfor -%}
 }
-- 
GitLab


From c0fbe6d000d76fecd4de534fcbdd5e823dbf7819 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 16:42:57 +0100
Subject: [PATCH 06/49] Cransible charybde nginx configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 host_vars/charybde.adm.crans.org.yml | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/host_vars/charybde.adm.crans.org.yml b/host_vars/charybde.adm.crans.org.yml
index 5eea3c44..0bda434f 100644
--- a/host_vars/charybde.adm.crans.org.yml
+++ b/host_vars/charybde.adm.crans.org.yml
@@ -33,3 +33,29 @@ to_backup:
   hosts_allow: "*",
   read_only: "yes"
   }
+
+loc_nginx:
+  servers:
+    server_name:
+      - "ftp"
+      - "ftp.*"
+      - "mirror"
+      - "mirror.*"
+      - "archive.ubuntu.com"
+      - "fr.archive.ubuntu.com"
+      - "security.ubuntu.com"
+      - "ftps"
+      - "ftps.*"
+    root: "/pubftp"
+    locations:
+      - filter: "/"
+      - params:
+        - "autoindex on"
+        - "autoindex_exact_size off"
+        - "add_before_body /.html/HEADER.html"
+        - "add_after_body /.html/FOOTER.html"
+      - filter: "/pub/events/"
+        params:
+          - "mp4"
+          - "mp4_buffer_size 1m"
+          - "mp4_max_buffer_size 5m"
-- 
GitLab


From 6c8be2638c9a49e5d7566e00dd09c1e7dda50d39 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 16:46:06 +0100
Subject: [PATCH 07/49] Add default global nginx configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/nginx.yml      | 9 +++++++++
 group_vars/nginx_rtmp.yml | 4 ----
 hosts                     | 4 +---
 3 files changed, 10 insertions(+), 7 deletions(-)
 create mode 100644 group_vars/nginx.yml
 delete mode 100644 group_vars/nginx_rtmp.yml

diff --git a/group_vars/nginx.yml b/group_vars/nginx.yml
new file mode 100644
index 00000000..8591ff32
--- /dev/null
+++ b/group_vars/nginx.yml
@@ -0,0 +1,9 @@
+---
+glob_nginx:
+  servers:
+    server_name:
+      - "default"
+      - "_"
+    root: "/var/www/html"
+    locations:
+      - filter: "/"
diff --git a/group_vars/nginx_rtmp.yml b/group_vars/nginx_rtmp.yml
deleted file mode 100644
index d5626daa..00000000
--- a/group_vars/nginx_rtmp.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-
-glob_nginx_rtmp:
-  uri: stream.crans.org
diff --git a/hosts b/hosts
index 92e55030..ce350a71 100644
--- a/hosts
+++ b/hosts
@@ -86,10 +86,8 @@ monitoring.adm.crans.org
 [nginx]
 charybde.adm.crans.org
 
-[nginx_rtmp]
-fluxx.adm.crans.org
-
 [nginx:children]
+mailman
 reverseproxy
 
 [ntp_server]
-- 
GitLab


From 6ee4d8b44d6568c256faa33543cf5db94be383dc Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 16:49:47 +0100
Subject: [PATCH 08/49] Deploy nginx configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 roles/nginx/tasks/main.yml | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 0ff8bdc6..3d80b8ba 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -44,6 +44,22 @@
   notify: Reload nginx
   ignore_errors: "{{ ansible_check_mode }}"
 
+- name: Copy service nginx configuration
+  when: nginx.servers|length > 0
+  template:
+    src: "nginx/sites-available/service.j2"
+    dest: "/etc/nginx/sites-available/service"
+  notify: Reload nginx
+
+- name: Activate local nginx service site
+  when: nginx.servers|length > 0
+  file:
+    src: "/etc/nginx/sites-available/service"
+    dest: "/etc/nginx/sites-enabled/service"
+    state: link
+  notify: Reload nginx
+  ignore_errors: "{{ ansible_check_mode }}"
+
 - name: Copy 50x error page
   template:
     src: www/html/50x.html.j2
-- 
GitLab


From 3fceaeb8367863c6fcf9a298f696fb61e71d8775 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 16:56:01 +0100
Subject: [PATCH 09/49] [nginx] allow setting credentials to a nginx server

---
 group_vars/mailman.yml                | 2 ++
 roles/nginx-mailman/tasks/main.yml    | 6 ------
 roles/nginx/tasks/main.yml            | 6 ++++++
 roles/nginx/templates/nginx/passwd.j2 | 4 ++++
 4 files changed, 12 insertions(+), 6 deletions(-)
 create mode 100644 roles/nginx/templates/nginx/passwd.j2

diff --git a/group_vars/mailman.yml b/group_vars/mailman.yml
index ee9fc899..ce6c454e 100644
--- a/group_vars/mailman.yml
+++ b/group_vars/mailman.yml
@@ -2,6 +2,8 @@
 loc_nginx:
   default_server: lists.crans.org
   default_ssl_server: lists.crans.org
+  auth_passwd:
+    Stop: "$apr1$NXaV5H7Q$J3ora3Jo5h775Y1nm93PN1"
   servers:
     - server_name:
       - lists.crans.org
diff --git a/roles/nginx-mailman/tasks/main.yml b/roles/nginx-mailman/tasks/main.yml
index 2e4cef6e..f74b3f0f 100644
--- a/roles/nginx-mailman/tasks/main.yml
+++ b/roles/nginx-mailman/tasks/main.yml
@@ -4,14 +4,8 @@
     src: "{{ item.src }}"
     dest: "{{ item.dest }}"
   loop:
-    - src: nginx/sites-available/mailman.j2
-      dest: /etc/nginx/sites-available/mailman
-    - src: nginx/mailman_passwd.j2
-      dest: /etc/nginx/mailman_passwd
     - src: nginx/snippets/fastcgi-mailman.conf.j2
       dest: /etc/nginx/snippets/fastcgi-mailman.conf
-    - src: nginx/snippets/options-ssl.conf.j2
-      dest: /etc/nginx/snippets/options-ssl.conf
     - src: var/www/robots.txt.j2
       dest: /var/www/robots.txt
     - src: var/www/custom_401.html.j2
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 3d80b8ba..61b69322 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -70,3 +70,9 @@
     src: update-motd.d/05-service.j2
     dest: /etc/update-motd.d/05-nginx
     mode: 0755
+
+- name: Install passwords
+  template:
+    src: nginx/passwd.j2
+    dest: /etc/nginx/passwd
+    mode: 0644
diff --git a/roles/nginx/templates/nginx/passwd.j2 b/roles/nginx/templates/nginx/passwd.j2
new file mode 100644
index 00000000..ea58b2da
--- /dev/null
+++ b/roles/nginx/templates/nginx/passwd.j2
@@ -0,0 +1,4 @@
+{{ ansible_header | comment }}
+{% for user, hash in nginx.auth_passwd -%}
+{{ user }}: {{ hash }}
+{% endfor -%}
-- 
GitLab


From 0eaee6c78fb7f2d9ce3ec685527b60767405d2b4 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 17:00:21 +0100
Subject: [PATCH 10/49] [nginx] Copy robots.txt

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/mailman.yml                                      | 1 +
 roles/nginx-mailman/tasks/main.yml                          | 2 --
 roles/nginx/tasks/main.yml                                  | 6 ++++++
 .../var/www => nginx/templates/www/html}/robots.txt.j2      | 0
 4 files changed, 7 insertions(+), 2 deletions(-)
 rename roles/{nginx-mailman/templates/var/www => nginx/templates/www/html}/robots.txt.j2 (100%)

diff --git a/group_vars/mailman.yml b/group_vars/mailman.yml
index ce6c454e..1eac28df 100644
--- a/group_vars/mailman.yml
+++ b/group_vars/mailman.yml
@@ -4,6 +4,7 @@ loc_nginx:
   default_ssl_server: lists.crans.org
   auth_passwd:
     Stop: "$apr1$NXaV5H7Q$J3ora3Jo5h775Y1nm93PN1"
+  deploy_robots_file: true
   servers:
     - server_name:
       - lists.crans.org
diff --git a/roles/nginx-mailman/tasks/main.yml b/roles/nginx-mailman/tasks/main.yml
index f74b3f0f..b381b1ab 100644
--- a/roles/nginx-mailman/tasks/main.yml
+++ b/roles/nginx-mailman/tasks/main.yml
@@ -6,8 +6,6 @@
   loop:
     - src: nginx/snippets/fastcgi-mailman.conf.j2
       dest: /etc/nginx/snippets/fastcgi-mailman.conf
-    - src: var/www/robots.txt.j2
-      dest: /var/www/robots.txt
     - src: var/www/custom_401.html.j2
       dest: /var/www/custom_401.html
   notify: Reload nginx
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 61b69322..11cf30ad 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -65,6 +65,12 @@
     src: www/html/50x.html.j2
     dest: /var/www/html/50x.html
 
+- name: Copy robots.txt file
+  when: nginx.deploy_robots_file
+  template:
+    src: www/html/robots.txt.j2
+    dest: /var/www/html/robots.txt
+
 - name: Indicate role in motd
   template:
     src: update-motd.d/05-service.j2
diff --git a/roles/nginx-mailman/templates/var/www/robots.txt.j2 b/roles/nginx/templates/www/html/robots.txt.j2
similarity index 100%
rename from roles/nginx-mailman/templates/var/www/robots.txt.j2
rename to roles/nginx/templates/www/html/robots.txt.j2
-- 
GitLab


From dafa3685ceb398fcda7995d58db2596de4fa24aa Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 17:04:53 +0100
Subject: [PATCH 11/49] [nginx] Copy 401 error page if we use credentials

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/mailman.yml                                     | 6 +++---
 roles/nginx/tasks/main.yml                                 | 7 +++++++
 .../custom_401.html.j2 => nginx/templates/www/401.html.j2} | 0
 3 files changed, 10 insertions(+), 3 deletions(-)
 rename roles/{nginx-mailman/templates/var/www/custom_401.html.j2 => nginx/templates/www/401.html.j2} (100%)

diff --git a/group_vars/mailman.yml b/group_vars/mailman.yml
index 1eac28df..4a70e7f4 100644
--- a/group_vars/mailman.yml
+++ b/group_vars/mailman.yml
@@ -17,7 +17,7 @@ loc_nginx:
         - filter: "/error/"
           params:
             - "internal"
-            - "alias /var/www"
+            - "alias /var/www/html"
         - filter: "/create"
           params:
             - "default_type text/html"
@@ -34,7 +34,7 @@ loc_nginx:
             - "deny all"
             - "auth_basic \"On n'aime pas les spambots, donc on a mis un mot de passe. Le login est Stop et le mot de passe est Spam.\""
             - "auth_basic_user_file /etc/nginx/passwd"
-            - "error_page 401 /error/custom_401.html"
+            - "error_page 401 /error/401.html"
         - filter: "~ ^/admin"
           params:
             - "satisfy any"
@@ -44,7 +44,7 @@ loc_nginx:
             - "deny all"
             - "auth_basic \"On n'aime pas les spambots, donc on a mis un mot de passe. Le login est Stop et le mot de passe est Spam.\""
             - "auth_basic_user_file /etc/nginx/passwd"
-            - "error_page 401 /error/custom_401.html"
+            - "error_page 401 /error/401.html"
         - filter: "/images/mailman"
           params:
             - "alias /usr/share/images/mailman"
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 11cf30ad..159f5cf9 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -78,7 +78,14 @@
     mode: 0755
 
 - name: Install passwords
+  when: nginx.auth_passwd|length > 0
   template:
     src: nginx/passwd.j2
     dest: /etc/nginx/passwd
     mode: 0644
+
+- name: Copy 401 error page
+  when: nginx.auth_passwd|length > 0
+  template:
+    src: www/html/401.html.j2
+    dest: /var/www/html/401.html
diff --git a/roles/nginx-mailman/templates/var/www/custom_401.html.j2 b/roles/nginx/templates/www/401.html.j2
similarity index 100%
rename from roles/nginx-mailman/templates/var/www/custom_401.html.j2
rename to roles/nginx/templates/www/401.html.j2
-- 
GitLab


From ec262bd5c1286d05102b19c55f7186c2286af37a Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 17:16:04 +0100
Subject: [PATCH 12/49] [nginx] Drop role nginx-mailman

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 plays/mailman.yml                             |  3 +-
 roles/nginx-mailman/handlers/main.yml         |  5 -
 roles/nginx-mailman/tasks/main.yml            | 20 ----
 .../templates/nginx/mailman_passwd.j2         |  2 -
 .../nginx/sites-available/mailman.j2          | 94 -------------------
 5 files changed, 1 insertion(+), 123 deletions(-)
 delete mode 100644 roles/nginx-mailman/handlers/main.yml
 delete mode 100644 roles/nginx-mailman/tasks/main.yml
 delete mode 100644 roles/nginx-mailman/templates/nginx/mailman_passwd.j2
 delete mode 100644 roles/nginx-mailman/templates/nginx/sites-available/mailman.j2

diff --git a/plays/mailman.yml b/plays/mailman.yml
index 56008f2a..17aa53da 100755
--- a/plays/mailman.yml
+++ b/plays/mailman.yml
@@ -20,12 +20,11 @@
     nginx:
       ssl:
         cert: /etc/letsencrypt/live/crans.org/fullchain.pem
-        key: /etc/letsencrypt/live/crans.org/privkey.pem
+        cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
         trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
   roles:
     - mailman
     - nginx
-    - nginx-mailman
 
 # Deploy Mailman3
 - hosts: mailman.adm.crans.org
diff --git a/roles/nginx-mailman/handlers/main.yml b/roles/nginx-mailman/handlers/main.yml
deleted file mode 100644
index 6dfcdd76..00000000
--- a/roles/nginx-mailman/handlers/main.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-- name: Reload nginx
-  systemd:
-    name: nginx
-    state: reloaded
diff --git a/roles/nginx-mailman/tasks/main.yml b/roles/nginx-mailman/tasks/main.yml
deleted file mode 100644
index b381b1ab..00000000
--- a/roles/nginx-mailman/tasks/main.yml
+++ /dev/null
@@ -1,20 +0,0 @@
----
-- name: Copy configuration files
-  template:
-    src: "{{ item.src }}"
-    dest: "{{ item.dest }}"
-  loop:
-    - src: nginx/snippets/fastcgi-mailman.conf.j2
-      dest: /etc/nginx/snippets/fastcgi-mailman.conf
-    - src: var/www/custom_401.html.j2
-      dest: /var/www/custom_401.html
-  notify: Reload nginx
-
-- name: Enable mailman
-  file:
-    src: /etc/nginx/sites-available/mailman
-    dest: /etc/nginx/sites-enabled/mailman
-    state: link
-    force: true
-  when: not ansible_check_mode
-  notify: Reload nginx
diff --git a/roles/nginx-mailman/templates/nginx/mailman_passwd.j2 b/roles/nginx-mailman/templates/nginx/mailman_passwd.j2
deleted file mode 100644
index 741d52d9..00000000
--- a/roles/nginx-mailman/templates/nginx/mailman_passwd.j2
+++ /dev/null
@@ -1,2 +0,0 @@
-{{ ansible_header | comment }}
-Stop:$apr1$NXaV5H7Q$J3ora3Jo5h775Y1nm93PN1
diff --git a/roles/nginx-mailman/templates/nginx/sites-available/mailman.j2 b/roles/nginx-mailman/templates/nginx/sites-available/mailman.j2
deleted file mode 100644
index ba13c111..00000000
--- a/roles/nginx-mailman/templates/nginx/sites-available/mailman.j2
+++ /dev/null
@@ -1,94 +0,0 @@
-{{ ansible_header | comment }}
-server {
-	listen 80 default;
-	listen [::]:80 default;
-
-	server_name _;
-
-	location / {
-	    return 302 https://{{ mailman.default_host }}$request_uri;
-	}
-}
-
-# Redirect everybody to mailing lists
-server {
-	listen 443 default_server ssl;
-	listen [::]:443 default_server ssl;
-	server_name _;
-
-	include "/etc/nginx/snippets/options-ssl.conf";
-
-	location / {
-		 return 302 https://{{ mailman.default_host }}$request_uri;
-	}
-}
-
-server {
-	listen 443 ssl http2;
-	listen [::]:443 ssl http2;
-	server_name {{ mailman.default_host }};
-
-	include "/etc/nginx/snippets/options-ssl.conf";
-
-	root /usr/lib/cgi-bin/mailman/;
-	index index.htm index.html;
-
-        location /error/ {
-		internal;
-		alias /var/www/;
-        }
-
-	location /create {
-		default_type text/html;
-		alias /etc/mailman/create.html;
-	}
-
-	location ~ ^/$ {
-		return 302 https://{{ mailman.default_host }}/listinfo;
-	}
-
-	location / {
-		include "/etc/nginx/snippets/fastcgi-mailman.conf";
-	}
-
-        location ~ ^/listinfo {
-                satisfy any;
-		include "/etc/nginx/snippets/fastcgi-mailman.conf";
-
-		{% for net in mynetworks -%}
-                allow {{ net }};
-		{% endfor -%}
-                deny all;
-
-	        auth_basic {{ mailman.auth_basic }}
-		auth_basic_user_file /etc/nginx/mailman_passwd;
-
-		error_page 401 /error/custom_401.html;
-        }
-
-        location ~ ^/admin {
-                satisfy any;
-
-		include "/etc/nginx/snippets/fastcgi-mailman.conf";
-
-		{% for net in mynetworks -%}
-                allow {{ net }};
-		{% endfor -%}
-                deny all;
-
-	        auth_basic {{ mailman.auth_basic }}
-		auth_basic_user_file /etc/nginx/mailman_passwd;
-		error_page 401 /error/custom_401.html;
-        }
-
-
-	location /images/mailman { alias /usr/share/images/mailman;}
-
-	location /robots.txt { alias /var/www/robots.txt;}
-
-	location /archives {
-		alias /var/lib/mailman/archives/public;
-		autoindex on;
-	}
-
-}
-- 
GitLab


From a9897ec3c0b30e8a13022f6e7a44e39042a2bcd5 Mon Sep 17 00:00:00 2001
From: ynerant <ynerant@crans.org>
Date: Wed, 17 Feb 2021 20:29:12 +0100
Subject: [PATCH 13/49] [nginx] Load global and local nginx configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/nginx.yml       |  4 ++++
 plays/mailman.yml          | 12 +-----------
 plays/mirror.yml           |  2 ++
 roles/nginx/tasks/main.yml |  8 ++++----
 4 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/group_vars/nginx.yml b/group_vars/nginx.yml
index 8591ff32..00383aea 100644
--- a/group_vars/nginx.yml
+++ b/group_vars/nginx.yml
@@ -1,5 +1,9 @@
 ---
 glob_nginx:
+  ssl:
+    cert: /etc/letsencrypt/live/crans.org/fullchain.pem
+    cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
+    trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
   servers:
     server_name:
       - "default"
diff --git a/plays/mailman.yml b/plays/mailman.yml
index 17aa53da..a0a2a60f 100755
--- a/plays/mailman.yml
+++ b/plays/mailman.yml
@@ -8,20 +8,10 @@
       default_url: "https://lists.crans.org/"
       default_host: "lists.crans.org"
       default_language: "fr"
-      auth_basic: |
-        "On n'aime pas les spambots, donc on a mis un mot de passe. Le login est Stop et le mot de passe est Spam.";
-      custom_logo: "crans_icon_dark.svg"
-      custom_logo_name: "crans.svg"
-      custom_logo_url: "https://www.crans.org/"
-      custom_logo_alt: "CRANS"
     spamassassin: "SpamAssassin_crans"
     smtphost: "smtp.adm.crans.org"
     mynetworks: ['138.231.0.0/16', '185.230.76.0/22', '2a0c:700:0::/40']
-    nginx:
-      ssl:
-        cert: /etc/letsencrypt/live/crans.org/fullchain.pem
-        cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
-        trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
+    nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
   roles:
     - mailman
     - nginx
diff --git a/plays/mirror.yml b/plays/mirror.yml
index b7a1f219..4b5ba67a 100755
--- a/plays/mirror.yml
+++ b/plays/mirror.yml
@@ -71,6 +71,8 @@
         cron_time: "00 5"
         rsync_host: cdimage.ubuntu.com
         rsync_path: cdimage/ubuntu-mate/releases
+
+    nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
   roles:
     - ftpsync
     - rsync-mirror
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 159f5cf9..441ac4dd 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -21,7 +21,7 @@
     dest: /etc/letsencrypt/dhparam
 
 - name: Copy reverse proxy sites
-  when: nginx.reverseproxy_sites|length + nginx.redirect_sites|length > 0
+  when: nginx.reverseproxy_sites is defined or nginx.redirect_sites is defined
   template:
     src: "nginx/sites-available/{{ item }}.j2"
     dest: "/etc/nginx/sites-available/{{ item }}"
@@ -32,7 +32,7 @@
   notify: Reload nginx
 
 - name: Activate reverse proxy sites
-  when: nginx.reverseproxy_sites|length + nginx.redirect_sites|length > 0
+  when: nginx.reverseproxy_sites is defined or nginx.redirect_sites is defined
   file:
     src: "/etc/nginx/sites-available/{{ item }}"
     dest: "/etc/nginx/sites-enabled/{{ item }}"
@@ -45,14 +45,14 @@
   ignore_errors: "{{ ansible_check_mode }}"
 
 - name: Copy service nginx configuration
-  when: nginx.servers|length > 0
+  when: nginx.servers is defined and nginx.servers|length > 0
   template:
     src: "nginx/sites-available/service.j2"
     dest: "/etc/nginx/sites-available/service"
   notify: Reload nginx
 
 - name: Activate local nginx service site
-  when: nginx.servers|length > 0
+  when: nginx.servers|bool
   file:
     src: "/etc/nginx/sites-available/service"
     dest: "/etc/nginx/sites-enabled/service"
-- 
GitLab


From 2b8e0dbbffcabb7f35cc5d33dbd9b57b29592567 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 18:49:08 +0100
Subject: [PATCH 14/49] [nginx] Fix nginx template, this is now usable

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/mailman.yml                        |  5 +++-
 group_vars/nginx.yml                          |  6 +++++
 group_vars/reverseproxy.yml                   |  2 --
 roles/nginx/tasks/main.yml                    | 11 ++++++++
 roles/nginx/templates/nginx/passwd.j2         |  2 +-
 .../nginx/sites-available/service.j2          | 27 ++++++++++---------
 .../templates/www/{ => html}/401.html.j2      |  0
 7 files changed, 37 insertions(+), 16 deletions(-)
 rename roles/nginx/templates/www/{ => html}/401.html.j2 (100%)

diff --git a/group_vars/mailman.yml b/group_vars/mailman.yml
index 4a70e7f4..cd7d754b 100644
--- a/group_vars/mailman.yml
+++ b/group_vars/mailman.yml
@@ -25,7 +25,10 @@ loc_nginx:
         - filter: "~ ^/$"
           params:
             - "return 302 https://lists.crans.org/listinfo"
-        - filter: "~ ^/admin"
+        - filter: "/"
+          params:
+            - "include \"/etc/nginx/snippets/fastcgi.conf\""
+        - filter: "~ ^/listinfo"
           params:
             - "satisfy any"
             - "include \"/etc/nginx/snippets/fastcgi.conf\""
diff --git a/group_vars/nginx.yml b/group_vars/nginx.yml
index 00383aea..1d97f621 100644
--- a/group_vars/nginx.yml
+++ b/group_vars/nginx.yml
@@ -1,13 +1,19 @@
 ---
 glob_nginx:
+  contact: contact@crans.org
+  who: "L'équipe technique du Cr@ns"
   ssl:
     cert: /etc/letsencrypt/live/crans.org/fullchain.pem
     cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
     trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
+  default_server:
+  default_ssl_server:
   servers:
+    ssl: false
     server_name:
       - "default"
       - "_"
     root: "/var/www/html"
     locations:
       - filter: "/"
+  upstreams: []
diff --git a/group_vars/reverseproxy.yml b/group_vars/reverseproxy.yml
index 49f1ed78..fb542879 100644
--- a/group_vars/reverseproxy.yml
+++ b/group_vars/reverseproxy.yml
@@ -6,8 +6,6 @@ certbot:
   domains: "crans.org, *.crans.org, crans.fr, *.crans.fr, crans.eu, *.crans.eu"
 
 nginx:
-  contact: contact@crans.org
-  who: "l'équipe technique du Cr@ns"
   ssl:
     cert: /etc/letsencrypt/live/crans.org/fullchain.pem
     cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 441ac4dd..8d6d3823 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -25,6 +25,7 @@
   template:
     src: "nginx/sites-available/{{ item }}.j2"
     dest: "/etc/nginx/sites-available/{{ item }}"
+    mode: 0644
   loop:
     - reverseproxy
     - reverseproxy_redirect_dname
@@ -49,6 +50,7 @@
   template:
     src: "nginx/sites-available/service.j2"
     dest: "/etc/nginx/sites-available/service"
+    mode: 0644
   notify: Reload nginx
 
 - name: Activate local nginx service site
@@ -64,12 +66,18 @@
   template:
     src: www/html/50x.html.j2
     dest: /var/www/html/50x.html
+    owner: www-data
+    group: www-data
+    mode: 0644
 
 - name: Copy robots.txt file
   when: nginx.deploy_robots_file
   template:
     src: www/html/robots.txt.j2
     dest: /var/www/html/robots.txt
+    owner: www-data
+    group: www-data
+    mode: 0644
 
 - name: Indicate role in motd
   template:
@@ -89,3 +97,6 @@
   template:
     src: www/html/401.html.j2
     dest: /var/www/html/401.html
+    owner: www-data
+    group: www-data
+    mode: 0644
diff --git a/roles/nginx/templates/nginx/passwd.j2 b/roles/nginx/templates/nginx/passwd.j2
index ea58b2da..e87369c9 100644
--- a/roles/nginx/templates/nginx/passwd.j2
+++ b/roles/nginx/templates/nginx/passwd.j2
@@ -1,4 +1,4 @@
 {{ ansible_header | comment }}
-{% for user, hash in nginx.auth_passwd -%}
+{% for user, hash in nginx.auth_passwd.items() -%}
 {{ user }}: {{ hash }}
 {% endfor -%}
diff --git a/roles/nginx/templates/nginx/sites-available/service.j2 b/roles/nginx/templates/nginx/sites-available/service.j2
index 13569dcc..11afc9d5 100644
--- a/roles/nginx/templates/nginx/sites-available/service.j2
+++ b/roles/nginx/templates/nginx/sites-available/service.j2
@@ -7,14 +7,14 @@ upstream {{ upstream.name }} {
 }
 {% endfor -%}
 
-{% if nginx.default_ssl_host -%}
+{% if nginx.default_ssl_server -%}
 # Redirect all services to the main site
 server {
     listen 443 default_server ssl;
     listen [::]:443 default_server ssl;
     include "/etc/nginx/snippets/options-ssl.conf";
 
-    server_name {{ ngix.default_ssl_server }};
+    server_name {{ nginx.default_ssl_server }};
     charset utf-8;
 
     # Hide Nginx version
@@ -51,20 +51,20 @@ server {
     listen 80 default;
     listen [::]:80 default;
 
-    server_name {{ server.server_name|join:" " }};
+    server_name {{ server.server_name|join(" ") }};
     charset utf-8;
 
     # Hide Nginx version
     server_tokens off;
 
     location / {
-        return 302 https://{{ server.server_name }}$request_uri;
+        return 302 https://$host$request_uri;
     }
 }
 {% endif -%}
 
 server {
-    {% if server.ssl -%}
+    {% if server.ssl is defined and server.ssl -%}
     listen 443 default_server ssl;
     listen [::]:443 default_server ssl;
     include "/etc/nginx/snippets/options-ssl.conf";
@@ -73,32 +73,35 @@ server {
     listen [::]:80 default;
     {% endif -%}
 
-    server_name {{ server.server_name }};
+    server_name {{ server.server_name|join(" ") }};
     charset utf-8;
 
     # Hide Nginx version
     server_tokens off;
 
-    {% if server.root -%}
+    {% if server.root is defined -%}
     root {{ server.root }};
     {% endif -%}
-    {% if server.index -%}
-    index {{ server.index|join:" " }};
+    {% if server.index is defined -%}
+    index {{ server.index|join(" ") }};
     {% endif -%}
 
-    {% if server.access_log -%}
+    {% if server.access_log is defined -%}
     access_log {{ server.access_log }};
     {% endif -%}
-    {% if server.error_log -%}
+    {% if server.error_log is defined -%}
     error_log {{ server.error_log }};
     {% endif -%}
 
+    {% if server.locations is defined -%}
+
     {% for location in server.locations -%}
     location {{ location.filter }} {
-        {% for param in params -%}
+        {% for param in location.params -%}
         {{ param }};
         {% endfor -%}
     }
     {% endfor -%}
+{% endif -%}
 }
 {% endfor %}
diff --git a/roles/nginx/templates/www/401.html.j2 b/roles/nginx/templates/www/html/401.html.j2
similarity index 100%
rename from roles/nginx/templates/www/401.html.j2
rename to roles/nginx/templates/www/html/401.html.j2
-- 
GitLab


From 9f21a7ad796141740221585940db9f03e8523f73 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 19:12:11 +0100
Subject: [PATCH 15/49] [nginx] Drop nginx-pubftp role

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 plays/mirror.yml                              |  1 -
 roles/ftpsync/tasks/main.yml                  | 13 +++++++++++
 .../templates/html/FOOTER.html.j2             |  0
 .../templates/html/HEADER.html.j2             |  0
 .../templates/html/style.min.css.j2           |  0
 roles/nginx-pubftp/tasks/main.yml             | 14 ------------
 roles/nginx-pubftp/templates/nginx/ftp.j2     | 22 -------------------
 .../templates/update-motd.d/05-service.j2     |  3 ---
 8 files changed, 13 insertions(+), 40 deletions(-)
 rename roles/{nginx-pubftp => ftpsync}/templates/html/FOOTER.html.j2 (100%)
 rename roles/{nginx-pubftp => ftpsync}/templates/html/HEADER.html.j2 (100%)
 rename roles/{nginx-pubftp => ftpsync}/templates/html/style.min.css.j2 (100%)
 delete mode 100644 roles/nginx-pubftp/tasks/main.yml
 delete mode 100644 roles/nginx-pubftp/templates/nginx/ftp.j2
 delete mode 100755 roles/nginx-pubftp/templates/update-motd.d/05-service.j2

diff --git a/plays/mirror.yml b/plays/mirror.yml
index 4b5ba67a..56f83b3c 100755
--- a/plays/mirror.yml
+++ b/plays/mirror.yml
@@ -77,4 +77,3 @@
     - ftpsync
     - rsync-mirror
     - nginx
-    - nginx-pubftp
diff --git a/roles/ftpsync/tasks/main.yml b/roles/ftpsync/tasks/main.yml
index d5ff244a..2e6ca8b9 100644
--- a/roles/ftpsync/tasks/main.yml
+++ b/roles/ftpsync/tasks/main.yml
@@ -32,3 +32,16 @@
     src: update-motd.d/05-service.j2
     dest: /etc/update-motd.d/05-ftpsync
     mode: 0755
+
+- name: Copy configuration files
+  template:
+    src: "{{ item.src }}"
+    dest: "{{ item.dest }}"
+    mode: 0644
+  loop:
+    - src: html/HEADER.html.j2
+      dest: /pubftp/.html/HEADER.html
+    - src: html/FOOTER.html.j2
+      dest: /pubftp/.html/FOOTER.html
+    - src: html/style.min.css.j2
+      dest: /pubftp/.html/style.min.css
diff --git a/roles/nginx-pubftp/templates/html/FOOTER.html.j2 b/roles/ftpsync/templates/html/FOOTER.html.j2
similarity index 100%
rename from roles/nginx-pubftp/templates/html/FOOTER.html.j2
rename to roles/ftpsync/templates/html/FOOTER.html.j2
diff --git a/roles/nginx-pubftp/templates/html/HEADER.html.j2 b/roles/ftpsync/templates/html/HEADER.html.j2
similarity index 100%
rename from roles/nginx-pubftp/templates/html/HEADER.html.j2
rename to roles/ftpsync/templates/html/HEADER.html.j2
diff --git a/roles/nginx-pubftp/templates/html/style.min.css.j2 b/roles/ftpsync/templates/html/style.min.css.j2
similarity index 100%
rename from roles/nginx-pubftp/templates/html/style.min.css.j2
rename to roles/ftpsync/templates/html/style.min.css.j2
diff --git a/roles/nginx-pubftp/tasks/main.yml b/roles/nginx-pubftp/tasks/main.yml
deleted file mode 100644
index d4e0a1f9..00000000
--- a/roles/nginx-pubftp/tasks/main.yml
+++ /dev/null
@@ -1,14 +0,0 @@
----
-- name: Copy configuration files
-  template:
-    src: "{{ item.src }}"
-    dest: "{{ item.dest }}"
-  loop:
-    - src: nginx/ftp.j2
-      dest: /etc/nginx/sites-available/ftp
-    - src: html/HEADER.html.j2
-      dest: /pubftp/.html/HEADER.html
-    - src: html/FOOTER.html.j2
-      dest: /pubftp/.html/FOOTER.html
-    - src: html/style.min.css.j2
-      dest: /pubftp/.html/style.min.css
diff --git a/roles/nginx-pubftp/templates/nginx/ftp.j2 b/roles/nginx-pubftp/templates/nginx/ftp.j2
deleted file mode 100644
index 7ebf0cf2..00000000
--- a/roles/nginx-pubftp/templates/nginx/ftp.j2
+++ /dev/null
@@ -1,22 +0,0 @@
-{{ ansible_header | comment }}
-server { 
-    listen 80;
-    listen [::]:80;
-    server_name ftp ftp.* mirror mirror.* archive.ubuntu.com fr.archive.ubuntu.com security.ubuntu.com ftps ftps.*;
-
-    root /pubftp;
-    index index.html;
-
-    location  /  {
-        autoindex on;
-        autoindex_exact_size off;
-        add_before_body /.html/HEADER.html;
-        add_after_body /.html/FOOTER.html;
-    }
-
-    location /pub/events/ {
-        mp4;
-        mp4_buffer_size     1m;
-        mp4_max_buffer_size 5m;
-    }
-}
diff --git a/roles/nginx-pubftp/templates/update-motd.d/05-service.j2 b/roles/nginx-pubftp/templates/update-motd.d/05-service.j2
deleted file mode 100755
index 82373d0b..00000000
--- a/roles/nginx-pubftp/templates/update-motd.d/05-service.j2
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/tail +14
-{{ ansible_header | comment }}
-> NGINX a été déployé sur cette machine. Voir /etc/nginx/.
-- 
GitLab


From dd249f2a30eb4e6498f77fa55e3b83defcb7cf49 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 21:31:25 +0100
Subject: [PATCH 16/49] [nginx] Disable default site

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 roles/nginx/tasks/main.yml                        | 5 +++++
 roles/nginx/templates/update-motd.d/10-service.j2 | 3 +++
 2 files changed, 8 insertions(+)
 create mode 100755 roles/nginx/templates/update-motd.d/10-service.j2

diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 8d6d3823..061f1992 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -20,6 +20,11 @@
     src: letsencrypt/dhparam.j2
     dest: /etc/letsencrypt/dhparam
 
+- name: Disable default site
+  file:
+    dest: "/etc/nginx/sites-enabled/default"
+    state: absent
+
 - name: Copy reverse proxy sites
   when: nginx.reverseproxy_sites is defined or nginx.redirect_sites is defined
   template:
diff --git a/roles/nginx/templates/update-motd.d/10-service.j2 b/roles/nginx/templates/update-motd.d/10-service.j2
new file mode 100755
index 00000000..82373d0b
--- /dev/null
+++ b/roles/nginx/templates/update-motd.d/10-service.j2
@@ -0,0 +1,3 @@
+#!/usr/bin/tail +14
+{{ ansible_header | comment }}
+> NGINX a été déployé sur cette machine. Voir /etc/nginx/.
-- 
GitLab


From a16208b1c3f78e7bdb353bf59faa0cdc1921cd0f Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 2 Jan 2021 21:47:21 +0100
Subject: [PATCH 17/49] [nginx] Add template permissions

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 roles/nginx/tasks/main.yml | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 061f1992..87721eae 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -11,6 +11,9 @@
   template:
     src: "nginx/snippets/{{ item }}.j2"
     dest: "/etc/nginx/snippets/{{ item }}"
+    owner: root
+    group: root
+    mode: 0644
   loop:
     - options-ssl.conf
     - options-proxypass.conf
@@ -19,6 +22,9 @@
   template:
     src: letsencrypt/dhparam.j2
     dest: /etc/letsencrypt/dhparam
+    owner: root
+    group: root
+    mode: 0644
 
 - name: Disable default site
   file:
@@ -30,6 +36,8 @@
   template:
     src: "nginx/sites-available/{{ item }}.j2"
     dest: "/etc/nginx/sites-available/{{ item }}"
+    owner: root
+    group: root
     mode: 0644
   loop:
     - reverseproxy
@@ -42,6 +50,8 @@
   file:
     src: "/etc/nginx/sites-available/{{ item }}"
     dest: "/etc/nginx/sites-enabled/{{ item }}"
+    owner: root
+    group: root
     state: link
   loop:
     - reverseproxy
@@ -55,6 +65,8 @@
   template:
     src: "nginx/sites-available/service.j2"
     dest: "/etc/nginx/sites-available/service"
+    owner: root
+    group: root
     mode: 0644
   notify: Reload nginx
 
@@ -63,6 +75,8 @@
   file:
     src: "/etc/nginx/sites-available/service"
     dest: "/etc/nginx/sites-enabled/service"
+    owner: root
+    group: root
     state: link
   notify: Reload nginx
   ignore_errors: "{{ ansible_check_mode }}"
-- 
GitLab


From c3d58d9ca91955ecb91e5ad438d445e476500795 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Wed, 13 Jan 2021 22:13:15 +0100
Subject: [PATCH 18/49] [nginx] Fix default configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/mailman.yml                        |  1 +
 group_vars/nginx.yml                          | 23 +++++++++++--------
 host_vars/charybde.adm.crans.org.yml          |  1 +
 hosts                                         |  2 ++
 roles/nginx/tasks/main.yml                    |  8 +++----
 .../nginx/sites-available/service.j2          |  9 +++++++-
 6 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/group_vars/mailman.yml b/group_vars/mailman.yml
index cd7d754b..9be951c7 100644
--- a/group_vars/mailman.yml
+++ b/group_vars/mailman.yml
@@ -1,5 +1,6 @@
 ---
 loc_nginx:
+  service_name: mailman
   default_server: lists.crans.org
   default_ssl_server: lists.crans.org
   auth_passwd:
diff --git a/group_vars/nginx.yml b/group_vars/nginx.yml
index 1d97f621..4f8d5101 100644
--- a/group_vars/nginx.yml
+++ b/group_vars/nginx.yml
@@ -2,18 +2,23 @@
 glob_nginx:
   contact: contact@crans.org
   who: "L'équipe technique du Cr@ns"
+  service_name: service
   ssl:
     cert: /etc/letsencrypt/live/crans.org/fullchain.pem
     cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
     trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
-  default_server:
-  default_ssl_server:
   servers:
-    ssl: false
-    server_name:
-      - "default"
-      - "_"
-    root: "/var/www/html"
-    locations:
-      - filter: "/"
+    - ssl: false
+      server_name:
+        - "default"
+        - "_"
+      root: "/var/www/html"
+      locations:
+        - filter: "/"
+          params: []
   upstreams: []
+
+  auth_passwd: []
+  default_server:
+  default_ssl_server:
+  deploy_robots_file: false
diff --git a/host_vars/charybde.adm.crans.org.yml b/host_vars/charybde.adm.crans.org.yml
index 0bda434f..625d329e 100644
--- a/host_vars/charybde.adm.crans.org.yml
+++ b/host_vars/charybde.adm.crans.org.yml
@@ -35,6 +35,7 @@ to_backup:
   }
 
 loc_nginx:
+  service_name: ftp
   servers:
     server_name:
       - "ftp"
diff --git a/hosts b/hosts
index ce350a71..13bbcb8b 100644
--- a/hosts
+++ b/hosts
@@ -23,6 +23,7 @@ belenios.adm.crans.org
 [certbot:children]
 dovecot
 git
+irc
 radius  # We use certbot to manage LE certificates
 reverseproxy
 
@@ -87,6 +88,7 @@ monitoring.adm.crans.org
 charybde.adm.crans.org
 
 [nginx:children]
+irc
 mailman
 reverseproxy
 
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 87721eae..4d4179c8 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -64,17 +64,17 @@
   when: nginx.servers is defined and nginx.servers|length > 0
   template:
     src: "nginx/sites-available/service.j2"
-    dest: "/etc/nginx/sites-available/service"
+    dest: "/etc/nginx/sites-available/{{ nginx.service_name }}"
     owner: root
     group: root
     mode: 0644
   notify: Reload nginx
 
 - name: Activate local nginx service site
-  when: nginx.servers|bool
+  when: nginx.servers is defined and nginx.servers|length > 0
   file:
-    src: "/etc/nginx/sites-available/service"
-    dest: "/etc/nginx/sites-enabled/service"
+    src: "/etc/nginx/sites-available/{{ nginx.service_name }}"
+    dest: "/etc/nginx/sites-enabled/{{ nginx.service_name }}"
     owner: root
     group: root
     state: link
diff --git a/roles/nginx/templates/nginx/sites-available/service.j2 b/roles/nginx/templates/nginx/sites-available/service.j2
index 11afc9d5..bf529e50 100644
--- a/roles/nginx/templates/nginx/sites-available/service.j2
+++ b/roles/nginx/templates/nginx/sites-available/service.j2
@@ -1,5 +1,12 @@
 {{ ansible_header | comment }}
 
+# Automatic Connection header for WebSocket support
+# See http://nginx.org/en/docs/http/websocket.html
+map $http_upgrade $connection_upgrade {
+    default upgrade;
+    ''      close;
+}
+
 {% for upstream in nginx.upstreams -%}
 upstream {{ upstream.name }} {
     # Path of the server
@@ -45,7 +52,7 @@ server {
 {% endif -%}
 
 {% for server in nginx.servers %}
-{% if server.ssl -%}
+{% if server.ssl is defined and server.ssl -%}
 # Redirect HTTP to HTTPS
 server {
     listen 80 default;
-- 
GitLab


From 70a78d06d1664c94ad7eca2eadf6997f9fe27166 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 14:43:36 +0100
Subject: [PATCH 19/49] [irc] Configure nginx for the lounge

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 host_vars/irc.adm.crans.org.yml | 16 ++++++++++++++++
 plays/irc.yml                   |  9 +++++++++
 2 files changed, 25 insertions(+)
 create mode 100755 plays/irc.yml

diff --git a/host_vars/irc.adm.crans.org.yml b/host_vars/irc.adm.crans.org.yml
index 53d3a98a..39a43fd5 100644
--- a/host_vars/irc.adm.crans.org.yml
+++ b/host_vars/irc.adm.crans.org.yml
@@ -2,3 +2,19 @@
 interfaces:
   adm: ens18
   srv: ens19
+
+loc_certbot:
+  domains: "irc.crans.org"
+
+loc_nginx:
+  service_name: "thelounge"
+  servers:
+    - server_name:
+        - "irc.crans.org"
+        - "irc"
+      ssl: true
+      locations:
+        - filter: "^~ /web/"
+          params:
+            - "proxy_pass http://localhost:9000/"
+            - "include \"/etc/nginx/snippets/options-proxypass.conf\""
diff --git a/plays/irc.yml b/plays/irc.yml
new file mode 100755
index 00000000..bd4a5e99
--- /dev/null
+++ b/plays/irc.yml
@@ -0,0 +1,9 @@
+#!/usr/bin/env ansible-playbook
+---
+- hosts: irc
+  vars:
+    certbot: '{{ glob_certbot | default({}) | combine(loc_certbot | default({})) }}'
+    nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
+  roles:
+    - certbot
+    - nginx
-- 
GitLab


From e3763a712162817f9d8b04144e83d73f4c402a0d Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Wed, 13 Jan 2021 22:47:41 +0100
Subject: [PATCH 20/49] [irc] Add redirections

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 host_vars/irc.adm.crans.org.yml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/host_vars/irc.adm.crans.org.yml b/host_vars/irc.adm.crans.org.yml
index 39a43fd5..ddd54b17 100644
--- a/host_vars/irc.adm.crans.org.yml
+++ b/host_vars/irc.adm.crans.org.yml
@@ -18,3 +18,9 @@ loc_nginx:
           params:
             - "proxy_pass http://localhost:9000/"
             - "include \"/etc/nginx/snippets/options-proxypass.conf\""
+        - filter: "~ ^/$"
+          params:
+            - "redirect 302 https://irc.crans.org/web/"
+        - filter: "/"
+          params:
+            - "redirect 302 \"https://wiki.crans.org/VieCrans/UtiliserIrc#Via_l.27interface_web\""
-- 
GitLab


From f039121e212026164038f2d53060dacda75459d3 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Wed, 13 Jan 2021 23:05:46 +0100
Subject: [PATCH 21/49] [thelounge] Download the Debian package and install it,
 and deploy configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/irc.yml                     |  13 +
 host_vars/irc.adm.crans.org.yml        |   3 +
 plays/irc.yml                          |   2 +
 plays/zamok.yml                        |   1 +
 roles/thelounge/tasks/main.yml         |  28 ++
 roles/thelounge/templates/config.js.j2 | 469 +++++++++++++++++++++++++
 6 files changed, 516 insertions(+)
 create mode 100644 group_vars/irc.yml
 create mode 100644 roles/thelounge/tasks/main.yml
 create mode 100644 roles/thelounge/templates/config.js.j2

diff --git a/group_vars/irc.yml b/group_vars/irc.yml
new file mode 100644
index 00000000..d0b65df1
--- /dev/null
+++ b/group_vars/irc.yml
@@ -0,0 +1,13 @@
+glob_thelounge:
+  public: "false"
+  irc:
+    name: Crans
+    host: irc.crans.org
+    port: 6697
+    password:
+    tls: "true"
+    rejectUnauthorized: "true"
+    nick: "thelounge%%"
+    username: "thelounge"
+    realname: "The Lounge User"
+    join: "#general"
diff --git a/host_vars/irc.adm.crans.org.yml b/host_vars/irc.adm.crans.org.yml
index ddd54b17..d66d7c4e 100644
--- a/host_vars/irc.adm.crans.org.yml
+++ b/host_vars/irc.adm.crans.org.yml
@@ -24,3 +24,6 @@ loc_nginx:
         - filter: "/"
           params:
             - "redirect 302 \"https://wiki.crans.org/VieCrans/UtiliserIrc#Via_l.27interface_web\""
+
+loc_thelounge:
+  public: "true"
diff --git a/plays/irc.yml b/plays/irc.yml
index bd4a5e99..95563292 100755
--- a/plays/irc.yml
+++ b/plays/irc.yml
@@ -4,6 +4,8 @@
   vars:
     certbot: '{{ glob_certbot | default({}) | combine(loc_certbot | default({})) }}'
     nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
+    thelounge: '{{ glob_thelounge | default({}) | combine(loc_thelounge | default({})) }}'
   roles:
     - certbot
     - nginx
+    - thelounge
diff --git a/plays/zamok.yml b/plays/zamok.yml
index 4c52af09..70b16c7d 100755
--- a/plays/zamok.yml
+++ b/plays/zamok.yml
@@ -6,6 +6,7 @@
     adh: '{{ glob_adh | combine(loc_adh | default({}), recursive=True) }}'
   roles:
     - zamok-tools
+    - thelounge
     - postfix
     - prometheus-node-exporter-postfix
 
diff --git a/roles/thelounge/tasks/main.yml b/roles/thelounge/tasks/main.yml
new file mode 100644
index 00000000..e253d2db
--- /dev/null
+++ b/roles/thelounge/tasks/main.yml
@@ -0,0 +1,28 @@
+---
+- name: Install NodeJS
+  apt:
+    update_cache: true
+    install_recommends: false
+    name: nodejs
+  register: apt_result
+  retries: 3
+  until: apt_result is succeeded
+
+#- name: Download The Lounge packet
+#  get_url:
+#    url: https://github.com/thelounge/thelounge/releases/download/v4.2.0/thelounge_4.2.0_all.deb
+#    dest: /var/cache/apt/archives/thelounge_4.2.0_all.deb
+#    checksum: sha256:cc8b01c3818b636ff9bd034db09826ce19c02af5b8a459209ea817cd2868f323
+#    mode: 0644
+
+#- name: Install The Lounge from the deb package
+#  apt:
+#    deb: /var/cache/apt/archives/thelounge_4.2.0_all.deb
+
+- name: Deploy The Lounge configuration
+  template:
+    src: config.js.j2
+    dest: /etc/thelounge/config.js
+    owner: thelounge
+    group: thelounge
+    mode: 0660
diff --git a/roles/thelounge/templates/config.js.j2 b/roles/thelounge/templates/config.js.j2
new file mode 100644
index 00000000..41f1474c
--- /dev/null
+++ b/roles/thelounge/templates/config.js.j2
@@ -0,0 +1,469 @@
+"use strict";
+
+module.exports = {
+	// ## Server settings
+
+	// ### `public`
+	//
+	// When set to `true`, The Lounge starts in public mode. When set to `false`,
+	// it starts in private mode.
+	//
+	// - A **public server** does not require authentication. Anyone can connect
+	//   to IRC networks in this mode. All IRC connections and channel
+	//   scrollbacks are lost when a user leaves the client.
+	// - A **private server** requires users to log in. Their IRC connections are
+	//   kept even when they are not using or logged in to the client. All joined
+	//   channels and scrollbacks are available when they come back.
+	//
+	// This value is set to `false` by default.
+	public: {{ thelounge.public }},
+
+	// ### `host`
+	//
+	// IP address or hostname for the web server to listen to. For example, set it
+	// to `"127.0.0.1"` to accept connections from localhost only.
+	//
+	// For UNIX domain sockets, use `"unix:/absolute/path/to/file.sock"`.
+	//
+	// This value is set to `undefined` by default to listen on all interfaces.
+	host: undefined,
+
+	// ### `port`
+	//
+	// Set the port to listen to.
+	//
+	// This value is set to `9000` by default.
+	port: 9000,
+
+	// ### `bind`
+	//
+	// Set the local IP to bind to for outgoing connections.
+	//
+	// This value is set to `undefined` by default to let the operating system
+	// pick its preferred one.
+	bind: undefined,
+
+	// ### `reverseProxy`
+	//
+	// When set to `true`, The Lounge is marked as served behind a reverse proxy
+	// and will honor the `X-Forwarded-For` header.
+	//
+	// This value is set to `false` by default.
+	reverseProxy: false,
+
+	// ### `maxHistory`
+	//
+	// Defines the maximum number of history lines that will be kept in memory per
+	// channel/query, in order to reduce the memory usage of the server. Setting
+	// this to `-1` will keep unlimited amount.
+	//
+	// This value is set to `10000` by default.
+	maxHistory: 10000,
+
+	// ### `https`
+	//
+	// These settings are used to run The Lounge's web server using encrypted TLS.
+	//
+	// If you want more control over the webserver,
+	// [use a reverse proxy instead](https://thelounge.chat/docs/guides/reverse-proxies).
+	//
+	// The available keys for the `https` object are:
+	//
+	// - `enable`: when set to `false`, HTTPS support is disabled
+	//    and all other values are ignored.
+	// - `key`: Path to the private key file.
+	// - `certificate`: Path to the certificate.
+	// - `ca`: Path to the CA bundle.
+	//
+	// The value of `enable` is set to `false` to disable HTTPS by default, in
+	// which case the other two string settings are ignored.
+	https: {
+		enable: false,
+		key: "",
+		certificate: "",
+		ca: "",
+	},
+
+	// ## Client settings
+
+	// ### `theme`
+	//
+	// Set the default theme to serve to new users. They will be able to select a
+	// different one in their client settings among those available.
+	//
+	// The Lounge ships with two themes (`default` and `morning`) and can be
+	// extended by installing more themes. Read more about how to manage them
+	// [here](https://thelounge.chat/docs/guides/theme-creation).
+	//
+	// This value needs to be the package name and not the display name. For
+	// example, the value for Morning would be `morning`, and the value for
+	// Solarized would be `thelounge-theme-solarized`.
+	//
+	// This value is set to `"default"` by default.
+	theme: "default",
+
+	// ### `prefetch`
+	//
+	// When set to `true`, The Lounge will load thumbnails and site descriptions
+	// from URLs posted in channels and private messages.
+	//
+	// This value is set to `false` by default.
+	prefetch: false,
+
+	// ### `disableMediaPreview`
+	//
+	// When set to `true`, The Lounge will not preview media (images, video and
+	// audio) hosted on third-party sites. This ensures the client does not
+	// make any requests to external sites. If `prefetchStorage` is enabled,
+	// images proxied via the The Lounge will be previewed.
+	//
+	// This has no effect if `prefetch` is set to `false`.
+	//
+	// This value is set to `false` by default.
+	disableMediaPreview: false,
+
+	// ### `prefetchStorage`
+
+	// When set to `true`, The Lounge will store and proxy prefetched images and
+	// thumbnails on the filesystem rather than directly display the content at
+	// the original URLs.
+	//
+	// This option primarily exists to resolve mixed content warnings by not
+	// loading images from http hosts. This option does not work for video
+	// or audio as The Lounge will only load these from https hosts.
+	//
+	// If storage is enabled, The Lounge will fetch and store images and thumbnails
+	// in the `${THELOUNGE_HOME}/storage` folder.
+	//
+	// Images are deleted when they are no longer referenced by any message
+	// (controlled by `maxHistory`), and the folder is cleaned up when The Lounge
+	// restarts.
+	//
+	// This value is set to `false` by default.
+	prefetchStorage: false,
+
+	// ### `prefetchMaxImageSize`
+	//
+	// When `prefetch` is enabled, images will only be displayed if their file
+	// size does not exceed this limit.
+	//
+	// This value is set to `2048` kilobytes by default.
+	prefetchMaxImageSize: 2048,
+
+	// ### `fileUpload`
+	//
+	// Allow uploading files to the server hosting The Lounge.
+	//
+	// Files are stored in the `${THELOUNGE_HOME}/uploads` folder, do not expire,
+	// and are not removed by The Lounge. This may cause issues depending on your
+	// hardware, for example in terms of disk usage.
+	//
+	// The available keys for the `fileUpload` object are:
+	//
+	// - `enable`: When set to `true`, files can be uploaded on the client with a
+	//   drag-and-drop or using the upload dialog.
+	// - `maxFileSize`: When file upload is enabled, users sending files above
+	//   this limit will be prompted with an error message in their browser. A value of
+	//   `-1` disables the file size limit and allows files of any size. **Use at
+	//   your own risk.** This value is set to `10240` kilobytes by default.
+	// - `baseUrl`: If you want change the URL where uploaded files are accessed,
+	//   you can set this option to `"https://example.com/folder/"` and the final URL
+	//   would look like `"https://example.com/folder/aabbccddeeff1234/name.png"`.
+	//   If you use this option, you must have a reverse proxy configured,
+	//   to correctly proxy the uploads URLs back to The Lounge.
+	//   This value is set to `null` by default.
+	fileUpload: {
+		enable: false,
+		maxFileSize: 10240,
+		baseUrl: null,
+	},
+
+	// ### `transports`
+	//
+	// Set `socket.io` transports.
+	//
+	// This value is set to `["polling", "websocket"]` by default.
+	transports: ["polling", "websocket"],
+
+	// ### `leaveMessage`
+	//
+	// Set users' default `quit` and `part` messages if they are not providing
+	// one.
+	//
+	// This value is set to `"The Lounge - https://thelounge.chat"` by
+	// default.
+	leaveMessage: "The Lounge - https://thelounge.chat",
+
+	// ## Default network
+
+	// ### `defaults`
+	//
+	// Specifies default network information that will be used as placeholder
+	// values in the *Connect* window.
+	//
+	// The available keys for the `defaults` object are:
+	//
+	// - `name`: Name to display in the channel list of The Lounge. This value is
+	//   not forwarded to the IRC network.
+	// - `host`: IP address or hostname of the IRC server.
+	// - `port`: Usually 6667 for unencrypted connections and 6697 for
+	//   connections encrypted with TLS.
+	// - `password`: Connection password. If the server supports SASL capability,
+	//   then this password will be used in SASL authentication.
+	// - `tls`: Enable TLS connections
+	// - `rejectUnauthorized`: Whether the server certificate should be verified
+	//   against the list of supplied Certificate Authorities (CAs) by your
+	//   Node.js installation.
+	// - `nick`: Nick name. Percent signs (`%`) will be replaced by random
+	//   numbers from 0 to 9. For example, `Guest%%%` may become `Guest123`.
+	// - `username`: User name.
+	// - `realname`: Real name.
+	// - `join`: Comma-separated list of channels to auto-join once connected.
+	//
+	// This value is set to connect to the official channel of The Lounge on
+	// Freenode by default:
+	//
+	// ```js
+	// defaults: {
+	//   name: "Freenode",
+	//   host: "chat.freenode.net",
+	//   port: 6697,
+	//   password: "",
+	//   tls: true,
+	//   rejectUnauthorized: true,
+	//   nick: "thelounge%%",
+	//   username: "thelounge",
+	//   realname: "The Lounge User",
+	//   join: "#thelounge"
+	// }
+	// ```
+	defaults: {
+		name: "{{ thelounge.irc.name }}",
+		host: "{{ thelounge.irc.host }}",
+		port: {{ thelounge.irc.port }},
+		password: "{{ thelounge.irc.password }}",
+		tls: {{ thelounge.irc.tls }},
+		rejectUnauthorized: {{ thelounge.irc.rejectUnauthorized }},
+		nick: "{{ thelounge.irc.nick }}",
+		username: "{{ thelounge.irc.username }}",
+		realname: "{{ thelounge.irc.realname }}",
+		join: "{{ thelounge.irc.join }}",
+	},
+
+	// ### `lockNetwork`
+	//
+	// When set to `true`, users will not be able to modify host, port and TLS
+	// settings and will be limited to the configured network.
+	// These fields will also be hidden from the UI.
+	//
+	// This value is set to `false` by default.
+	lockNetwork: false,
+
+	// ## User management
+
+	// ### `messageStorage`
+
+	// The Lounge can log user messages, for example to access them later or to
+	// reload messages on server restart.
+
+	// Set this array with one or multiple values to enable logging:
+	// - `text`: Messages per network and channel will be stored as text files.
+	//   **Messages will not be reloaded on restart.**
+	// - `sqlite`: Messages are stored in SQLite database files, one per user.
+	//
+	// Logging can be disabled globally by setting this value to an empty array
+	// `[]`. Logging is also controlled per user individually in the `log` key of
+	// their JSON configuration file.
+	//
+	// This value is set to `["sqlite", "text"]` by default.
+	messageStorage: ["sqlite", "text"],
+
+	// ### `useHexIp`
+	//
+	// When set to `true`, users' IP addresses will be encoded as hex.
+	//
+	// This is done to share the real user IP address with the server for host
+	// masking purposes. This is encoded in the `username` field and only supports
+	// IPv4.
+	//
+	// This value is set to `false` by default.
+	useHexIp: false,
+
+	// ## WEBIRC support
+	//
+	// When enabled, The Lounge will pass the connecting user's host and IP to the
+	// IRC server. Note that this requires to obtain a password from the IRC
+	// network that The Lounge will be connecting to and generally involves a lot
+	// of trust from the network you are connecting to.
+	//
+	// There are 2 ways to configure the `webirc` setting:
+	//
+	// - **Basic**: an object where keys are IRC hosts and values are passwords.
+	//   For example:
+	//
+	//   ```json
+	//   webirc: {
+	//     "irc.example.net": "thisiswebircpassword1",
+	//     "irc.example.org": "thisiswebircpassword2",
+	//   },
+	//   ```
+	//
+	// - **Advanced**: an object where keys are IRC hosts and values are functions
+	//   that take two arguments (`webircObj`, `network`) and return an
+	//   object to be directly passed to `irc-framework`. `webircObj` contains the
+	//   generated object which you can modify. For example:
+	//
+	//   ```js
+	//   webirc: {
+	//     "irc.example.com": (webircObj, network) => {
+	//       webircObj.password = "thisiswebircpassword";
+	//       webircObj.hostname = `webirc/${webircObj.hostname}`;
+	//       return webircObj;
+	//     },
+	//   },
+	//   ```
+	//
+	// This value is set to `null` to disable WEBIRC by default.
+	webirc: null,
+
+	// ## identd and oidentd support
+
+	// ### `identd`
+	//
+	// Run The Lounge with `identd` support.
+	//
+	// The available keys for the `identd` object are:
+	//
+	// - `enable`: When `true`, the identd daemon runs on server start.
+	// - `port`: Port to listen for ident requests.
+	//
+	// The value of `enable` is set to `false` to disable `identd` support by
+	// default, in which case the value of `port` is ignored. The default value of
+	// `port` is 113.
+	identd: {
+		enable: false,
+		port: 113,
+	},
+
+	// ### `oidentd`
+	//
+	// When this setting is a string, this enables `oidentd` support using the
+	// configuration file located at the given path.
+	//
+	// This is set to `null` by default to disable `oidentd` support.
+	oidentd: null,
+
+	// ## LDAP support
+
+	// These settings enable and configure LDAP authentication.
+	//
+	// They are only being used in private mode. To know more about private mode,
+	// see the `public` setting above.
+
+	//
+	// The authentication process works as follows:
+	//
+	// 1. The Lounge connects to the LDAP server with its system credentials.
+	// 2. It performs an LDAP search query to find the full DN associated to the
+	//    user requesting to log in.
+	// 3. The Lounge tries to connect a second time, but this time using the
+	//    user's DN and password. Authentication is validated if and only if this
+	//    connection is successful.
+	//
+	// The search query takes a couple of parameters in `searchDN`:
+	//
+	// - a base DN `searchDN/base`. Only children nodes of this DN will be likely
+	//   be returned;
+	// - a search scope `searchDN/scope` (see LDAP documentation);
+	// - the query itself, built as `(&(<primaryKey>=<username>) <filter>)`
+	//   where `<username>` is the user name provided in the log in request,
+	//   `<primaryKey>` is provided by the config and `<filter>` is a filtering
+	//   complement also given in the config, to filter for instance only for
+	//   nodes of type `inetOrgPerson`, or whatever LDAP search allows.
+	//
+	// Alternatively, you can specify the `bindDN` parameter. This will make The
+	// Lounge ignore `searchDN` options and assume that the user DN is always
+	// `<bindDN>,<primaryKey>=<username>`, where `<username>` is the user name
+	// provided in the log in request, and `<bindDN>` and `<primaryKey>` are
+	// provided by the configuration.
+	//
+	// The available keys for the `ldap` object are:
+	ldap: {
+		// - `enable`: when set to `false`, LDAP support is disabled and all other
+		//   values are ignored.
+		enable: false,
+
+		// - `url`: A url of the form `ldaps://<ip>:<port>`.
+		//   For plain connections, use the `ldap` scheme.
+		url: "ldaps://example.com",
+
+		// - `tlsOptions`: LDAP connection TLS options (only used if scheme is
+		//   `ldaps://`). It is an object whose values are Node.js' `tls.connect()`
+		//   options. It is set to `{}` by default.
+		//   For example, this option can be used in order to force the use of IPv6:
+		//   ```js
+		//   {
+		//     host: 'my::ip::v6',
+		//     servername: 'example.com'
+		//   }
+		//   ```
+		tlsOptions: {},
+
+		// - `primaryKey`: LDAP primary key. It is set to `"uid"` by default.
+		primaryKey: "uid",
+
+		// - `baseDN`: LDAP base DN, alternative to `searchDN`. For example, set it
+		//   to `"ou=accounts,dc=example,dc=com"`.
+		//   When unset, the LDAP auth logic with use `searchDN` instead to locate users.
+
+		// - `searchDN`: LDAP search DN settings. This defines the procedure by
+		//   which The Lounge first looks for the user DN before authenticating them.
+		//   It is ignored if `baseDN` is specified. It is an object with the
+		//   following keys:
+		searchDN: {
+			//   - `rootDN`: This bind DN is used to query the server for the DN of
+			//     the user. This is supposed to be a system user that has access in
+			//     read-only to the DNs of the people that are allowed to log in.
+			//     It is set to `"cn=thelounge,ou=system-users,dc=example,dc=com"` by
+			//     default.
+			rootDN: "cn=thelounge,ou=system-users,dc=example,dc=com",
+
+			//   - `rootPassword`: Password of The Lounge LDAP system user.
+			rootPassword: "1234",
+
+			//   - `ldapFilter`: it is set to `"(objectClass=person)(memberOf=ou=accounts,dc=example,dc=com)"`
+			//     by default.
+			filter: "(objectClass=person)(memberOf=ou=accounts,dc=example,dc=com)",
+
+			//   - `base`: LDAP search base (search only within this node). It is set
+			//     to `"dc=example,dc=com"` by default.
+			base: "dc=example,dc=com",
+
+			//   - `scope`: LDAP search scope. It is set to `"sub"` by default.
+			scope: "sub",
+		},
+	},
+
+	// ## Debugging settings
+
+	// The `debug` object contains several settings to enable debugging in The
+	// Lounge. Use them to learn more about an issue you are noticing but be aware
+	// this may produce more logging or may affect connection performance so it is
+	// not recommended to use them by default.
+	//
+	// All values in the `debug` object are set to `false`.
+	debug: {
+		// ### `debug.ircFramework`
+		//
+		// When set to true, this enables extra debugging output provided by
+		// [`irc-framework`](https://github.com/kiwiirc/irc-framework), the
+		// underlying IRC library for Node.js used by The Lounge.
+		ircFramework: false,
+
+		// ### `debug.raw`
+		//
+		// When set to `true`, this enables logging of raw IRC messages into each
+		// server window, displayed on the client.
+		raw: false,
+	},
+};
-- 
GitLab


From 3f4a66eb7c6d365ca65303c459a95da3b6df5a80 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 14:44:04 +0100
Subject: [PATCH 22/49] [thelounge] Copy ldap configuration for zamok

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/irc.yml                     |  9 +++++++++
 host_vars/zamok.adm.crans.org.yml      |  3 +++
 plays/zamok.yml                        |  1 -
 roles/thelounge/templates/config.js.j2 | 16 ++++++++--------
 4 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/group_vars/irc.yml b/group_vars/irc.yml
index d0b65df1..cdfc1ffb 100644
--- a/group_vars/irc.yml
+++ b/group_vars/irc.yml
@@ -11,3 +11,12 @@ glob_thelounge:
     username: "thelounge"
     realname: "The Lounge User"
     join: "#general"
+  ldap_enable: "false"
+  ldap:
+    url: "ldap://172.16.10.157"
+    primaryKey: "cn"
+    rootDN: "cn=thelounge,ou=service-users,dc=crans,dc=org"
+    rootPassword: "{{ vault_ldap_thelounge_password }}"
+    filter: "(objectclass=inetOrgPerson)"
+    base: "dc=crans,dc=org"
+    scope: "sub"
diff --git a/host_vars/zamok.adm.crans.org.yml b/host_vars/zamok.adm.crans.org.yml
index 4b02629e..34e0ed9b 100644
--- a/host_vars/zamok.adm.crans.org.yml
+++ b/host_vars/zamok.adm.crans.org.yml
@@ -8,3 +8,6 @@ loc_borg:
       params:
         - "- name: all"
         - "  password: {{ vault.mysql_zamok_password }}"
+
+loc_thelounge:
+  ldap_enable: "true"
diff --git a/plays/zamok.yml b/plays/zamok.yml
index 70b16c7d..4c52af09 100755
--- a/plays/zamok.yml
+++ b/plays/zamok.yml
@@ -6,7 +6,6 @@
     adh: '{{ glob_adh | combine(loc_adh | default({}), recursive=True) }}'
   roles:
     - zamok-tools
-    - thelounge
     - postfix
     - prometheus-node-exporter-postfix
 
diff --git a/roles/thelounge/templates/config.js.j2 b/roles/thelounge/templates/config.js.j2
index 41f1474c..e7d43fcf 100644
--- a/roles/thelounge/templates/config.js.j2
+++ b/roles/thelounge/templates/config.js.j2
@@ -391,11 +391,11 @@ module.exports = {
 	ldap: {
 		// - `enable`: when set to `false`, LDAP support is disabled and all other
 		//   values are ignored.
-		enable: false,
+		enable: {{ thelounge.ldap_enable }},
 
 		// - `url`: A url of the form `ldaps://<ip>:<port>`.
 		//   For plain connections, use the `ldap` scheme.
-		url: "ldaps://example.com",
+		url: "{{ thelounge.ldap.url }}",
 
 		// - `tlsOptions`: LDAP connection TLS options (only used if scheme is
 		//   `ldaps://`). It is an object whose values are Node.js' `tls.connect()`
@@ -410,7 +410,7 @@ module.exports = {
 		tlsOptions: {},
 
 		// - `primaryKey`: LDAP primary key. It is set to `"uid"` by default.
-		primaryKey: "uid",
+		primaryKey: "{{ thelounge.ldap.primaryKey }}",
 
 		// - `baseDN`: LDAP base DN, alternative to `searchDN`. For example, set it
 		//   to `"ou=accounts,dc=example,dc=com"`.
@@ -426,21 +426,21 @@ module.exports = {
 			//     read-only to the DNs of the people that are allowed to log in.
 			//     It is set to `"cn=thelounge,ou=system-users,dc=example,dc=com"` by
 			//     default.
-			rootDN: "cn=thelounge,ou=system-users,dc=example,dc=com",
+			rootDN: "{{ thelounge.ldap.rootDN }}",
 
 			//   - `rootPassword`: Password of The Lounge LDAP system user.
-			rootPassword: "1234",
+			rootPassword: "{{ thelounge.ldap.rootPassword }}",
 
 			//   - `ldapFilter`: it is set to `"(objectClass=person)(memberOf=ou=accounts,dc=example,dc=com)"`
 			//     by default.
-			filter: "(objectClass=person)(memberOf=ou=accounts,dc=example,dc=com)",
+			filter: "{{ thelounge.ldap.filter }}",
 
 			//   - `base`: LDAP search base (search only within this node). It is set
 			//     to `"dc=example,dc=com"` by default.
-			base: "dc=example,dc=com",
+			base: "{{ thelounge.ldap.base }}",
 
 			//   - `scope`: LDAP search scope. It is set to `"sub"` by default.
-			scope: "sub",
+			scope: "{{ thelounge.ldap.scope }}",
 		},
 	},
 
-- 
GitLab


From 334b4ace023f6a2dcb1194f419897fc5be6c31fb Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Thu, 14 Jan 2021 00:28:37 +0100
Subject: [PATCH 23/49] [thelounge] Uncomment the download of the Debian
 package, but for now the package must be manually downloaded because of
 redirection issues

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 roles/thelounge/tasks/main.yml | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/roles/thelounge/tasks/main.yml b/roles/thelounge/tasks/main.yml
index e253d2db..612b375d 100644
--- a/roles/thelounge/tasks/main.yml
+++ b/roles/thelounge/tasks/main.yml
@@ -8,16 +8,16 @@
   retries: 3
   until: apt_result is succeeded
 
-#- name: Download The Lounge packet
-#  get_url:
-#    url: https://github.com/thelounge/thelounge/releases/download/v4.2.0/thelounge_4.2.0_all.deb
-#    dest: /var/cache/apt/archives/thelounge_4.2.0_all.deb
-#    checksum: sha256:cc8b01c3818b636ff9bd034db09826ce19c02af5b8a459209ea817cd2868f323
-#    mode: 0644
+- name: Download The Lounge packet
+  get_url:
+    url: https://github.com/thelounge/thelounge/releases/download/v4.2.0/thelounge_4.2.0_all.deb
+    dest: /var/cache/apt/archives/thelounge_4.2.0_all.deb
+    checksum: sha256:cc8b01c3818b636ff9bd034db09826ce19c02af5b8a459209ea817cd2868f323
+    mode: 0644
 
-#- name: Install The Lounge from the deb package
-#  apt:
-#    deb: /var/cache/apt/archives/thelounge_4.2.0_all.deb
+- name: Install The Lounge from the deb package
+  apt:
+    deb: /var/cache/apt/archives/thelounge_4.2.0_all.deb
 
 - name: Deploy The Lounge configuration
   template:
-- 
GitLab


From 2360b992c496f7abd884a3c12da0acc57aeb35ee Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Thu, 14 Jan 2021 00:29:35 +0100
Subject: [PATCH 24/49] Restart the lounge at the end of the playbook

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 roles/thelounge/tasks/main.yml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/roles/thelounge/tasks/main.yml b/roles/thelounge/tasks/main.yml
index 612b375d..4eaa9423 100644
--- a/roles/thelounge/tasks/main.yml
+++ b/roles/thelounge/tasks/main.yml
@@ -26,3 +26,8 @@
     owner: thelounge
     group: thelounge
     mode: 0660
+
+- name: Restart The Lounge
+  systemd:
+    name: thelounge
+    state: restarted
-- 
GitLab


From af33ff7d5634da0fa96ac5473900bbd18fe3dece Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Thu, 14 Jan 2021 00:34:52 +0100
Subject: [PATCH 25/49] [thelounge] Temporary not download automatically the
 thelounge packet

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 roles/thelounge/tasks/main.yml | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/roles/thelounge/tasks/main.yml b/roles/thelounge/tasks/main.yml
index 4eaa9423..2d4053e9 100644
--- a/roles/thelounge/tasks/main.yml
+++ b/roles/thelounge/tasks/main.yml
@@ -8,12 +8,12 @@
   retries: 3
   until: apt_result is succeeded
 
-- name: Download The Lounge packet
-  get_url:
-    url: https://github.com/thelounge/thelounge/releases/download/v4.2.0/thelounge_4.2.0_all.deb
-    dest: /var/cache/apt/archives/thelounge_4.2.0_all.deb
-    checksum: sha256:cc8b01c3818b636ff9bd034db09826ce19c02af5b8a459209ea817cd2868f323
-    mode: 0644
+#- name: Download The Lounge packet
+#  get_url:
+#    url: https://github.com/thelounge/thelounge/releases/download/v4.2.0/thelounge_4.2.0_all.deb
+#    dest: /var/cache/apt/archives/thelounge_4.2.0_all.deb
+#    checksum: sha256:6096f992526f7ce01ea7c5aa1fb12bb013ce872f3c67f1fe11cd44f85a3fe405
+#    mode: 0644
 
 - name: Install The Lounge from the deb package
   apt:
-- 
GitLab


From 9d5a080fc5e67835c1e94bd6eeaa51ef8e4fb015 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Thu, 14 Jan 2021 12:08:02 +0100
Subject: [PATCH 26/49] [thelounge] Support zamok configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/irc.yml                     |  3 +++
 host_vars/zamok.adm.crans.org.yml      |  3 +++
 hosts                                  |  1 +
 roles/thelounge/templates/config.js.j2 | 10 +++++-----
 4 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/group_vars/irc.yml b/group_vars/irc.yml
index cdfc1ffb..e55af642 100644
--- a/group_vars/irc.yml
+++ b/group_vars/irc.yml
@@ -1,5 +1,8 @@
 glob_thelounge:
   public: "false"
+  host: "undefined"
+  reverseProxy: "false"
+  oidentd: "null"
   irc:
     name: Crans
     host: irc.crans.org
diff --git a/host_vars/zamok.adm.crans.org.yml b/host_vars/zamok.adm.crans.org.yml
index 34e0ed9b..8949c4df 100644
--- a/host_vars/zamok.adm.crans.org.yml
+++ b/host_vars/zamok.adm.crans.org.yml
@@ -10,4 +10,7 @@ loc_borg:
         - "  password: {{ vault.mysql_zamok_password }}"
 
 loc_thelounge:
+  host: "\"172.16.10.31\""
+  oidentd: "\"/usr/local/lib/thelounge/.oidentd.conf\""
+  reverseProxy: "true"
   ldap_enable: "true"
diff --git a/hosts b/hosts
index 13bbcb8b..d7bde7ff 100644
--- a/hosts
+++ b/hosts
@@ -64,6 +64,7 @@ horde.adm.crans.org
 
 [irc]
 irc.adm.crans.org
+zamok.adm.crans.org
 
 [keepalived:children]
 routeurs_vm
diff --git a/roles/thelounge/templates/config.js.j2 b/roles/thelounge/templates/config.js.j2
index e7d43fcf..3dd10515 100644
--- a/roles/thelounge/templates/config.js.j2
+++ b/roles/thelounge/templates/config.js.j2
@@ -26,7 +26,7 @@ module.exports = {
 	// For UNIX domain sockets, use `"unix:/absolute/path/to/file.sock"`.
 	//
 	// This value is set to `undefined` by default to listen on all interfaces.
-	host: undefined,
+	host: {{ thelounge.host }},
 
 	// ### `port`
 	//
@@ -49,7 +49,7 @@ module.exports = {
 	// and will honor the `X-Forwarded-For` header.
 	//
 	// This value is set to `false` by default.
-	reverseProxy: false,
+	reverseProxy: {{ thelounge.reverseProxy }},
 
 	// ### `maxHistory`
 	//
@@ -108,7 +108,7 @@ module.exports = {
 	// from URLs posted in channels and private messages.
 	//
 	// This value is set to `false` by default.
-	prefetch: false,
+	prefetch: true,
 
 	// ### `disableMediaPreview`
 	//
@@ -140,7 +140,7 @@ module.exports = {
 	// restarts.
 	//
 	// This value is set to `false` by default.
-	prefetchStorage: false,
+	prefetchStorage: true,
 
 	// ### `prefetchMaxImageSize`
 	//
@@ -351,7 +351,7 @@ module.exports = {
 	// configuration file located at the given path.
 	//
 	// This is set to `null` by default to disable `oidentd` support.
-	oidentd: null,
+	oidentd: {{ thelounge.oidentd }},
 
 	// ## LDAP support
 
-- 
GitLab


From 7c1abf3aee5109bfbcf01774b9593de6515b34c5 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Wed, 27 Jan 2021 15:00:28 +0100
Subject: [PATCH 27/49] [thelounge/nginx] Return is not redirect

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 host_vars/irc.adm.crans.org.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/host_vars/irc.adm.crans.org.yml b/host_vars/irc.adm.crans.org.yml
index d66d7c4e..c825629f 100644
--- a/host_vars/irc.adm.crans.org.yml
+++ b/host_vars/irc.adm.crans.org.yml
@@ -20,10 +20,10 @@ loc_nginx:
             - "include \"/etc/nginx/snippets/options-proxypass.conf\""
         - filter: "~ ^/$"
           params:
-            - "redirect 302 https://irc.crans.org/web/"
+            - "return 302 https://irc.crans.org/web/"
         - filter: "/"
           params:
-            - "redirect 302 \"https://wiki.crans.org/VieCrans/UtiliserIrc#Via_l.27interface_web\""
+            - "return 302 \"https://wiki.crans.org/VieCrans/UtiliserIrc#Via_l.27interface_web\""
 
 loc_thelounge:
   public: "true"
-- 
GitLab


From 96d5f945e3cce71c475acb2c6c7151b7707f2afe Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 1 Feb 2021 21:19:23 +0100
Subject: [PATCH 28/49] [nginx] Update configuration for default servers

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 .../nginx/templates/nginx/sites-available/service.j2 | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/roles/nginx/templates/nginx/sites-available/service.j2 b/roles/nginx/templates/nginx/sites-available/service.j2
index bf529e50..5a883a48 100644
--- a/roles/nginx/templates/nginx/sites-available/service.j2
+++ b/roles/nginx/templates/nginx/sites-available/service.j2
@@ -21,7 +21,7 @@ server {
     listen [::]:443 default_server ssl;
     include "/etc/nginx/snippets/options-ssl.conf";
 
-    server_name {{ nginx.default_ssl_server }};
+    server_name _;
     charset utf-8;
 
     # Hide Nginx version
@@ -39,7 +39,7 @@ server {
     listen 80 default_server;
     listen [::]:80 default_server;
 
-    server_name {{ nginx.default_server }};
+    server_name _;
     charset utf-8;
 
     # Hide Nginx version
@@ -55,8 +55,8 @@ server {
 {% if server.ssl is defined and server.ssl -%}
 # Redirect HTTP to HTTPS
 server {
-    listen 80 default;
-    listen [::]:80 default;
+    listen 80;
+    listen [::]:80;
 
     server_name {{ server.server_name|join(" ") }};
     charset utf-8;
@@ -72,8 +72,8 @@ server {
 
 server {
     {% if server.ssl is defined and server.ssl -%}
-    listen 443 default_server ssl;
-    listen [::]:443 default_server ssl;
+    listen 443 ssl;
+    listen [::]:443 ssl;
     include "/etc/nginx/snippets/options-ssl.conf";
     {% else -%}
     listen 80 default;
-- 
GitLab


From 72238d79ede38fd43d55f7777d5d6e5c205e6dec Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Thu, 18 Feb 2021 15:49:10 +0100
Subject: [PATCH 29/49] [nginx] Add feature to manage multiple certificates,
 for example for crans.org and for adm.crans.org

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/mailman.yml                        |  2 +-
 group_vars/nginx.yml                          | 12 +++--
 host_vars/charybde.adm.crans.org.yml          | 46 +++++++++----------
 host_vars/irc.adm.crans.org.yml               | 10 +++-
 plays/irc.yml                                 |  2 +-
 plays/mailman.yml                             |  4 ++
 roles/nginx/tasks/main.yml                    | 30 +++++++-----
 .../nginx/sites-available/service.j2          | 42 +++++++----------
 .../nginx/snippets/options-ssl.conf.j2        |  6 +--
 .../templates/update-motd.d/10-service.j2     |  3 --
 10 files changed, 83 insertions(+), 74 deletions(-)
 delete mode 100755 roles/nginx/templates/update-motd.d/10-service.j2

diff --git a/group_vars/mailman.yml b/group_vars/mailman.yml
index 9be951c7..115215fa 100644
--- a/group_vars/mailman.yml
+++ b/group_vars/mailman.yml
@@ -9,7 +9,7 @@ loc_nginx:
   servers:
     - server_name:
       - lists.crans.org
-      ssl: true
+      ssl: crans.org
       root: "/usr/lib/cgi-bin/mailman/"
       index:
         - index.htm
diff --git a/group_vars/nginx.yml b/group_vars/nginx.yml
index 4f8d5101..76e216b5 100644
--- a/group_vars/nginx.yml
+++ b/group_vars/nginx.yml
@@ -4,11 +4,14 @@ glob_nginx:
   who: "L'équipe technique du Cr@ns"
   service_name: service
   ssl:
-    cert: /etc/letsencrypt/live/crans.org/fullchain.pem
-    cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
-    trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
+    # Add adm.crans.org if necessary
+    - name: crans.org
+      cert: /etc/letsencrypt/live/crans.org/fullchain.pem
+      cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
+      trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
   servers:
-    - ssl: false
+    - ssl: false  # Replace by crans.org or adm.crans.org
+      default: true
       server_name:
         - "default"
         - "_"
@@ -21,4 +24,5 @@ glob_nginx:
   auth_passwd: []
   default_server:
   default_ssl_server:
+  default_ssl_domain: crans.org
   deploy_robots_file: false
diff --git a/host_vars/charybde.adm.crans.org.yml b/host_vars/charybde.adm.crans.org.yml
index 625d329e..fd0885f9 100644
--- a/host_vars/charybde.adm.crans.org.yml
+++ b/host_vars/charybde.adm.crans.org.yml
@@ -37,26 +37,26 @@ to_backup:
 loc_nginx:
   service_name: ftp
   servers:
-    server_name:
-      - "ftp"
-      - "ftp.*"
-      - "mirror"
-      - "mirror.*"
-      - "archive.ubuntu.com"
-      - "fr.archive.ubuntu.com"
-      - "security.ubuntu.com"
-      - "ftps"
-      - "ftps.*"
-    root: "/pubftp"
-    locations:
-      - filter: "/"
-      - params:
-        - "autoindex on"
-        - "autoindex_exact_size off"
-        - "add_before_body /.html/HEADER.html"
-        - "add_after_body /.html/FOOTER.html"
-      - filter: "/pub/events/"
-        params:
-          - "mp4"
-          - "mp4_buffer_size 1m"
-          - "mp4_max_buffer_size 5m"
+    - server_name:
+        - "ftp"
+        - "ftp.*"
+        - "mirror"
+        - "mirror.*"
+        - "archive.ubuntu.com"
+        - "fr.archive.ubuntu.com"
+        - "security.ubuntu.com"
+        - "ftps"
+        - "ftps.*"
+      root: "/pubftp"
+      locations:
+        - filter: "/"
+          params:
+          - "autoindex on"
+          - "autoindex_exact_size off"
+          - "add_before_body /.html/HEADER.html"
+          - "add_after_body /.html/FOOTER.html"
+        - filter: "/pub/events/"
+          params:
+            - "mp4"
+            - "mp4_buffer_size 1m"
+            - "mp4_max_buffer_size 5m"
diff --git a/host_vars/irc.adm.crans.org.yml b/host_vars/irc.adm.crans.org.yml
index c825629f..bf956da8 100644
--- a/host_vars/irc.adm.crans.org.yml
+++ b/host_vars/irc.adm.crans.org.yml
@@ -4,7 +4,12 @@ interfaces:
   srv: ens19
 
 loc_certbot:
-  domains: "irc.crans.org"
+  - dns_rfc2136_server: '172.16.10.147'
+    dns_rfc2136_name: certbot_challenge.
+    dns_rfc2136_secret: "{{ vault_certbot_dns_secret }}"
+    mail: root@crans.org
+    certname: crans.org
+    domains: "irc.crans.org"
 
 loc_nginx:
   service_name: "thelounge"
@@ -12,7 +17,8 @@ loc_nginx:
     - server_name:
         - "irc.crans.org"
         - "irc"
-      ssl: true
+      default: true
+      ssl: crans.org
       locations:
         - filter: "^~ /web/"
           params:
diff --git a/plays/irc.yml b/plays/irc.yml
index 95563292..ab253706 100755
--- a/plays/irc.yml
+++ b/plays/irc.yml
@@ -2,7 +2,7 @@
 ---
 - hosts: irc
   vars:
-    certbot: '{{ glob_certbot | default({}) | combine(loc_certbot | default({})) }}'
+    certbot: '{{ loc_certbot | default(glob_certbot | default([])) }}'
     nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
     thelounge: '{{ glob_thelounge | default({}) | combine(loc_thelounge | default({})) }}'
   roles:
diff --git a/plays/mailman.yml b/plays/mailman.yml
index a0a2a60f..ac7afd00 100755
--- a/plays/mailman.yml
+++ b/plays/mailman.yml
@@ -8,6 +8,10 @@
       default_url: "https://lists.crans.org/"
       default_host: "lists.crans.org"
       default_language: "fr"
+      custom_logo: "crans_icon_dark.svg"
+      custom_logo_name: "crans.svg"
+      custom_logo_url: "https://www.crans.org/"
+      custom_logo_alt: "CRANS"
     spamassassin: "SpamAssassin_crans"
     smtphost: "smtp.adm.crans.org"
     mynetworks: ['138.231.0.0/16', '185.230.76.0/22', '2a0c:700:0::/40']
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 4d4179c8..847e397b 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -7,16 +7,22 @@
   retries: 3
   until: apt_result is succeeded
 
-- name: Copy snippets
+- name: Copy proxypass snippets
   template:
-    src: "nginx/snippets/{{ item }}.j2"
-    dest: "/etc/nginx/snippets/{{ item }}"
+    src: "nginx/snippets/options-proxypass.conf.j2"
+    dest: "/etc/nginx/snippets/options-proxypass.conf"
     owner: root
     group: root
     mode: 0644
-  loop:
-    - options-ssl.conf
-    - options-proxypass.conf
+
+- name: Copy SSL snippets
+  template:
+    src: "nginx/snippets/options-ssl.conf.j2"
+    dest: "/etc/nginx/snippets/options-ssl.{{ item.name }}.conf"
+    owner: root
+    group: root
+    mode: 0644
+  loop: "{{ nginx.ssl }}"
 
 - name: Copy dhparam
   template:
@@ -98,12 +104,6 @@
     group: www-data
     mode: 0644
 
-- name: Indicate role in motd
-  template:
-    src: update-motd.d/05-service.j2
-    dest: /etc/update-motd.d/05-nginx
-    mode: 0755
-
 - name: Install passwords
   when: nginx.auth_passwd|length > 0
   template:
@@ -119,3 +119,9 @@
     owner: www-data
     group: www-data
     mode: 0644
+
+- name: Indicate role in motd
+  template:
+    src: update-motd.d/05-service.j2
+    dest: /etc/update-motd.d/05-nginx
+    mode: 0755
diff --git a/roles/nginx/templates/nginx/sites-available/service.j2 b/roles/nginx/templates/nginx/sites-available/service.j2
index 5a883a48..1e17e099 100644
--- a/roles/nginx/templates/nginx/sites-available/service.j2
+++ b/roles/nginx/templates/nginx/sites-available/service.j2
@@ -19,7 +19,7 @@ upstream {{ upstream.name }} {
 server {
     listen 443 default_server ssl;
     listen [::]:443 default_server ssl;
-    include "/etc/nginx/snippets/options-ssl.conf";
+    include "/etc/nginx/snippets/options-ssl.{{ nginx.default_ssl_domain }}.conf";
 
     server_name _;
     charset utf-8;
@@ -55,8 +55,8 @@ server {
 {% if server.ssl is defined and server.ssl -%}
 # Redirect HTTP to HTTPS
 server {
-    listen 80;
-    listen [::]:80;
+    listen 80{% if server.default is defined and server.default %} default_server{% endif %};
+    listen [::]:80{% if server.default is defined and server.default %} default_server{% endif %};
 
     server_name {{ server.server_name|join(" ") }};
     charset utf-8;
@@ -72,9 +72,9 @@ server {
 
 server {
     {% if server.ssl is defined and server.ssl -%}
-    listen 443 ssl;
-    listen [::]:443 ssl;
-    include "/etc/nginx/snippets/options-ssl.conf";
+    listen 443{% if server.default is defined and server.default %} default_server{% endif %} ssl;
+    listen [::]:443{% if server.default is defined and server.default %} default_server{% endif %} ssl;
+    include "/etc/nginx/snippets/options-ssl.{{ server.ssl }}.conf";
     {% else -%}
     listen 80 default;
     listen [::]:80 default;
@@ -86,29 +86,21 @@ server {
     # Hide Nginx version
     server_tokens off;
 
-    {% if server.root is defined -%}
-    root {{ server.root }};
-    {% endif -%}
-    {% if server.index is defined -%}
-    index {{ server.index|join(" ") }};
-    {% endif -%}
-
-    {% if server.access_log is defined -%}
-    access_log {{ server.access_log }};
-    {% endif -%}
-    {% if server.error_log is defined -%}
-    error_log {{ server.error_log }};
-    {% endif -%}
+    {% if server.root is defined %}root {{ server.root }};{% endif %}
+    {% if server.index is defined %}index {{ server.index|join(" ") }};{% endif %}
 
-    {% if server.locations is defined -%}
+    {% if server.access_log is defined %}access_log {{ server.access_log }};{% endif %}
+    {% if server.error_log is defined %}error_log {{ server.error_log }};{% endif %}
 
-    {% for location in server.locations -%}
+{% if server.locations is defined %}
+{% for location in server.locations %}
     location {{ location.filter }} {
-        {% for param in location.params -%}
+{% for param in location.params %}
         {{ param }};
-        {% endfor -%}
+{% endfor %}
     }
-    {% endfor -%}
-{% endif -%}
+
+{% endfor %}
+{% endif %}
 }
 {% endfor %}
diff --git a/roles/nginx/templates/nginx/snippets/options-ssl.conf.j2 b/roles/nginx/templates/nginx/snippets/options-ssl.conf.j2
index 1a9273a8..c980c90b 100644
--- a/roles/nginx/templates/nginx/snippets/options-ssl.conf.j2
+++ b/roles/nginx/templates/nginx/snippets/options-ssl.conf.j2
@@ -1,7 +1,7 @@
 {{ ansible_header | comment }}
 
-ssl_certificate {{ nginx.ssl.cert }};
-ssl_certificate_key {{ nginx.ssl.cert_key }};
+ssl_certificate {{ item.cert }};
+ssl_certificate_key {{ item.cert_key }};
 ssl_session_timeout 1d;
 ssl_session_cache shared:MozSSL:10m;
 ssl_session_tickets off;
@@ -13,5 +13,5 @@ ssl_prefer_server_ciphers off;
 # Enable OCSP Stapling, point to certificate chain
 ssl_stapling on;
 ssl_stapling_verify on;
-ssl_trusted_certificate {{ nginx.ssl.trusted_cert }};
+ssl_trusted_certificate {{ item.trusted_cert }};
 
diff --git a/roles/nginx/templates/update-motd.d/10-service.j2 b/roles/nginx/templates/update-motd.d/10-service.j2
deleted file mode 100755
index 82373d0b..00000000
--- a/roles/nginx/templates/update-motd.d/10-service.j2
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/tail +14
-{{ ansible_header | comment }}
-> NGINX a été déployé sur cette machine. Voir /etc/nginx/.
-- 
GitLab


From de58138a2294b667e543241ce9f6989f56eb1703 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 14:44:44 +0100
Subject: [PATCH 30/49] [nginx] Multiple certficates are compatible with
 reverse-proxy

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/reverseproxy.yml                   | 25 +++++++++++--------
 plays/reverse-proxy.yml                       |  3 ++-
 roles/nginx/tasks/main.yml                    |  4 +--
 .../nginx/sites-available/redirect.j2         | 10 ++++----
 .../nginx/sites-available/reverseproxy.j2     |  4 +--
 .../reverseproxy_redirect_dname.j2            |  6 ++---
 6 files changed, 29 insertions(+), 23 deletions(-)

diff --git a/group_vars/reverseproxy.yml b/group_vars/reverseproxy.yml
index fb542879..3be4680e 100644
--- a/group_vars/reverseproxy.yml
+++ b/group_vars/reverseproxy.yml
@@ -1,16 +1,21 @@
-certbot:
-  dns_rfc2136_name: certbot_challenge.
-  dns_rfc2136_secret: "{{ vault.certbot_dns_secret }}"
-  mail: root@crans.org
-  certname: crans.org
-  domains: "crans.org, *.crans.org, crans.fr, *.crans.fr, crans.eu, *.crans.eu"
+loc_certbot:
+  - dns_rfc2136_server: '172.16.10.147'
+    dns_rfc2136_name: certbot_challenge.
+    dns_rfc2136_secret: "{{ vault.certbot_dns_secret }}"
+    mail: root@crans.org
+    certname: crans.org
+    domains: "crans.org, *.crans.org, crans.fr, *.crans.fr, crans.eu, *.crans.eu"
 
-nginx:
+loc_nginx:
+  servers: []
   ssl:
-    cert: /etc/letsencrypt/live/crans.org/fullchain.pem
-    cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
-    trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
+    - name: crans.org
+      cert: /etc/letsencrypt/live/crans.org/fullchain.pem
+      cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
+      trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
 
+
+glob_reverseproxy:
   redirect_dnames:
     - crans.eu
     - crans.fr
diff --git a/plays/reverse-proxy.yml b/plays/reverse-proxy.yml
index c81106c4..3b03f0a9 100755
--- a/plays/reverse-proxy.yml
+++ b/plays/reverse-proxy.yml
@@ -3,7 +3,8 @@
 - hosts: reverseproxy
   vars:
     certbot: '{{ loc_certbot | default(glob_certbot | default([])) }}'
-    mirror: '{{ glob_mirror.name }}'
+    nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
+    reverseproxy: '{{ glob_reverseproxy | default({}) | combine(loc_reverseproxy | default({})) }}'
   roles:
     - certbot
     - nginx
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 847e397b..c4371062 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -38,7 +38,7 @@
     state: absent
 
 - name: Copy reverse proxy sites
-  when: nginx.reverseproxy_sites is defined or nginx.redirect_sites is defined
+  when: reverseproxy is defined
   template:
     src: "nginx/sites-available/{{ item }}.j2"
     dest: "/etc/nginx/sites-available/{{ item }}"
@@ -52,7 +52,7 @@
   notify: Reload nginx
 
 - name: Activate reverse proxy sites
-  when: nginx.reverseproxy_sites is defined or nginx.redirect_sites is defined
+  when: reverseproxy is defined
   file:
     src: "/etc/nginx/sites-available/{{ item }}"
     dest: "/etc/nginx/sites-enabled/{{ item }}"
diff --git a/roles/nginx/templates/nginx/sites-available/redirect.j2 b/roles/nginx/templates/nginx/sites-available/redirect.j2
index 9cdb545b..44cce798 100644
--- a/roles/nginx/templates/nginx/sites-available/redirect.j2
+++ b/roles/nginx/templates/nginx/sites-available/redirect.j2
@@ -1,6 +1,6 @@
 {{ ansible_header | comment }}
 
-{% for site in nginx.redirect_sites %}
+{% for site in reverseproxy.redirect_sites %}
 # Redirect http://{{ site.from }} to http://{{ site.to }}
 server {
     listen 80;
@@ -21,7 +21,7 @@ server {
     server_name {{ site.from }};
 
     # SSL common conf
-    include "/etc/nginx/snippets/options-ssl.conf";
+    include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
 
     location / {
         return 302 https://{{ site.to }}$request_uri;
@@ -31,8 +31,8 @@ server {
 {% endfor %}
 
 {# Also redirect for DNAMEs #}
-{% for dname in nginx.redirect_dnames %}
-{% for site in nginx.redirect_sites %}
+{% for dname in reverseproxy.redirect_dnames %}
+{% for site in reverseproxy.redirect_sites %}
 {% set from = site.from | regex_replace('crans.org', dname) %}
 {% if from != site.from %}
 # Redirect http://{{ from }} to http://{{ site.to }}
@@ -55,7 +55,7 @@ server {
     server_name {{ from }};
 
     # SSL common conf
-    include "/etc/nginx/snippets/options-ssl.conf";
+    include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
 
     location / {
         return 302 https://{{ site.to }}$request_uri;
diff --git a/roles/nginx/templates/nginx/sites-available/reverseproxy.j2 b/roles/nginx/templates/nginx/sites-available/reverseproxy.j2
index 0898da05..dc8ae1b4 100644
--- a/roles/nginx/templates/nginx/sites-available/reverseproxy.j2
+++ b/roles/nginx/templates/nginx/sites-available/reverseproxy.j2
@@ -7,7 +7,7 @@ map $http_upgrade $connection_upgrade {
     ''      close;
 }
 
-{% for site in nginx.reverseproxy_sites %}
+{% for site in reverseproxy.reverseproxy_sites %}
 # Redirect http://{{ site.from }} to https://{{ site.from }}
 server {
     listen 80;
@@ -28,7 +28,7 @@ server {
     server_name {{ site.from }};
 
     # SSL common conf
-    include "/etc/nginx/snippets/options-ssl.conf";
+    include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
 
     # Log into separate log files
     access_log      /var/log/nginx/{{ site.from }}.log;
diff --git a/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2 b/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
index db2084a4..0ca20f57 100644
--- a/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
+++ b/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
@@ -1,7 +1,7 @@
 {{ ansible_header | comment }}
 
-{% for dname in nginx.redirect_dnames %}
-{% for site in nginx.reverseproxy_sites %}
+{% for dname in reverseproxy.redirect_dnames %}
+{% for site in reverseproxy.reverseproxy_sites %}
 {% set from = site.from | regex_replace('crans.org', dname) %}
 {% set to = site.from %}
 {% if from != site.from %}
@@ -25,7 +25,7 @@ server {
     server_name {{ from }};
 
     # SSL common conf
-    include "/etc/nginx/snippets/options-ssl.conf";
+    include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
 
     location / {
         return 302 https://{{ to }}$request_uri;
-- 
GitLab


From 19beb342271ff5c992f9f8981151501eaefde83c Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Thu, 18 Feb 2021 18:28:19 +0100
Subject: [PATCH 31/49] [nginx/certbot] Remove obsolete files

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 roles/certbot/tasks/main.yml | 11 ++++++++++-
 roles/nginx/tasks/main.yml   | 17 +++++++++--------
 2 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/roles/certbot/tasks/main.yml b/roles/certbot/tasks/main.yml
index 812aff2c..91e2fde8 100644
--- a/roles/certbot/tasks/main.yml
+++ b/roles/certbot/tasks/main.yml
@@ -29,7 +29,7 @@
   template:
     src: "letsencrypt/dhparam.j2"
     dest: "/etc/letsencrypt/dhparam"
-    mode: 0644
+    mode: 0600
 
 - name: Create /etc/letsencrypt/conf.d
   file:
@@ -46,3 +46,12 @@
 - name: Run certbot
   command: certbot --non-interactive --config /etc/letsencrypt/conf.d/{{ item.certname }}.ini certonly
   loop: "{{ certbot }}"
+
+- name: Clean old files
+  file:
+    path: "{{ item }}"
+    state: absent
+  loop:
+    - "/etc/letsencrypt/options-ssl-nginx.conf"
+    - "/etc/letsencrypt/ssl-dhparams.pem"
+    - "/etc/letsencrypt/rfc2136.ini"
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index c4371062..c43f3a33 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -24,14 +24,6 @@
     mode: 0644
   loop: "{{ nginx.ssl }}"
 
-- name: Copy dhparam
-  template:
-    src: letsencrypt/dhparam.j2
-    dest: /etc/letsencrypt/dhparam
-    owner: root
-    group: root
-    mode: 0644
-
 - name: Disable default site
   file:
     dest: "/etc/nginx/sites-enabled/default"
@@ -125,3 +117,12 @@
     src: update-motd.d/05-service.j2
     dest: /etc/update-motd.d/05-nginx
     mode: 0755
+
+- name: Clean old files
+  file:
+    path: "{{ item }}"
+    state: absent
+  loop:
+    - "/etc/nginx/snippets/options-ssl.conf"
+    - "/var/www/custom_401.html"
+    - "/var/www/robots.txt"
-- 
GitLab


From 44cf074a39ed2db31fdcd8d6eae3d96068c49e05 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Thu, 18 Feb 2021 22:07:24 +0100
Subject: [PATCH 32/49] [nginx] Add feature to add additional params to a nginx
 server

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/nginx.yml                                   | 1 +
 roles/nginx/templates/nginx/sites-available/service.j2 | 6 ++++++
 2 files changed, 7 insertions(+)

diff --git a/group_vars/nginx.yml b/group_vars/nginx.yml
index 76e216b5..a75550cc 100644
--- a/group_vars/nginx.yml
+++ b/group_vars/nginx.yml
@@ -19,6 +19,7 @@ glob_nginx:
       locations:
         - filter: "/"
           params: []
+      additional_params: []
   upstreams: []
 
   auth_passwd: []
diff --git a/roles/nginx/templates/nginx/sites-available/service.j2 b/roles/nginx/templates/nginx/sites-available/service.j2
index 1e17e099..b44a4d53 100644
--- a/roles/nginx/templates/nginx/sites-available/service.j2
+++ b/roles/nginx/templates/nginx/sites-available/service.j2
@@ -92,6 +92,12 @@ server {
     {% if server.access_log is defined %}access_log {{ server.access_log }};{% endif %}
     {% if server.error_log is defined %}error_log {{ server.error_log }};{% endif %}
 
+{% if server.additional_params is defined %}
+{% for param in server.additional_params %}
+    {{ param }};
+{% endfor %}
+{% endif %}
+
 {% if server.locations is defined %}
 {% for location in server.locations %}
     location {{ location.filter }} {
-- 
GitLab


From 1575b3eea549a819c7728c8b879a476ee15db235 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Fri, 19 Feb 2021 01:37:52 +0100
Subject: [PATCH 33/49] [nginx] Add nginx playbook

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 plays/nginx.yml | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100755 plays/nginx.yml

diff --git a/plays/nginx.yml b/plays/nginx.yml
new file mode 100755
index 00000000..073a013a
--- /dev/null
+++ b/plays/nginx.yml
@@ -0,0 +1,7 @@
+#!/usr/bin/env ansible-playbook
+---
+- hosts: nginx
+  vars:
+    nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
+  roles:
+    - nginx
-- 
GitLab


From 201dbd6ee0cb7aece54f0e0bfdba02fb964405dc Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 14:33:21 +0100
Subject: [PATCH 34/49] [thelounge] Download debian package

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 roles/thelounge/handlers/main.yml |  5 +++++
 roles/thelounge/tasks/main.yml    | 21 ++++++++++-----------
 2 files changed, 15 insertions(+), 11 deletions(-)
 create mode 100644 roles/thelounge/handlers/main.yml

diff --git a/roles/thelounge/handlers/main.yml b/roles/thelounge/handlers/main.yml
new file mode 100644
index 00000000..95b737c9
--- /dev/null
+++ b/roles/thelounge/handlers/main.yml
@@ -0,0 +1,5 @@
+---
+- name: Restart The Lounge
+  systemd:
+    name: thelounge
+    state: restarted
diff --git a/roles/thelounge/tasks/main.yml b/roles/thelounge/tasks/main.yml
index 2d4053e9..7403a1dc 100644
--- a/roles/thelounge/tasks/main.yml
+++ b/roles/thelounge/tasks/main.yml
@@ -8,16 +8,19 @@
   retries: 3
   until: apt_result is succeeded
 
-#- name: Download The Lounge packet
-#  get_url:
-#    url: https://github.com/thelounge/thelounge/releases/download/v4.2.0/thelounge_4.2.0_all.deb
-#    dest: /var/cache/apt/archives/thelounge_4.2.0_all.deb
-#    checksum: sha256:6096f992526f7ce01ea7c5aa1fb12bb013ce872f3c67f1fe11cd44f85a3fe405
-#    mode: 0644
+- name: Download The Lounge packet
+  get_url:
+    url: https://github.com/thelounge/thelounge/releases/download/v4.2.0/thelounge_4.2.0_all.deb
+    dest: /var/cache/apt/archives/thelounge_4.2.0_all.deb
+    checksum: sha512:10d17c199fef595c46ba55f36ab7aa0a7469448603482eb780284e05532f5d69302d47bdc9c558badc30b0a026767a24b7065a4fe8d7c865f1737b0041420208
+    owner: root
+    group: root
+    mode: 0644
 
 - name: Install The Lounge from the deb package
   apt:
     deb: /var/cache/apt/archives/thelounge_4.2.0_all.deb
+  notify: Restart The Lounge
 
 - name: Deploy The Lounge configuration
   template:
@@ -26,8 +29,4 @@
     owner: thelounge
     group: thelounge
     mode: 0660
-
-- name: Restart The Lounge
-  systemd:
-    name: thelounge
-    state: restarted
+  notify: Restart The Lounge
-- 
GitLab


From 1ec1aeca90c6e02bad0f7d8370fb2687c45fc8c5 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 14:34:22 +0100
Subject: [PATCH 35/49] [thelounge] vault_ldap_thelounge_password ->
 vault.ldap_thelounge_password

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/irc.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/group_vars/irc.yml b/group_vars/irc.yml
index e55af642..67a0ab6d 100644
--- a/group_vars/irc.yml
+++ b/group_vars/irc.yml
@@ -19,7 +19,7 @@ glob_thelounge:
     url: "ldap://172.16.10.157"
     primaryKey: "cn"
     rootDN: "cn=thelounge,ou=service-users,dc=crans,dc=org"
-    rootPassword: "{{ vault_ldap_thelounge_password }}"
+    rootPassword: "{{ vault.ldap_thelounge_password }}"
     filter: "(objectclass=inetOrgPerson)"
     base: "dc=crans,dc=org"
     scope: "sub"
-- 
GitLab


From 2c9b89a74d78e89dc7fdb226a12b3439a77982fc Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 14:37:54 +0100
Subject: [PATCH 36/49] [thelounge] Rename irc to thelounge

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/{irc.yml => thelounge.yml} |  0
 hosts                                 |  5 ++++-
 plays/irc.yml                         | 10 +++++++---
 3 files changed, 11 insertions(+), 4 deletions(-)
 rename group_vars/{irc.yml => thelounge.yml} (100%)

diff --git a/group_vars/irc.yml b/group_vars/thelounge.yml
similarity index 100%
rename from group_vars/irc.yml
rename to group_vars/thelounge.yml
diff --git a/hosts b/hosts
index d7bde7ff..c1246405 100644
--- a/hosts
+++ b/hosts
@@ -64,7 +64,6 @@ horde.adm.crans.org
 
 [irc]
 irc.adm.crans.org
-zamok.adm.crans.org
 
 [keepalived:children]
 routeurs_vm
@@ -126,6 +125,10 @@ routeur-daniel.adm.crans.org
 routeur-jack.adm.crans.org
 routeur-sam.adm.crans.org
 
+[thelounge]
+irc.adm.crans.org
+zamok.adm.crans.org
+
 [virtu]
 daniel.adm.crans.org
 jack.adm.crans.org
diff --git a/plays/irc.yml b/plays/irc.yml
index ab253706..348f9d26 100755
--- a/plays/irc.yml
+++ b/plays/irc.yml
@@ -1,11 +1,15 @@
 #!/usr/bin/env ansible-playbook
 ---
-- hosts: irc
+- hosts: thelounge
+  vars:
+    thelounge: '{{ glob_thelounge | default({}) | combine(loc_thelounge | default({})) }}'
+  roles:
+    - thelounge
+
+- hosts: thelounge,!adh_server
   vars:
     certbot: '{{ loc_certbot | default(glob_certbot | default([])) }}'
     nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
-    thelounge: '{{ glob_thelounge | default({}) | combine(loc_thelounge | default({})) }}'
   roles:
     - certbot
     - nginx
-    - thelounge
-- 
GitLab


From 8a1f26d852e129d9cc6738eafe538a3b28794ed9 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 14:47:25 +0100
Subject: [PATCH 37/49] [thelounge] Replace groups in hosts

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 hosts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/hosts b/hosts
index c1246405..3207f3e5 100644
--- a/hosts
+++ b/hosts
@@ -23,9 +23,9 @@ belenios.adm.crans.org
 [certbot:children]
 dovecot
 git
-irc
 radius  # We use certbot to manage LE certificates
 reverseproxy
+thelounge
 
 [dhcp:children]
 routeurs_vm
@@ -88,9 +88,9 @@ monitoring.adm.crans.org
 charybde.adm.crans.org
 
 [nginx:children]
-irc
 mailman
 reverseproxy
+thelounge
 
 [ntp_server]
 charybde.adm.crans.org
-- 
GitLab


From e4bdec1dd8238f9ccc0ed677065b80d05a56c60e Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 14:52:15 +0100
Subject: [PATCH 38/49] [nginx] Add Nginx playbook

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 plays/nginx.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/plays/nginx.yml b/plays/nginx.yml
index 073a013a..f2895803 100755
--- a/plays/nginx.yml
+++ b/plays/nginx.yml
@@ -1,5 +1,6 @@
 #!/usr/bin/env ansible-playbook
 ---
+# Deploy Nginx
 - hosts: nginx
   vars:
     nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
-- 
GitLab


From 4c115a8b342a157e1fface355ad35bbbdfe45302 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 15:09:47 +0100
Subject: [PATCH 39/49] [thelounge] Don't load ldap configuration if it is
 disabled

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 host_vars/irc.adm.crans.org.yml        | 2 +-
 plays/nginx.yml                        | 2 +-
 roles/thelounge/templates/config.js.j2 | 3 ++-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/host_vars/irc.adm.crans.org.yml b/host_vars/irc.adm.crans.org.yml
index bf956da8..a093bda0 100644
--- a/host_vars/irc.adm.crans.org.yml
+++ b/host_vars/irc.adm.crans.org.yml
@@ -6,7 +6,7 @@ interfaces:
 loc_certbot:
   - dns_rfc2136_server: '172.16.10.147'
     dns_rfc2136_name: certbot_challenge.
-    dns_rfc2136_secret: "{{ vault_certbot_dns_secret }}"
+    dns_rfc2136_secret: "{{ vault.certbot_dns_secret }}"
     mail: root@crans.org
     certname: crans.org
     domains: "irc.crans.org"
diff --git a/plays/nginx.yml b/plays/nginx.yml
index f2895803..7cb58317 100755
--- a/plays/nginx.yml
+++ b/plays/nginx.yml
@@ -1,7 +1,7 @@
 #!/usr/bin/env ansible-playbook
 ---
 # Deploy Nginx
-- hosts: nginx
+- hosts: nginx,!adh_server
   vars:
     nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
   roles:
diff --git a/roles/thelounge/templates/config.js.j2 b/roles/thelounge/templates/config.js.j2
index 3dd10515..87ce22da 100644
--- a/roles/thelounge/templates/config.js.j2
+++ b/roles/thelounge/templates/config.js.j2
@@ -392,7 +392,7 @@ module.exports = {
 		// - `enable`: when set to `false`, LDAP support is disabled and all other
 		//   values are ignored.
 		enable: {{ thelounge.ldap_enable }},
-
+{% if thelounge.ldap_enable == "true" %}
 		// - `url`: A url of the form `ldaps://<ip>:<port>`.
 		//   For plain connections, use the `ldap` scheme.
 		url: "{{ thelounge.ldap.url }}",
@@ -442,6 +442,7 @@ module.exports = {
 			//   - `scope`: LDAP search scope. It is set to `"sub"` by default.
 			scope: "{{ thelounge.ldap.scope }}",
 		},
+{% endif %}
 	},
 
 	// ## Debugging settings
-- 
GitLab


From 8d8c212f496012f20a253b89409ecdbc76753527 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 16:15:31 +0100
Subject: [PATCH 40/49] [nginx/roundcube] Factorize configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/roundcube.yml                     |  3 ++-
 host_vars/roundcube.adm.crans.org.yml        | 22 +++++++++++++++
 hosts                                        |  1 +
 plays/roundcube.yml                          |  4 ++-
 roles/roundcube/tasks/main.yml               | 14 ----------
 roles/roundcube/templates/nginx/roundcube.j2 | 28 --------------------
 6 files changed, 28 insertions(+), 44 deletions(-)
 delete mode 100644 roles/roundcube/templates/nginx/roundcube.j2

diff --git a/group_vars/roundcube.yml b/group_vars/roundcube.yml
index 9c32c7d0..903313f5 100644
--- a/group_vars/roundcube.yml
+++ b/group_vars/roundcube.yml
@@ -1,4 +1,4 @@
-roundcube_glob:
+glob_roundcube:
   name: Crans
   imap_server: owl.adm.crans.org
   smtp_server: smtp.adm.crans.org
@@ -29,3 +29,4 @@ roundcube_glob:
     elastic: https://www.crans.org/images/crans.svg
     larry: https://www.crans.org/images/crans_banner.png
     classic: https://www.crans.org/images/crans_banner.png
+
diff --git a/host_vars/roundcube.adm.crans.org.yml b/host_vars/roundcube.adm.crans.org.yml
index 2eb6f993..67d59ab3 100644
--- a/host_vars/roundcube.adm.crans.org.yml
+++ b/host_vars/roundcube.adm.crans.org.yml
@@ -2,3 +2,25 @@
 interfaces:
   adm: eth0
   srv_nat: eth1
+
+loc_nginx:
+  service_name: "roundcube"
+  ssl: []
+  servers:
+    - server_name:
+      - "roundcube.adm.crans.org"
+      default: true
+      root: "/var/lib/roundcube"
+      locations:
+        - filter: "~ \\.php$"
+          params:
+            - "include snippets/fastcgi-php.conf"
+            - "fastcgi_buffer_size 128k"
+            - "fastcgi_buffers 4 256k"
+            - "fastcgi_busy_buffers_size 256k"
+            - "fastcgi_pass unix:/var/run/php/php7.3-fpm.sock"
+            - "include fastcgi_params"
+      additional_params:
+        - "index index.php index.htm index.html"
+        - "try_files $uri $uri/ /index.php?q=$uri&$args"
+        - "client_max_body_size 10G"
diff --git a/hosts b/hosts
index 3207f3e5..a6380b61 100644
--- a/hosts
+++ b/hosts
@@ -90,6 +90,7 @@ charybde.adm.crans.org
 [nginx:children]
 mailman
 reverseproxy
+roundcube
 thelounge
 
 [ntp_server]
diff --git a/plays/roundcube.yml b/plays/roundcube.yml
index 996ca7c4..c57e8920 100755
--- a/plays/roundcube.yml
+++ b/plays/roundcube.yml
@@ -3,6 +3,8 @@
 
 - hosts: roundcube
   vars:
-    roundcube: '{{ roundcube_glob | default({}) | combine(roundcube_loc | default({})) }}'
+    nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
+    roundcube: '{{ glob_roundcube | default({}) | combine(loc_roundcube | default({})) }}'
   roles:
     - roundcube
+    - nginx
diff --git a/roles/roundcube/tasks/main.yml b/roles/roundcube/tasks/main.yml
index 18745b55..a67f90b8 100644
--- a/roles/roundcube/tasks/main.yml
+++ b/roles/roundcube/tasks/main.yml
@@ -4,7 +4,6 @@
     update_cache: true
     install_recommends: false
     name:
-      - nginx
       - roundcube
       - roundcube-pgsql
       - roundcube-plugins
@@ -52,19 +51,6 @@
   loop: "{{ roundcube.plugins }}"
   when: item.repo is defined
 
-- name: Copy NGINX site
-  template:
-    src: nginx/roundcube.j2
-    dest: /etc/nginx/sites-available/roundcube
-  notify: Restart nginx
-
-- name: Activate NGINX site
-  file:
-    src: /etc/nginx/sites-available/roundcube
-    dest: /etc/nginx/sites-enabled/roundcube
-    state: link
-  notify: Restart nginx
-
 - name: Indicate role in motd
   template:
     src: update-motd.d/05-service.j2
diff --git a/roles/roundcube/templates/nginx/roundcube.j2 b/roles/roundcube/templates/nginx/roundcube.j2
deleted file mode 100644
index ce34cead..00000000
--- a/roles/roundcube/templates/nginx/roundcube.j2
+++ /dev/null
@@ -1,28 +0,0 @@
-{{ ansible_header | comment }}
-
-server {
-    listen roundcube.adm.crans.org:80;
-    listen [2a0c:700:0:2:6809:acff:fe67:47e6]:80;
-
-    server_name roundcube.adm.crans.org;
-
-    root /var/lib/roundcube;
-
-    index index.php index.htm index.html;
-    try_files $uri $uri/ /index.php?q=$uri&$args;
-
-    location ~ \.php$ {
-        include snippets/fastcgi-php.conf;
-        fastcgi_buffer_size 128k;
-        fastcgi_buffers 4 256k;
-        fastcgi_busy_buffers_size 256k;
-        fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
-        include fastcgi_params;
-    }
-
-    set_real_ip_from 10.231.136.0/24;
-    set_real_ip_from 2a0c:700:0:2::/64;
-    real_ip_header P-Real-Ip;
-
-    client_max_body_size 10G;
-}
-- 
GitLab


From 3b79c0177c65b7b8687ec93fa31e4ed9ce600e55 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 18:28:55 +0100
Subject: [PATCH 41/49] [nginx] Don't deploy SSL configuration if we don't need
 one

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/roundcube.yml              | 20 ++++++++++++++++++++
 host_vars/charybde.adm.crans.org.yml  |  1 +
 host_vars/roundcube.adm.crans.org.yml | 22 ----------------------
 3 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/group_vars/roundcube.yml b/group_vars/roundcube.yml
index 903313f5..7e660170 100644
--- a/group_vars/roundcube.yml
+++ b/group_vars/roundcube.yml
@@ -30,3 +30,23 @@ glob_roundcube:
     larry: https://www.crans.org/images/crans_banner.png
     classic: https://www.crans.org/images/crans_banner.png
 
+loc_nginx:
+  service_name: "roundcube"
+  ssl: []
+  servers:
+    - server_name: "{{ query('ldap', 'ip', ansible_hostname, 'adm') | ipwrap + [ansible_hostname, ansible_hostname + '.adm.crans.org'] }}"
+      default: true
+      root: "/var/lib/roundcube"
+      locations:
+        - filter: "~ \\.php$"
+          params:
+            - "include snippets/fastcgi-php.conf"
+            - "fastcgi_buffer_size 128k"
+            - "fastcgi_buffers 4 256k"
+            - "fastcgi_busy_buffers_size 256k"
+            - "fastcgi_pass unix:/var/run/php/php7.3-fpm.sock"
+            - "include fastcgi_params"
+      additional_params:
+        - "index index.php index.htm index.html"
+        - "try_files $uri $uri/ /index.php?q=$uri&$args"
+        - "client_max_body_size 10G"
diff --git a/host_vars/charybde.adm.crans.org.yml b/host_vars/charybde.adm.crans.org.yml
index fd0885f9..00db4ce7 100644
--- a/host_vars/charybde.adm.crans.org.yml
+++ b/host_vars/charybde.adm.crans.org.yml
@@ -36,6 +36,7 @@ to_backup:
 
 loc_nginx:
   service_name: ftp
+  ssl: []
   servers:
     - server_name:
         - "ftp"
diff --git a/host_vars/roundcube.adm.crans.org.yml b/host_vars/roundcube.adm.crans.org.yml
index 67d59ab3..2eb6f993 100644
--- a/host_vars/roundcube.adm.crans.org.yml
+++ b/host_vars/roundcube.adm.crans.org.yml
@@ -2,25 +2,3 @@
 interfaces:
   adm: eth0
   srv_nat: eth1
-
-loc_nginx:
-  service_name: "roundcube"
-  ssl: []
-  servers:
-    - server_name:
-      - "roundcube.adm.crans.org"
-      default: true
-      root: "/var/lib/roundcube"
-      locations:
-        - filter: "~ \\.php$"
-          params:
-            - "include snippets/fastcgi-php.conf"
-            - "fastcgi_buffer_size 128k"
-            - "fastcgi_buffers 4 256k"
-            - "fastcgi_busy_buffers_size 256k"
-            - "fastcgi_pass unix:/var/run/php/php7.3-fpm.sock"
-            - "include fastcgi_params"
-      additional_params:
-        - "index index.php index.htm index.html"
-        - "try_files $uri $uri/ /index.php?q=$uri&$args"
-        - "client_max_body_size 10G"
-- 
GitLab


From 82119c746ef3540c6c135e22e98c99916e0b0cd8 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 18:55:10 +0100
Subject: [PATCH 42/49] [nginx] Define proper set_realip_from

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/nginx.yml                          |  3 +++
 .../nginx/sites-available/redirect.j2         | 20 +++++++++++++++++++
 .../nginx/sites-available/reverseproxy.j2     | 10 ++++++++--
 .../reverseproxy_redirect_dname.j2            | 10 ++++++++++
 .../nginx/sites-available/service.j2          | 20 +++++++++++++++++++
 5 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/group_vars/nginx.yml b/group_vars/nginx.yml
index a75550cc..774fa0e1 100644
--- a/group_vars/nginx.yml
+++ b/group_vars/nginx.yml
@@ -26,4 +26,7 @@ glob_nginx:
   default_server:
   default_ssl_server:
   default_ssl_domain: crans.org
+  real_ip_from:
+    - "172.16.0.0/16"
+    - "2a0c:700:0:2::/64"
   deploy_robots_file: false
diff --git a/roles/nginx/templates/nginx/sites-available/redirect.j2 b/roles/nginx/templates/nginx/sites-available/redirect.j2
index 44cce798..d40a3a4b 100644
--- a/roles/nginx/templates/nginx/sites-available/redirect.j2
+++ b/roles/nginx/templates/nginx/sites-available/redirect.j2
@@ -8,6 +8,11 @@ server {
 
     server_name {{ site.from }};
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 http://{{ site.to }}$request_uri;
     }
@@ -23,6 +28,11 @@ server {
     # SSL common conf
     include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 https://{{ site.to }}$request_uri;
     }
@@ -42,6 +52,11 @@ server {
 
     server_name {{ from }};
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 http://{{ site.to }}$request_uri;
     }
@@ -57,6 +72,11 @@ server {
     # SSL common conf
     include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 https://{{ site.to }}$request_uri;
     }
diff --git a/roles/nginx/templates/nginx/sites-available/reverseproxy.j2 b/roles/nginx/templates/nginx/sites-available/reverseproxy.j2
index dc8ae1b4..27013aab 100644
--- a/roles/nginx/templates/nginx/sites-available/reverseproxy.j2
+++ b/roles/nginx/templates/nginx/sites-available/reverseproxy.j2
@@ -15,6 +15,11 @@ server {
 
     server_name {{ site.from }};
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 https://$host$request_uri;
     }
@@ -43,8 +48,9 @@ server {
         root /var/www/html;
     }
 
-    set_real_ip_from 10.231.136.0/24;
-    set_real_ip_from 2a0c:700:0:2::/64;
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
     real_ip_header P-Real-Ip;
 
     location / {
diff --git a/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2 b/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
index 0ca20f57..0b39022f 100644
--- a/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
+++ b/roles/nginx/templates/nginx/sites-available/reverseproxy_redirect_dname.j2
@@ -12,6 +12,11 @@ server {
 
     server_name {{ from }};
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 http://{{ to }}$request_uri;
     }
@@ -27,6 +32,11 @@ server {
     # SSL common conf
     include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 https://{{ to }}$request_uri;
     }
diff --git a/roles/nginx/templates/nginx/sites-available/service.j2 b/roles/nginx/templates/nginx/sites-available/service.j2
index b44a4d53..7c7244ab 100644
--- a/roles/nginx/templates/nginx/sites-available/service.j2
+++ b/roles/nginx/templates/nginx/sites-available/service.j2
@@ -27,6 +27,11 @@ server {
     # Hide Nginx version
     server_tokens off;
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 https://{{ nginx.default_ssl_server }}$request_uri;
     }
@@ -45,6 +50,11 @@ server {
     # Hide Nginx version
     server_tokens off;
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 http://{{ nginx.default_server }}$request_uri;
     }
@@ -64,6 +74,11 @@ server {
     # Hide Nginx version
     server_tokens off;
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     location / {
         return 302 https://$host$request_uri;
     }
@@ -86,6 +101,11 @@ server {
     # Hide Nginx version
     server_tokens off;
 
+{% for realip in nginx.real_ip_from %}
+    set_real_ip_from {{ realip }};
+{% endfor %}
+    real_ip_header P-Real-Ip;
+
     {% if server.root is defined %}root {{ server.root }};{% endif %}
     {% if server.index is defined %}index {{ server.index|join(" ") }};{% endif %}
 
-- 
GitLab


From d6f15d421000955dcfe0c8ed7653085e65d88725 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 19:11:47 +0100
Subject: [PATCH 43/49] [nginx/cas] Factorize nginx configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/django_cas.yml                     | 37 ++++++++++++++-----
 hosts                                         |  1 +
 plays/cas.yml                                 |  2 +
 roles/django-cas/handlers/main.yml            |  5 ---
 roles/django-cas/tasks/main.yml               | 15 --------
 .../templates/nginx/sites-available/cas.j2    | 26 -------------
 roles/roundcube/handlers/main.yml             |  5 ---
 7 files changed, 30 insertions(+), 61 deletions(-)
 delete mode 100644 roles/django-cas/templates/nginx/sites-available/cas.j2
 delete mode 100644 roles/roundcube/handlers/main.yml

diff --git a/group_vars/django_cas.yml b/group_vars/django_cas.yml
index ffed7ace..a576a8c7 100644
--- a/group_vars/django_cas.yml
+++ b/group_vars/django_cas.yml
@@ -2,13 +2,6 @@
 glob_django_cas:
   repo: 'http://gitlab.adm.crans.org/nounous/django-cas.git'
   path: '/var/local/django-cas'
-  url:
-    - cas.crans.org
-    - cas.adm.crans.org
-    - login.crans.org
-    - login.adm.crans.org
-    - auth.crans.org
-    - auth.adm.crans.org
   ldap:
     dn: 'cn=Utilisateurs,dc=crans,dc=org'
     password: "{{ vault.cas_ldap_password }}"
@@ -18,6 +11,30 @@ glob_django_cas:
     host: tealc.adm.crans.org
     password: "{{ vault.cas_database_password }}"
   secret_key: "{{ vault.cas_secret_key }}"
-  reverse_proxy:
-    - '10.231.136.0/24'
-    - '2a0c:700:0:2::/64'
+
+loc_nginx:
+  service_name: "cas"
+  ssl: []
+  servers:
+    - server_name:
+        - cas.crans.org
+        - cas.adm.crans.org
+        - login.crans.org
+        - login.adm.crans.org
+        - auth.crans.org
+        - auth.adm.crans.org
+      default: true
+      locations:
+        - filter: "/cas"
+          params:
+            - "rewrite ^/cas$ / redirect"
+            - "rewrite ^/cas/(.*)$ /$1 redirect"
+
+        - filter: "/static"
+          params:
+            - "alias /var/local/django-cas/cas/local_static"
+
+        - filter: "/"
+          params:
+            - "uwsgi_pass unix:///var/run/uwsgi/app/cas/socket"
+            - "include uwsgi_params"
diff --git a/hosts b/hosts
index a6380b61..a42cb4f7 100644
--- a/hosts
+++ b/hosts
@@ -88,6 +88,7 @@ monitoring.adm.crans.org
 charybde.adm.crans.org
 
 [nginx:children]
+django_cas
 mailman
 reverseproxy
 roundcube
diff --git a/plays/cas.yml b/plays/cas.yml
index a55a8ab1..634f03e4 100755
--- a/plays/cas.yml
+++ b/plays/cas.yml
@@ -5,5 +5,7 @@
 - hosts: django_cas
   vars:
     django_cas: "{{ glob_django_cas | default({}) | combine(loc_django_cas | default({})) }}"
+    nginx: "{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}"
   roles:
     - django-cas
+    - nginx
diff --git a/roles/django-cas/handlers/main.yml b/roles/django-cas/handlers/main.yml
index fe8fbf15..ba46876d 100644
--- a/roles/django-cas/handlers/main.yml
+++ b/roles/django-cas/handlers/main.yml
@@ -1,9 +1,4 @@
 ---
-- name: Restart nginx
-  service:
-    name: nginx
-    state: restarted
-
 - name: Restart uwsgi
   service:
     name: uwsgi
diff --git a/roles/django-cas/tasks/main.yml b/roles/django-cas/tasks/main.yml
index 3b40472c..cc854db1 100644
--- a/roles/django-cas/tasks/main.yml
+++ b/roles/django-cas/tasks/main.yml
@@ -3,7 +3,6 @@
   apt:
     update_cache: true
     name:
-      - nginx
       - uwsgi
       - uwsgi-plugin-python3
       - python3-django
@@ -30,20 +29,6 @@
     owner: www-data
   notify: Restart uwsgi
 
-- name: Configure NGINX site
-  template:
-    src: nginx/sites-available/cas.j2
-    dest: /etc/nginx/sites-available/cas
-    mode: 0644
-  notify: Restart nginx
-
-- name: Enable nginx site
-  file:
-    src: /etc/nginx/sites-available/cas
-    dest: /etc/nginx/sites-enabled/cas
-    state: link
-  notify: Restart nginx
-
 - name: Configure UWSGI app
   template:
     src: uwsgi/apps-available/cas.ini.j2
diff --git a/roles/django-cas/templates/nginx/sites-available/cas.j2 b/roles/django-cas/templates/nginx/sites-available/cas.j2
deleted file mode 100644
index 2372ae92..00000000
--- a/roles/django-cas/templates/nginx/sites-available/cas.j2
+++ /dev/null
@@ -1,26 +0,0 @@
-{{ ansible_header | comment }}
-
-server {
-     server_name {{ django_cas.url | join(' ') }};
-     listen 80;
-     listen [::]:80;
-
-     location /cas {
-         rewrite ^/cas$ / redirect;
-         rewrite ^/cas/(.*)$ /$1 redirect;
-     }
-
-     location /static {
-         alias {{ django_cas.path }}/cas/local_static;
-     }
-
-{% for ip in django_cas.reverse_proxy | default([]) %}
-     set_real_ip_from {{ ip }};
-{% endfor %}
-     real_ip_header P-Real-Ip;
-
-     location / {
-         uwsgi_pass unix:///var/run/uwsgi/app/cas/socket;
-         include uwsgi_params;
-     }
-}
diff --git a/roles/roundcube/handlers/main.yml b/roles/roundcube/handlers/main.yml
deleted file mode 100644
index 2e593d34..00000000
--- a/roles/roundcube/handlers/main.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-- name: Restart nginx
-  service:
-    name: nginx
-    state: restarted
-- 
GitLab


From 6b8fb0916fb2e79a05fb062e1ce011fd5a62fbf7 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 21:22:07 +0100
Subject: [PATCH 44/49] [nginx/moinmoin] Extract nginx configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/nginx.yml                          |  2 +-
 group_vars/wiki.yml                           | 37 +++++++++++
 host_vars/kiwi.adm.crans.org.yml              |  2 +-
 host_vars/sputnik.adm.crans.org               |  5 --
 host_vars/sputnik.adm.crans.org.yml           | 64 ++++++++++++++++++-
 hosts                                         |  8 +++
 plays/moinmoin.yml                            | 12 +++-
 roles/moinmoin/handlers/main.yml              |  5 --
 roles/moinmoin/tasks/main.yml                 | 13 ----
 .../templates/nginx/sites-available/wiki.j2   | 31 ---------
 10 files changed, 121 insertions(+), 58 deletions(-)
 create mode 100644 group_vars/wiki.yml
 delete mode 100644 host_vars/sputnik.adm.crans.org
 delete mode 100644 roles/moinmoin/templates/nginx/sites-available/wiki.j2

diff --git a/group_vars/nginx.yml b/group_vars/nginx.yml
index 774fa0e1..e2868541 100644
--- a/group_vars/nginx.yml
+++ b/group_vars/nginx.yml
@@ -28,5 +28,5 @@ glob_nginx:
   default_ssl_domain: crans.org
   real_ip_from:
     - "172.16.0.0/16"
-    - "2a0c:700:0:2::/64"
+    - "fd00:0:0:10::/64"
   deploy_robots_file: false
diff --git a/group_vars/wiki.yml b/group_vars/wiki.yml
new file mode 100644
index 00000000..310fe049
--- /dev/null
+++ b/group_vars/wiki.yml
@@ -0,0 +1,37 @@
+---
+glob_moinmoin:
+  main: false
+
+loc_nginx:
+  service_name: wiki
+  ssl: []
+  servers:
+    - server_name: "{{ query('ldap', 'ip', ansible_hostname, 'adm') | ipwrap + [ansible_hostname, ansible_hostname + '.adm.crans.org'] }}"
+      default: true
+      access_log: "/var/log/nginx/wiki.log combined"
+      error_log: "/var/log/nginx/wiki.error.log"
+      additional_params:
+        - "rewrite ^/$ $scheme://wiki.crans.org/PageAccueil"
+        - "client_max_body_size 15M"
+
+      locations:
+        - filter: "/wiki"
+          params:
+            - "alias /var/local/wiki/htdocs/"
+
+        - filter: "/robots.txt"
+          params:
+            - "alias /var/local/wiki/robots.txt"
+
+        - filter: "/favicon.ico"
+          params:
+            - "/var/local/wiki/favicon.ico"
+
+        - filter: "/www-sitemap.xml"
+          params:
+            - "alias /var/local/wiki/www-sitemap.xml"
+
+        - filter: "/"
+          params:
+            - "uwsgi_pass unix:///var/run/uwsgi/app/moinmoin/socket"
+            - "include uwsgi_params"
diff --git a/host_vars/kiwi.adm.crans.org.yml b/host_vars/kiwi.adm.crans.org.yml
index 162f1944..5ed64596 100644
--- a/host_vars/kiwi.adm.crans.org.yml
+++ b/host_vars/kiwi.adm.crans.org.yml
@@ -31,5 +31,5 @@ to_backup:
   read_only: "yes",
   }
 
-moinmoin:
+loc_moinmoin:
   main: true
diff --git a/host_vars/sputnik.adm.crans.org b/host_vars/sputnik.adm.crans.org
deleted file mode 100644
index 2878a578..00000000
--- a/host_vars/sputnik.adm.crans.org
+++ /dev/null
@@ -1,5 +0,0 @@
----
-loc_slapd:
-  ip: "{{ query('ldap', 'ip', 'sputnik', 'adm') | ipv4 | first }}"
-  replica: true
-  replica_rid: 4
diff --git a/host_vars/sputnik.adm.crans.org.yml b/host_vars/sputnik.adm.crans.org.yml
index 6b2473f1..c0aa02b8 100644
--- a/host_vars/sputnik.adm.crans.org.yml
+++ b/host_vars/sputnik.adm.crans.org.yml
@@ -23,5 +23,67 @@ to_backup:
   hosts_allow: ["zephir.adm.crans.org", "10.231.136.6", "172.31.0.1"],
   }
 
-moinmoin:
+loc_slapd:
+  ip: "{{ query('ldap', 'ip', 'sputnik', 'adm') | ipv4 | first }}"
+  replica: true
+  replica_rid: 4
+
+loc_moinmoin:
   main: false
+
+loc_certbot:
+  - dns_rfc2136_server: '172.16.10.147'
+    dns_rfc2136_name: certbot_adm_challenge.
+    dns_rfc2136_secret: "{{ vault.certbot_adm_dns_secret }}"
+    mail: root@crans.org
+    certname: adm.crans.org
+    domains: "*.adm.crans.org"
+  - dns_rfc2136_server: '172.16.10.147'
+    dns_rfc2136_name: certbot_challenge.
+    dns_rfc2136_secret: "{{ vault.certbot_dns_secret }}"
+    mail: root@crans.org
+    certname: crans.org
+    domains: "git2.crans.org, status.crans.org, wiki.crans.org"
+
+loc_nginx:
+  service_name: wiki
+  ssl:
+    - name: adm.crans.org
+      cert: /etc/letsencrypt/live/adm.crans.org/fullchain.pem
+      cert_key: /etc/letsencrypt/live/adm.crans.org/privkey.pem
+      trusted_cert: /etc/letsencrypt/live/adm.crans.org/chain.pem
+    - name: crans.org
+      cert: /etc/letsencrypt/live/crans.org/fullchain.pem
+      cert_key: /etc/letsencrypt/live/crans.org/privkey.pem
+      trusted_cert: /etc/letsencrypt/live/crans.org/chain.pem
+  servers:
+    - server_name:
+        - "wiki2.crans.org"
+      ssl : "crans.org"
+      access_log: "/var/log/nginx/wiki.log combined"
+      error_log: "/var/log/nginx/wiki.error.log"
+      additional_params:
+        - "rewrite ^/$ $scheme://wiki2.crans.org/PageAccueil"
+        - "client_max_body_size 15M"
+
+      locations:
+        - filter: "/wiki"
+          params:
+            - "alias /var/local/wiki/htdocs/"
+
+        - filter: "/robots.txt"
+          params:
+            - "alias /var/local/wiki/robots.txt"
+
+        - filter: "/favicon.ico"
+          params:
+            - "/var/local/wiki/favicon.ico"
+
+        - filter: "/www-sitemap.xml"
+          params:
+            - "alias /var/local/wiki/www-sitemap.xml"
+
+        - filter: "/"
+          params:
+            - "uwsgi_pass unix:///var/run/uwsgi/app/moinmoin/socket"
+            - "include uwsgi_params"
diff --git a/hosts b/hosts
index a42cb4f7..59945c46 100644
--- a/hosts
+++ b/hosts
@@ -20,6 +20,9 @@ tealc.adm.crans.org
 [belenios]
 belenios.adm.crans.org
 
+[certbot]
+sputnik.adm.crans.org
+
 [certbot:children]
 dovecot
 git
@@ -93,6 +96,7 @@ mailman
 reverseproxy
 roundcube
 thelounge
+wiki
 
 [ntp_server]
 charybde.adm.crans.org
@@ -136,6 +140,10 @@ daniel.adm.crans.org
 jack.adm.crans.org
 sam.adm.crans.org
 
+[wiki]
+kiwi.adm.crans.org
+sputnik.adm.crans.org
+
 [crans_routeurs:children]
 # dhcp  TODO: Really needed ?
 # keepalived
diff --git a/plays/moinmoin.yml b/plays/moinmoin.yml
index b9c63047..35207855 100755
--- a/plays/moinmoin.yml
+++ b/plays/moinmoin.yml
@@ -1,6 +1,16 @@
 #!/usr/bin/env ansible-playbook
 ---
+- hosts: certbot:&wiki
+  vars:
+    certbot: '{{ loc_certbot | default(glob_certbot | default([])) }}'
+  roles:
+    - certbot
+
 # Deploy MoinMoin Wiki
-- hosts: kiwi.adm.crans.org,soyouz.adm.crans.org,sputnik.adm.crans.org
+- hosts: wiki
+  vars:
+    moinmoin: '{{ glob_moinmoin | default({}) | combine(loc_moinmoin | default({})) }}'
+    nginx: '{{ glob_nginx | default({}) | combine(loc_nginx | default({})) }}'
   roles:
     - moinmoin
+    - nginx
diff --git a/roles/moinmoin/handlers/main.yml b/roles/moinmoin/handlers/main.yml
index ea116cb8..ba46876d 100644
--- a/roles/moinmoin/handlers/main.yml
+++ b/roles/moinmoin/handlers/main.yml
@@ -3,8 +3,3 @@
   service:
     name: uwsgi
     state: restarted
-
-- name: Restart nginx
-  service:
-    name: nginx
-    state: restarted
diff --git a/roles/moinmoin/tasks/main.yml b/roles/moinmoin/tasks/main.yml
index 50049b03..bef5dc51 100644
--- a/roles/moinmoin/tasks/main.yml
+++ b/roles/moinmoin/tasks/main.yml
@@ -40,19 +40,6 @@
     enabled: true
     state: started
 
-- name: Configure nginx
-  template:
-    src: nginx/sites-available/wiki.j2
-    dest: /etc/nginx/sites-available/wiki
-  notify: Restart nginx
-
-- name: Activate nginx site
-  file:
-    src: /etc/nginx/sites-available/wiki
-    dest: /etc/nginx/sites-enabled/wiki
-    state: link
-  notify: Restart nginx
-
 - name: Indicate role in motd
   template:
     src: update-motd.d/05-service.j2
diff --git a/roles/moinmoin/templates/nginx/sites-available/wiki.j2 b/roles/moinmoin/templates/nginx/sites-available/wiki.j2
deleted file mode 100644
index 4c7482f0..00000000
--- a/roles/moinmoin/templates/nginx/sites-available/wiki.j2
+++ /dev/null
@@ -1,31 +0,0 @@
-{{ ansible_header | comment }}
-
-server {
-    listen 80;
-    listen [::]:80;
-    server_name wiki.adm.crans.org;
-
-    access_log /var/log/nginx/wiki.log combined;
-    error_log /var/log/nginx/wiki.error.log;
-
-    # Redirect to home page
-    rewrite ^/$ $scheme://wiki.crans.org/PageAccueil;
-
-    # Limit uploads
-    client_max_body_size 15M;
-
-    # MoinMoin paths
-    location /wiki/ { alias /var/local/wiki/htdocs/; }
-    location /robots.txt { alias /var/local/wiki/robots.txt; }
-    location /favicon.ico { alias /var/local/wiki/favicon.ico; }
-    location /www-sitemap.xml { alias /var/local/wiki/www-sitemap.xml; }
-
-    location / {
-        uwsgi_pass unix:///var/run/uwsgi/app/moinmoin/socket;
-        include uwsgi_params;
-    }
-
-    set_real_ip_from 172.16.10.0/24;
-    set_real_ip_from fd00:0:0:10::/64;
-    real_ip_header X-Real-Ip;
-}
-- 
GitLab


From 978c265c0326a92583e2918669cfdc7c13674779 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 21:31:14 +0100
Subject: [PATCH 45/49] [nginx/statping] Extract nginx configuration from
 statping and gitea

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 host_vars/sputnik.adm.crans.org.yml | 12 +++++++++++-
 hosts                               |  1 +
 roles/statping/handlers/main.yml    |  5 -----
 roles/statping/tasks/main.yml       | 13 -------------
 4 files changed, 12 insertions(+), 19 deletions(-)

diff --git a/host_vars/sputnik.adm.crans.org.yml b/host_vars/sputnik.adm.crans.org.yml
index c0aa02b8..17503bf2 100644
--- a/host_vars/sputnik.adm.crans.org.yml
+++ b/host_vars/sputnik.adm.crans.org.yml
@@ -43,7 +43,7 @@ loc_certbot:
     dns_rfc2136_secret: "{{ vault.certbot_dns_secret }}"
     mail: root@crans.org
     certname: crans.org
-    domains: "git2.crans.org, status.crans.org, wiki.crans.org"
+    domains: "*.crans.org"
 
 loc_nginx:
   service_name: wiki
@@ -87,3 +87,13 @@ loc_nginx:
           params:
             - "uwsgi_pass unix:///var/run/uwsgi/app/moinmoin/socket"
             - "include uwsgi_params"
+
+loc_reverseproxy:
+  reverseproxy_sites:
+    - {from: status.crans.org, to: "127.0.0.1:8080"}
+    - {from: git2.crans.org, to: "127.0.0.1:3000"}
+    - {from: git2.adm.crans.org, to: "127.0.0.1:3000", ssl: adm.crans.org}
+
+  redirect_sites: []
+
+  static_sites: []
diff --git a/hosts b/hosts
index 59945c46..abd29eb7 100644
--- a/hosts
+++ b/hosts
@@ -122,6 +122,7 @@ radius
 
 [reverseproxy]
 hodaur.adm.crans.org
+sputnik.adm.crans.org
 
 [roundcube]
 roundcube.adm.crans.org
diff --git a/roles/statping/handlers/main.yml b/roles/statping/handlers/main.yml
index c395b355..4868a5ce 100644
--- a/roles/statping/handlers/main.yml
+++ b/roles/statping/handlers/main.yml
@@ -1,9 +1,4 @@
 ---
-- name: Restart nginx
-  service:
-    name: nginx
-    state: restarted
-
 - name: Restart statping
   service:
     name: statping
diff --git a/roles/statping/tasks/main.yml b/roles/statping/tasks/main.yml
index bc7487ad..03578d70 100644
--- a/roles/statping/tasks/main.yml
+++ b/roles/statping/tasks/main.yml
@@ -29,19 +29,6 @@
     enabled: true
     state: started
 
-- name: Configure statping nginx site
-  template:
-    src: nginx/sites-available/status.j2
-    dest: /etc/nginx/sites-available/status
-  notify: Restart nginx
-
-- name: Activate statping nginx site
-  file:
-    src: /etc/nginx/sites-available/status
-    dest: /etc/nginx/sites-enabled/status
-    state: link
-  notify: Restart nginx
-
 - name: Indicate role in motd
   template:
     src: update-motd.d/05-service.j2
-- 
GitLab


From 661682c550201b25bbcdb9a1c9f5e89055ab5ffc Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 21:40:34 +0100
Subject: [PATCH 46/49] [nginx/moinmoin] Fix moinmoin configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/wiki.yml                 | 2 +-
 host_vars/sputnik.adm.crans.org.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/group_vars/wiki.yml b/group_vars/wiki.yml
index 310fe049..63b6a0fe 100644
--- a/group_vars/wiki.yml
+++ b/group_vars/wiki.yml
@@ -25,7 +25,7 @@ loc_nginx:
 
         - filter: "/favicon.ico"
           params:
-            - "/var/local/wiki/favicon.ico"
+            - "alias /var/local/wiki/favicon.ico"
 
         - filter: "/www-sitemap.xml"
           params:
diff --git a/host_vars/sputnik.adm.crans.org.yml b/host_vars/sputnik.adm.crans.org.yml
index 17503bf2..7e6ff41c 100644
--- a/host_vars/sputnik.adm.crans.org.yml
+++ b/host_vars/sputnik.adm.crans.org.yml
@@ -77,7 +77,7 @@ loc_nginx:
 
         - filter: "/favicon.ico"
           params:
-            - "/var/local/wiki/favicon.ico"
+            - "alias /var/local/wiki/favicon.ico"
 
         - filter: "/www-sitemap.xml"
           params:
-- 
GitLab


From f83b34191afcc33b257900a056938d8412ff4566 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 21:54:10 +0100
Subject: [PATCH 47/49] [nginx/statping] Drop old statping configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 .../templates/nginx/sites-available/status.j2     | 15 ---------------
 1 file changed, 15 deletions(-)
 delete mode 100644 roles/statping/templates/nginx/sites-available/status.j2

diff --git a/roles/statping/templates/nginx/sites-available/status.j2 b/roles/statping/templates/nginx/sites-available/status.j2
deleted file mode 100644
index 4eb80fed..00000000
--- a/roles/statping/templates/nginx/sites-available/status.j2
+++ /dev/null
@@ -1,15 +0,0 @@
-{{ ansible_header | comment }}
-
-server {
-    listen 80;
-    listen [::]:80;
-    server_name status.crans.org;
-
-    access_log /var/log/nginx/status.log combined;
-    error_log /var/log/nginx/status.error.log;
-
-    location / {
-        proxy_pass http://127.0.0.1:8080;
-        proxy_redirect off;
-    }
-}
-- 
GitLab


From 28a6fd4be68017f92d02816e68b91fb4dfc2a701 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 22:24:09 +0100
Subject: [PATCH 48/49] [thelounge] Rename thelounge ldap password

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/thelounge.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/group_vars/thelounge.yml b/group_vars/thelounge.yml
index 67a0ab6d..66132cd1 100644
--- a/group_vars/thelounge.yml
+++ b/group_vars/thelounge.yml
@@ -19,7 +19,7 @@ glob_thelounge:
     url: "ldap://172.16.10.157"
     primaryKey: "cn"
     rootDN: "cn=thelounge,ou=service-users,dc=crans,dc=org"
-    rootPassword: "{{ vault.ldap_thelounge_password }}"
+    rootPassword: "{{ vault.ldap_thelounge }}"
     filter: "(objectclass=inetOrgPerson)"
     base: "dc=crans,dc=org"
     scope: "sub"
-- 
GitLab


From 6d35dcd7e836b2e59367249ec260c028697dbae0 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Mon, 22 Feb 2021 23:23:18 +0100
Subject: [PATCH 49/49] [nginx/mailman] Fix configuration

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
---
 group_vars/mailman.yml                         | 10 +++++-----
 host_vars/redisdead.adm.crans.org.yml          |  8 ++++++++
 hosts                                          |  1 +
 roles/mailman/tasks/main.yml                   |  8 ++++++++
 .../nginx/snippets/fastcgi-mailman.conf.j2     | 18 ++++++++++++++++++
 roles/nginx/templates/nginx/passwd.j2          |  2 +-
 roles/nginx/templates/www/html/robots.txt.j2   |  2 --
 7 files changed, 41 insertions(+), 8 deletions(-)
 create mode 100644 roles/mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2

diff --git a/group_vars/mailman.yml b/group_vars/mailman.yml
index 115215fa..fe7a0de7 100644
--- a/group_vars/mailman.yml
+++ b/group_vars/mailman.yml
@@ -18,21 +18,21 @@ loc_nginx:
         - filter: "/error/"
           params:
             - "internal"
-            - "alias /var/www/html"
+            - "alias /var/www/html/"
         - filter: "/create"
           params:
             - "default_type text/html"
-            - "alias /etc/mailman/create.txt"
+            - "alias /etc/mailman/create.html"
         - filter: "~ ^/$"
           params:
             - "return 302 https://lists.crans.org/listinfo"
         - filter: "/"
           params:
-            - "include \"/etc/nginx/snippets/fastcgi.conf\""
+            - "include \"/etc/nginx/snippets/fastcgi-mailman.conf\""
         - filter: "~ ^/listinfo"
           params:
             - "satisfy any"
-            - "include \"/etc/nginx/snippets/fastcgi.conf\""
+            - "include \"/etc/nginx/snippets/fastcgi-mailman.conf\""
             - "allow 185.230.76.0/22"
             - "allow 2a0c:700:0::/40"
             - "deny all"
@@ -42,7 +42,7 @@ loc_nginx:
         - filter: "~ ^/admin"
           params:
             - "satisfy any"
-            - "include \"/etc/nginx/snippets/fastcgi.conf\""
+            - "include \"/etc/nginx/snippets/fastcgi-mailman.conf\""
             - "allow 185.230.76.0/22"
             - "allow 2a0c:700:0::/40"
             - "deny all"
diff --git a/host_vars/redisdead.adm.crans.org.yml b/host_vars/redisdead.adm.crans.org.yml
index 8228a1d0..f562ec36 100644
--- a/host_vars/redisdead.adm.crans.org.yml
+++ b/host_vars/redisdead.adm.crans.org.yml
@@ -33,3 +33,11 @@ to_backup:
   secrets_file: "/etc/rsyncd.secrets",
   hosts_allow: ["zephir.adm.crans.org", "10.231.136.6"],
   }
+
+loc_certbot:
+  - dns_rfc2136_server: '172.16.10.147'
+    dns_rfc2136_name: certbot_challenge.
+    dns_rfc2136_secret: "{{ vault.certbot_dns_secret }}"
+    mail: root@crans.org
+    certname: crans.org
+    domains: "*.crans.org"
diff --git a/hosts b/hosts
index abd29eb7..e66ffb17 100644
--- a/hosts
+++ b/hosts
@@ -26,6 +26,7 @@ sputnik.adm.crans.org
 [certbot:children]
 dovecot
 git
+mailman
 radius  # We use certbot to manage LE certificates
 reverseproxy
 thelounge
diff --git a/roles/mailman/tasks/main.yml b/roles/mailman/tasks/main.yml
index 467ef9f0..9a74a41e 100644
--- a/roles/mailman/tasks/main.yml
+++ b/roles/mailman/tasks/main.yml
@@ -19,6 +19,14 @@
     - create.html
   notify: Reload mailman
 
+- name: Deploy mailman snippet
+  template:
+    src: "nginx/snippets/fastcgi-mailman.conf.j2"
+    dest: "/etc/nginx/snippets/fastcgi-mailman.conf"
+    owner: root
+    group: root
+    mode: 0644
+
 # Fanciness
 - name: Deploy custom logo
   copy:
diff --git a/roles/mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2 b/roles/mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2
new file mode 100644
index 00000000..d3215c7f
--- /dev/null
+++ b/roles/mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2
@@ -0,0 +1,18 @@
+{{ ansible_header | comment }}
+
+# regex to split $uri to $fastcgi_script_name and $fastcgi_path
+fastcgi_split_path_info (^/[^/]*)(.*)$;
+
+# check that the PHP script exists before passing it
+try_files $fastcgi_script_name =404;
+
+# Bypass the fact that try_files resets $fastcgi_path_info
+# see: http://trac.nginx.org/nginx/ticket/321
+set $path_info $fastcgi_path_info;
+fastcgi_param PATH_INFO $path_info;
+
+# Let NGINX handle errors
+fastcgi_intercept_errors on;
+
+include /etc/nginx/fastcgi.conf;
+fastcgi_pass unix:/var/run/fcgiwrap.socket;
diff --git a/roles/nginx/templates/nginx/passwd.j2 b/roles/nginx/templates/nginx/passwd.j2
index e87369c9..75d0ff7c 100644
--- a/roles/nginx/templates/nginx/passwd.j2
+++ b/roles/nginx/templates/nginx/passwd.j2
@@ -1,4 +1,4 @@
 {{ ansible_header | comment }}
 {% for user, hash in nginx.auth_passwd.items() -%}
-{{ user }}: {{ hash }}
+{{ user }}:{{ hash }}
 {% endfor -%}
diff --git a/roles/nginx/templates/www/html/robots.txt.j2 b/roles/nginx/templates/www/html/robots.txt.j2
index 3fbaed74..1f53798b 100644
--- a/roles/nginx/templates/www/html/robots.txt.j2
+++ b/roles/nginx/templates/www/html/robots.txt.j2
@@ -1,4 +1,2 @@
-{{ ansible_header | comment }}
-
 User-agent: *
 Disallow: /
-- 
GitLab