From 81f8cdfb7bb27ddec18b9d55a0df872c2281c49c Mon Sep 17 00:00:00 2001
From: Jeremy Stanley <fungi@yuggoth.org>
Date: Fri, 17 Dec 2021 19:48:51 +0000
Subject: [PATCH] Add HTTPS vhosts to mailman servers

Add secondary vhosts for HTTPS to each mailman site, but don't
remove the plain HTTP ones for now. Before switching to Mailman 3
we'll replace the current HTTP vhosts with blanket redirects to
HTTPS.

Add tests to make sure this is working, and also add a command-line
test for the lists.openinfra.dev site now that it's got a first
non-default list of its own. Also collect Apache logs from the test
nodes so we can see for sure what might break.

Change-Id: I4d93d643381f17c9a968595587909f0ba3dd6f92
---
 inventory/service/group_vars/mailman.yaml     |  1 +
 .../templates/mailman_multihost.vhost.j2      | 49 +++++++++++++++++
 .../roles/mailman/templates/mailman.vhost.j2  | 48 +++++++++++++++++
 testinfra/test_lists_k_i.py                   | 10 ++++
 testinfra/test_lists_o_o.py                   | 54 ++++++++++++++++++-
 zuul.d/system-config-run.yaml                 |  2 +
 6 files changed, 163 insertions(+), 1 deletion(-)

diff --git a/inventory/service/group_vars/mailman.yaml b/inventory/service/group_vars/mailman.yaml
index a3a089e0d7..72849c2d84 100644
--- a/inventory/service/group_vars/mailman.yaml
+++ b/inventory/service/group_vars/mailman.yaml
@@ -5,4 +5,5 @@ exim_smtp_accept_max_per_host: '10'
 iptables_extra_public_tcp_ports:
   - 25
   - 80
+  - 443
   - 465
diff --git a/playbooks/roles/mailman-site/templates/mailman_multihost.vhost.j2 b/playbooks/roles/mailman-site/templates/mailman_multihost.vhost.j2
index 634b9c7ee1..b834c276b7 100644
--- a/playbooks/roles/mailman-site/templates/mailman_multihost.vhost.j2
+++ b/playbooks/roles/mailman-site/templates/mailman_multihost.vhost.j2
@@ -12,6 +12,7 @@
 	DocumentRoot /var/www
 
 RewriteEngine on
+# TODO(fungi): convert this vhost into a blanket redirect to HTTPS when ready
 RewriteRule ^/$ /cgi-bin/mailman/listinfo [R]
 
 # We can find mailman here:
@@ -60,3 +61,51 @@ Alias /images/mailman/ /usr/share/images/mailman/
 </Directory>
 
 </VirtualHost>
+
+<VirtualHost *:443>
+  ServerName {{ mailman_site.listdomain }}
+  ServerAdmin webmaster@openstack.org
+  ErrorLog ${APACHE_LOG_DIR}/{{ mailman_site.listdomain }}-ssl-error.log
+  LogLevel warn
+  CustomLog ${APACHE_LOG_DIR}/{{ mailman_site.listdomain }}-ssl-access.log combined
+
+  SSLEngine on
+  SSLProtocol All -SSLv2 -SSLv3
+  # Note: this list should ensure ciphers that provide forward secrecy
+  SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!AES256:!aNULL:!eNULL:!MD5:!DSS:!PSK:!SRP
+  SSLHonorCipherOrder on
+
+  SSLCertificateFile /etc/letsencrypt-certs/{{ inventory_hostname }}/{{ inventory_hostname }}.cer
+  SSLCertificateKeyFile /etc/letsencrypt-certs/{{ inventory_hostname }}/{{ inventory_hostname }}.key
+  SSLCertificateChainFile /etc/letsencrypt-certs/{{ inventory_hostname }}/ca.cer
+
+  RewriteEngine on
+  RewriteRule ^/$ /cgi-bin/mailman/listinfo [R]
+
+  ScriptAlias /cgi-bin/mailman/ /usr/lib/cgi-bin/mailman/
+  Alias /pipermail/ /srv/mailman/{{ mailman_site.name }}/archives/public/
+  Alias /images/mailman/ /usr/share/images/mailman/
+
+  <Directory /usr/lib/cgi-bin/mailman/>
+    AllowOverride None
+    Options ExecCGI
+    AddHandler cgi-script .cgi
+    SetEnv HOST {{ mailman_site.listdomain }}
+    Order allow,deny
+    Allow from all
+    Require all granted
+  </Directory>
+  <Directory /srv/mailman/{{ mailman_site.name }}/archives/public/>
+    Options FollowSymlinks
+    AllowOverride None
+    Order allow,deny
+    Allow from all
+    Require all granted
+  </Directory>
+  <Directory /usr/share/images/mailman/>
+    AllowOverride None
+    Order allow,deny
+    Allow from all
+    Require all granted
+  </Directory>
+</VirtualHost>
diff --git a/playbooks/roles/mailman/templates/mailman.vhost.j2 b/playbooks/roles/mailman/templates/mailman.vhost.j2
index eae8a32a83..5ef04c0328 100644
--- a/playbooks/roles/mailman/templates/mailman.vhost.j2
+++ b/playbooks/roles/mailman/templates/mailman.vhost.j2
@@ -12,6 +12,7 @@
 	DocumentRoot /var/www
 
 RewriteEngine on
