From d25277323c30f617bfaf055899541c1dea025326 Mon Sep 17 00:00:00 2001
From: jurgenhaas <juergen@paragon-es.de>
Date: Thu, 5 Mar 2020 11:39:25 +0100
Subject: [PATCH] Implement support for maintenance mode for individual domains

---
 files/maintenance.http       | 34 ++++++++++++++++++++++++++++++++++
 tasks/configure.yml          | 10 ++++++++++
 tasks/install.yml            |  1 +
 templates/haproxy_cfg.jinja2 | 10 ++++++++++
 templates/maintenance_list   |  3 +++
 5 files changed, 58 insertions(+)
 create mode 100644 files/maintenance.http
 create mode 100644 templates/maintenance_list

diff --git a/files/maintenance.http b/files/maintenance.http
new file mode 100644
index 0000000..c0364cb
--- /dev/null
+++ b/files/maintenance.http
@@ -0,0 +1,34 @@
+HTTP/1.0 503 Service Unavailable
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+
+<!doctype html>
+<!-- 503 Service Unavailable -->
+<html>
+  <title>Site Maintenance | Wartung</title>
+  <style>
+    body { text-align: center; padding: 150px; }
+    h1 { font-size: 50px; }
+    body { font: 20px Helvetica, sans-serif; color: #333; }
+    article { display: block; text-align: left; width: 650px; margin: 0 auto; }
+    a { color: #dc8100; text-decoration: none; }
+    a:hover { color: #333; text-decoration: none; }
+  </style>
+
+  <body>
+    <article>
+      <h1>We&rsquo;ll be back soon!</h1>
+      <div>
+        <p>Sorry for the inconvenience but we're performing some maintenance at the moment. We'll be back online shortly!</p>
+      </div>
+    </article>
+
+    <article>
+      <h1>Wir sind bald zur&uuml;ck!</h1>
+      <div>
+        <p>Wir f&uuml;hren derzeit einige Wartungsarbeiten durch und entschuldigen uns f&uuml;r die Unannehmlichkeiten. Wir sind bald wieder online!</p>
+      </div>
+    </article>
+  </body>
+</html>
diff --git a/tasks/configure.yml b/tasks/configure.yml
index b664a9c..7f19c54 100644
--- a/tasks/configure.yml
+++ b/tasks/configure.yml
@@ -16,6 +16,16 @@
   notify:
     - Check HAProxy Config
 
+- name: Create maintenance lists
+  template:
+    src: maintenance_list
+    dest: /etc/haproxy/maintenance.list
+    owner: root
+    group: root
+    mode: 0644
+  notify:
+    - Check HAProxy Config
+
 - name: Create host lists
   template:
     src: host_list
diff --git a/tasks/install.yml b/tasks/install.yml
index ebe15c9..a8d3d0b 100644
--- a/tasks/install.yml
+++ b/tasks/install.yml
@@ -66,6 +66,7 @@
     - '502'
     - '503'
     - '504'
+    - 'maintenance'
   tags:
     - errorfiles
   notify:
diff --git a/templates/haproxy_cfg.jinja2 b/templates/haproxy_cfg.jinja2
index 7544eee..110e09a 100644
--- a/templates/haproxy_cfg.jinja2
+++ b/templates/haproxy_cfg.jinja2
@@ -64,6 +64,7 @@ frontend http_in
 {% endfor %}
   http-request set-header x-routing-host undefined
   acl letsencrypt_challenge path_beg /.well-known/acme-challenge/
+  http-request set-header x-routing-host maintenance if !letsencrypt_challenge { hdr(x-routing-host) undefined } { hdr(host) -i -n -f /etc/haproxy/maintenance.list }
   http-request redirect code 301 location %[base,lower,map(/etc/haproxy/redirect.domain-and-path.map)] if !letsencrypt_challenge { base,lower,map(/etc/haproxy/redirect.domain-and-path.map) -m found }
   http-request redirect code 301 location %[capture.req.uri,lower,map(/etc/haproxy/redirect.path.map)] if !letsencrypt_challenge { capture.req.uri,lower,map(/etc/haproxy/redirect.path.map) -m found }
   http-request redirect code 301 location %[hdr(host),lower,map(/etc/haproxy/redirect.domain.map)] if !letsencrypt_challenge { hdr(host),lower,map(/etc/haproxy/redirect.domain.map) -m found }
@@ -141,6 +142,7 @@ frontend http_in
   http-request set-header x-routing-host {{ routing.default }} if !letsencrypt_challenge { hdr(x-routing-host) undefined } { hdr(host) -i -n {{ routing.domain }} }
 {% endif %}
   use_backend backend_letsencrypt if letsencrypt_challenge
+  use_backend backend_maintenance if { hdr(x-routing-host) maintenance }
 {% if kibana_domain is defined %}
   acl kibana_present hdr(host) -i -n '{{ kibana_domain|default(inventory_hostname) }}'
   use_backend backend_redirect_ssl if kibana_present
@@ -224,6 +226,7 @@ frontend https_in_{{ cert.ip }}
   http-request deny if { {{ rule }} }
 {% endfor %}
   http-request set-header x-routing-host undefined
+  http-request set-header x-routing-host maintenance if { hdr(x-routing-host) undefined } { hdr(host) -i -n -f /etc/haproxy/maintenance.list }
 {% for host in groups['all']|sort %}
 {% for redirect in hostvars[host].proxy_redirect|default([]) %}
 {% for from in redirect.from %}
@@ -283,6 +286,7 @@ frontend https_in_{{ cert.ip }}
 {% if routing is defined and routing.default is defined %}
   http-request set-header x-routing-host {{ routing.default }} if { hdr(x-routing-host) undefined } { hdr(host) -i -n {{ routing.domain }} }
 {% endif %}
+  use_backend backend_maintenance if { hdr(x-routing-host) maintenance }
 {% if kibana_domain is defined %}
   acl kibana_present hdr(host) -i -n '{{ kibana_domain|default(inventory_hostname) }}'
   use_backend backend_kibana if kibana_present
@@ -473,3 +477,9 @@ backend backend_kibana
 {% endif %}
   server kibana 127.0.0.1:5601 check maxconn 32
 {% endif %}
+
+backend backend_maintenance
+{% if proxy_debug %}
+  http-response set-header X-Proxy-Backend "maintenance"
+{% endif %}
+  errorfile 503 /etc/haproxy/errors/maintenance.http
diff --git a/templates/maintenance_list b/templates/maintenance_list
new file mode 100644
index 0000000..9501976
--- /dev/null
+++ b/templates/maintenance_list
@@ -0,0 +1,3 @@
+{% for domain in proxy_maintenance_domains|default([]) %}
+{{domain}}
+{% endfor %}
-- 
GitLab