+# TODO(fungi): convert this vhost into a blanket redirect to HTTPS when ready
 RewriteRule ^/$ /cgi-bin/mailman/listinfo [R]
 
 # We can find mailman here:
@@ -59,3 +60,50 @@ Alias /images/mailman/ /usr/share/images/mailman/
 </Directory>
 
 </VirtualHost>
+
+<VirtualHost *:443>
+  ServerName {{ mailman_listdomain }}
+  ServerAdmin webmaster@openstack.org
+  ErrorLog ${APACHE_LOG_DIR}/{{ mailman_listdomain }}-ssl-error.log
+  LogLevel warn
+  CustomLog ${APACHE_LOG_DIR}/{{ mailman_listdomain }}-ssl-access.log combined
+
+  SSLEngine on
+  SSLProtocol All -SSLv2 -SSLv3
+  # Note: this list should ensure ciphers that provide forward secrecy
+  SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!AES256:!aNULL:!eNULL:!MD5:!DSS:!PSK:!SRP
+  SSLHonorCipherOrder on
+
+  SSLCertificateFile /etc/letsencrypt-certs/{{ inventory_hostname }}/{{ inventory_hostname }}.cer
+  SSLCertificateKeyFile /etc/letsencrypt-certs/{{ inventory_hostname }}/{{ inventory_hostname }}.key
+  SSLCertificateChainFile /etc/letsencrypt-certs/{{ inventory_hostname }}/ca.cer
+
+  RewriteEngine on
+  RewriteRule ^/$ /cgi-bin/mailman/listinfo [R]
+
+  ScriptAlias /cgi-bin/mailman/ /usr/lib/cgi-bin/mailman/
+  Alias /pipermail/ /var/lib/mailman/archives/public/
+  Alias /images/mailman/ /usr/share/images/mailman/
+
+  <Directory /usr/lib/cgi-bin/mailman/>
+    AllowOverride None
+    Options ExecCGI
+    AddHandler cgi-script .cgi
+    Order allow,deny
+    Allow from all
+    Require all granted
+  </Directory>
+  <Directory /var/lib/mailman/archives/public/>
+    Options FollowSymlinks
+    AllowOverride None
+    Order allow,deny
+    Allow from all
+    Require all granted
+  </Directory>
+  <Directory /usr/share/images/mailman/>
+    AllowOverride None
+    Order allow,deny
+    Allow from all
+    Require all granted
+  </Directory>
+</VirtualHost>
diff --git a/testinfra/test_lists_k_i.py b/testinfra/test_lists_k_i.py
index 6584461f3f..a56b76d3cd 100644
--- a/testinfra/test_lists_k_i.py
+++ b/testinfra/test_lists_k_i.py
@@ -15,3 +15,13 @@ testinfra_hosts = ['lists.katacontainers.io']
 def test_mm_list_is_present(host):
     cmd = host.run('list_lists --bare')
     assert 'kata-dev' in cmd.stdout
+
+def test_mm_list_site(host):
+    cmd = host.run('curl '
+                   '--resolve lists.katacontainers.io:80:127.0.0.1 '
+                   'http://lists.katacontainers.io/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.katacontainers.io Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl --insecure '
+                   '--resolve lists.katacontainers.io:443:127.0.0.1 '
+                   'https://lists.katacontainers.io/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.katacontainers.io Mailing Lists</TITLE>' in cmd.stdout
diff --git a/testinfra/test_lists_o_o.py b/testinfra/test_lists_o_o.py
index a38926b640..24ea24d0c9 100644
--- a/testinfra/test_lists_o_o.py
+++ b/testinfra/test_lists_o_o.py
@@ -19,6 +19,9 @@ def test_mm_list_is_present(host):
     cmd = host.run('HOST=lists.opendev.org list_lists --bare')
     assert 'service-discuss' in cmd.stdout
 
+    cmd = host.run('HOST=lists.openinfra.dev list_lists --bare')
+    assert 'staff' in cmd.stdout
+
     cmd = host.run('HOST=lists.openstack.org list_lists --bare')
     assert 'openstack-discuss' in cmd.stdout
 
@@ -28,8 +31,57 @@ def test_mm_list_is_present(host):
     cmd = host.run('HOST=lists.zuul-ci.org list_lists --bare')
     assert 'zuul-discuss' in cmd.stdout
 
+def test_mm_list_site(host):
+    cmd = host.run('curl '
+                   '--resolve lists.airshipit.org:80:127.0.0.1 '
+                   'http://lists.airshipit.org/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.airshipit.org Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl --insecure '
+                   '--resolve lists.airshipit.org:443:127.0.0.1 '
+                   'https://lists.airshipit.org/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.airshipit.org Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl '
+                   '--resolve lists.opendev.org:80:127.0.0.1 '
+                   'http://lists.opendev.org/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.opendev.org Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl --insecure '
+                   '--resolve lists.opendev.org:443:127.0.0.1 '
+                   'https://lists.opendev.org/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.opendev.org Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl '
+                   '--resolve lists.openinfra.dev:80:127.0.0.1 '
+                   'http://lists.openinfra.dev/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.openinfra.dev Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl --insecure '
+                   '--resolve lists.openinfra.dev:443:127.0.0.1 '
+                   'https://lists.openinfra.dev/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.openinfra.dev Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl '
+                   '--resolve lists.openstack.org:80:127.0.0.1 '
+                   'http://lists.openstack.org/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.openstack.org Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl --insecure '
+                   '--resolve lists.openstack.org:443:127.0.0.1 '
+                   'https://lists.openstack.org/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.openstack.org Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl '
+                   '--resolve lists.starlingx.io:80:127.0.0.1 '
+                   'http://lists.starlingx.io/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.starlingx.io Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl --insecure '
+                   '--resolve lists.starlingx.io:443:127.0.0.1 '
+                   'https://lists.starlingx.io/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.starlingx.io Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl '
+                   '--resolve lists.zuul-ci.org:80:127.0.0.1 '
+                   'http://lists.zuul-ci.org/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.zuul-ci.org Mailing Lists</TITLE>' in cmd.stdout
+    cmd = host.run('curl --insecure '
+                   '--resolve lists.zuul-ci.org:443:127.0.0.1 '
+                   'https://lists.zuul-ci.org/cgi-bin/mailman/listinfo')
+    assert '<TITLE>lists.zuul-ci.org Mailing Lists</TITLE>' in cmd.stdout
+
 def test_domain_aliases(host):
     domain_aliases = host.file('/etc/aliases.domain')
     assert domain_aliases.exists
     assert domain_aliases.contains('staff@lists.openstack.org: staff@lists.openinfra.dev')
-
diff --git a/zuul.d/system-config-run.yaml b/zuul.d/system-config-run.yaml
index 159050e1f3..c40a832305 100644
--- a/zuul.d/system-config-run.yaml
+++ b/zuul.d/system-config-run.yaml
@@ -269,11 +269,13 @@
       lists.katacontainers.io:
         host_copy_output:
           '/var/log/acme.sh': logs
+          '/var/log/apache2': logs
           '/var/log/mailman': logs
       lists.openstack.org:
         host_copy_output:
           '/etc/aliases.domain': logs_txt
           '/var/log/acme.sh': logs
+          '/var/log/apache2': logs
           '/var/log/mailman': logs
 
 - job: