Standardize HTTPS and vhost configuration

Adjust the SSL/TLS X.509 file handling to match our flexible
standard, which allows the deployer to choose between managing the
contents in or outside Puppet while still providing for a sane
snakeoil fallback to ease testing and development. Also tune the SSL
configuration to our typical stricter defaults, unconditionally
redirect all HTTP requests to HTTPS, and reformat the vhost
configuration for consistency and ease of future maintenance.

Change-Id: Id5241377665845f8c453cbb833bc40d3a5915d76
This commit is contained in:
Jeremy Stanley 2016-08-19 20:36:11 +00:00
parent 670312c04a
commit 4cbe39375c
2 changed files with 216 additions and 138 deletions

View File

@ -5,12 +5,13 @@ class mediawiki(
$mediawiki_images_location = '/srv/mediawiki/images', $mediawiki_images_location = '/srv/mediawiki/images',
$role = 'all', $role = 'all',
$site_hostname = $::fqdn, $site_hostname = $::fqdn,
$ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem', $serveradmin = "webmaster@${::fqdn}",
$ssl_cert_file_contents = undef, # If left empty puppet will not create file. $ssl_cert_file = undef,
$ssl_cert_file_contents = undef,
$ssl_chain_file = undef, $ssl_chain_file = undef,
$ssl_chain_file_contents = undef, # If left empty puppet will not create file. $ssl_chain_file_contents = undef,
$ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key', $ssl_key_file = undef,
$ssl_key_file_contents = undef, # If left empty puppet will not create file. $ssl_key_file_contents = undef,
$wg_recaptchapublickey = undef, $wg_recaptchapublickey = undef,
$wg_recaptchaprivatekey = undef, $wg_recaptchaprivatekey = undef,
$wg_googleanalyticsaccount = undef, $wg_googleanalyticsaccount = undef,
@ -95,42 +96,129 @@ class mediawiki(
ensure => present, ensure => present,
} }
if $ssl_cert_file_contents != undef { # To use the standard ssl-certs package snakeoil certificate, leave both
file { $ssl_cert_file: # $ssl_cert_file and $ssl_cert_file_contents empty. To use an existing
owner => 'root', # certificate, specify its path for $ssl_cert_file and leave
group => 'root', # $ssl_cert_file_contents empty. To manage the certificate with puppet,
mode => '0640', # provide $ssl_cert_file_contents and optionally specify the path to use for
content => $ssl_cert_file_contents, # it in $ssl_cert_file.
before => Httpd::Vhost[$site_hostname], if ($ssl_cert_file == undef) and ($ssl_cert_file_contents == undef) {
$cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
if ! defined(Package['ssl-cert']) {
package { 'ssl-cert':
ensure => present,
before => Httpd::Vhost[$site_hostname],
}
}
} else {
if $ssl_cert_file == undef {
$cert_file = "/etc/ssl/certs/${::fqdn}.pem"
if ! defined(File['/etc/ssl/certs']) {
file { '/etc/ssl/certs':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
before => File[$cert_file],
}
}
} else {
$cert_file = $ssl_cert_file
}
if $ssl_cert_file_contents != undef {
file { $cert_file:
ensure => present,
owner => 'root',
group => 'root',
mode => '0644',
content => $ssl_cert_file_contents,
before => Httpd::Vhost[$site_hostname],
}
} }
} }
if $ssl_key_file_contents != undef { # To avoid using an intermediate certificate chain, leave both
file { $ssl_key_file: # $ssl_chain_file and $ssl_chain_file_contents empty. To use an existing
owner => 'root', # chain, specify its path for $ssl_chain_file and leave
group => 'ssl-cert', # $ssl_chain_file_contents empty. To manage the chain with puppet, provide
mode => '0640', # $ssl_chain_file_contents and optionally specify the path to use for it in
content => $ssl_key_file_contents, # $ssl_chain_file.
before => Httpd::Vhost[$site_hostname], if ($ssl_chain_file == undef) and ($ssl_chain_file_contents == undef) {
$chain_file = undef
} else {
if $ssl_chain_file == undef {
$chain_file = "/etc/ssl/certs/${::fqdn}_intermediate.pem"
if ! defined(File['/etc/ssl/certs']) {
file { '/etc/ssl/certs':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
before => File[$chain_file],
}
}
} else {
$chain_file = $ssl_chain_file
}
if $ssl_chain_file_contents != undef {
file { $chain_file:
ensure => present,
owner => 'root',
group => 'root',
mode => '0644',
content => $ssl_chain_file_contents,
before => Httpd::Vhost[$site_hostname],
}
} }
} }
if $ssl_chain_file_contents != undef { # To use the standard ssl-certs package snakeoil key, leave both
file { $ssl_chain_file: # $ssl_key_file and $ssl_key_file_contents empty. To use an existing key,
owner => 'root', # specify its path for $ssl_key_file and leave $ssl_key_file_contents empty.
group => 'root', # To manage the key with puppet, provide $ssl_key_file_contents and
mode => '0640', # optionally specify the path to use for it in $ssl_key_file.
content => $ssl_chain_file_contents, if ($ssl_key_file == undef) and ($ssl_key_file_contents == undef) {
before => Httpd::Vhost[$site_hostname], $key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
if ! defined(Package['ssl-cert']) {
package { 'ssl-cert':
ensure => present,
before => Httpd::Vhost[$site_hostname],
}
}
} else {
if $ssl_key_file == undef {
$key_file = "/etc/ssl/private/${::fqdn}.key"
if ! defined(File['/etc/ssl/private']) {
file { '/etc/ssl/private':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0700',
before => File[$key_file],
}
}
} else {
$key_file = $ssl_key_file
}
if $ssl_key_file_contents != undef {
file { $key_file:
ensure => present,
owner => 'root',
group => 'root',
mode => '0600',
content => $ssl_key_file_contents,
before => Httpd::Vhost[$site_hostname],
}
} }
} }
::httpd::vhost { $site_hostname: ::httpd::vhost { $site_hostname:
port => 443, port => 443, # Is required despite not being used.
docroot => 'MEANINGLESS ARGUMENT', docroot => '/var/www',
priority => '50', priority => '50',
template => 'mediawiki/apache/mediawiki.erb', template => 'mediawiki/apache/mediawiki.erb',
ssl => true, ssl => true,
vhost_name => $site_hostname,
} }
httpd_mod { 'rewrite': httpd_mod { 'rewrite':
ensure => present, ensure => present,

View File

@ -1,123 +1,113 @@
##################################################################### # ************************************
### THIS FILE IS MANAGED BY PUPPET # Managed by Puppet
### puppet:///files/apache/sites/labconsole.wikimedia.org # ************************************
#####################################################################
# vim: filetype=apache
# Unconditionally redirect all HTTP traffic for this vhost to HTTPS
<VirtualHost *:80> <VirtualHost *:80>
ServerAdmin noc@openstack.org ServerName <%= @vhost_name %>
ServerName <%= scope.lookupvar("mediawiki::site_hostname") %> ServerAdmin <%= scope['mediawiki::serveradmin'] %>
RewriteEngine On
DocumentRoot /var/www RewriteRule ^/(.*) https://<%= @vhost_name %>/$1 [last,redirect=permanent]
<Directory /> LogLevel warn
Options FollowSymLinks ErrorLog /var/log/apache2/<%= @vhost_name %>_error.log
AllowOverride None CustomLog /var/log/apache2/<%= @vhost_name %>_access.log combined
</Directory> ServerSignature Off
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
RewriteEngine on
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/(.*)$ https://<%= scope.lookupvar("mediawiki::site_hostname") %>/$1 [L,R]
ErrorLog /var/log/apache2/mediawiki-error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog /var/log/apache2/mediawiki-access.log combined
ServerSignature Off
</VirtualHost> </VirtualHost>
<VirtualHost *:443> <VirtualHost *:443>
ServerAdmin noc@openstack.org
ServerName <%= scope.lookupvar("mediawiki::site_hostname") %>
SSLEngine on ServerName <%= @vhost_name %>
SSLProtocol All -SSLv2 -SSLv3 ServerAdmin <%= scope['mediawiki::serveradmin'] %>
SSLCertificateFile <%= scope.lookupvar("mediawiki::ssl_cert_file") %>
SSLCertificateKeyFile <%= scope.lookupvar("mediawiki::ssl_key_file") %>
<%# The original default was '' -%>
<%# scope.lookupvar returns nil for an undefined variable in puppet 4 -%>
<%# scope.lookupvar returns :undef for an undefined variable in puppet 3 -%>
<% unless ['', nil, :undef].include?(scope.lookupvar("mediawiki::ssl_chain_file")) %>
SSLCertificateChainFile <%= scope.lookupvar("mediawiki::ssl_chain_file") %>
<% end %>
RedirectMatch ^/$ http://<%= scope.lookupvar("mediawiki::site_hostname") %>/wiki/ SSLEngine on
SSLProtocol All -SSLv2 -SSLv3
# Once the machine is using something to terminate TLS that supports ECDHE
# then this should be edited to remove the RSA+AESGCM:RSA+AES so that PFS
# only is guaranteed.
SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!AES256:!aNULL:!eNULL:!MD5:!DSS:!PSK:!SRP
SSLHonorCipherOrder on
SSLCertificateFile <%= scope['mediawiki::cert_file'] %>
SSLCertificateKeyFile <%= scope['mediawiki::key_file'] %>
<% unless [nil, :undef].include?(scope['mediawiki::chain_file']) %>
SSLCertificateChainFile <%= scope['mediawiki::chain_file'] %>
<% end %>
DocumentRoot /var/www RedirectMatch ^/$ https://<%= @vhost_name %>/wiki/
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
<Directory "<%= scope.lookupvar('mediawiki::mediawiki_images_location') %>">
# Ignore .htaccess files
AllowOverride None
# Serve HTML as plaintext, don't execute SHTML DocumentRoot <%= @docroot %>
AddType text/plain .html .htm .shtml .php
# Don't run arbitrary PHP code. <Directory />
php_admin_flag engine off Options FollowSymLinks
</Directory> AllowOverride None
<Directory "<%= scope.lookupvar('mediawiki::mediawiki_location') %>"> </Directory>
Require all granted
</Directory>
<IfModule mod_expires.c>
ExpiresActive On
<Directory "<%= scope.lookupvar('mediawiki::mediawiki_location') %>">
<FilesMatch "\.(gif|jpe?g|png|css|js|woff|svg|eot|ttf)$">
ExpiresByType image/gif A2592000
ExpiresByType image/png A2592000
ExpiresByType image/jpeg A2592000
ExpiresByType text/css A2592000
ExpiresByType text/javascript A2592000
ExpiresByType application/x-javascript A2592000
ExpiresByType application/x-font-woff A2592000
ExpiresByType image/svg+xml A2592000
ExpiresByType application/vnd.ms-fontobject A2592000
ExpiresByType application/x-font-ttf A2592000
## I think it's likely dangerous to enable this for the entire domain.
## I'm nearly positive we only need to do so for the WebFonts.
## For now I'm going to keep this disabled.
#Header add Access-Control-Allow-Origin "*"
</FilesMatch>
</Directory>
</IfModule>
AddType application/x-font-woff .woff
AddType application/vnd.ms-fontobject .eot
# TTF doesn't have an official MIME type, but I really don't want to use application/octet-stream for it
AddType application/x-font-ttf .ttf
Alias /w/images <%= scope.lookupvar('mediawiki::mediawiki_images_location') %> <Directory /var/www/>
Alias /w <%= scope.lookupvar('mediawiki::mediawiki_location') %> Options Indexes FollowSymLinks MultiViews
Alias /wiki <%= scope.lookupvar('mediawiki::mediawiki_location') %>/index.php AllowOverride None
Order allow,deny
allow from all
</Directory>
# Redirect old /Article_Name urls <Directory "<%= scope['mediawiki::mediawiki_images_location'] %>">
RewriteEngine on # Ignore .htaccess files
RewriteCond %{REQUEST_URI} !^/w/ AllowOverride None
RewriteCond %{REQUEST_URI} !^/wiki/
RewriteRule ^/(.*)$ https://<%= scope.lookupvar("mediawiki::site_hostname") %>/wiki/$1 [L,R]
ErrorLog /var/log/apache2/error.log # Serve HTML as plaintext, don't execute SHTML
AddType text/plain .html .htm .shtml .php
# Possible values include: debug, info, notice, warn, error, crit, # Don't run arbitrary PHP code.
# alert, emerg. php_admin_flag engine off
LogLevel warn </Directory>
CustomLog /var/log/apache2/access.log combined <Directory "<%= scope['mediawiki::mediawiki_location'] %>">
ServerSignature Off Require all granted
</Directory>
<IfModule mod_expires.c>
ExpiresActive On
<Directory "<%= scope['mediawiki::mediawiki_location'] %>">
<FilesMatch "\.(gif|jpe?g|png|css|js|woff|svg|eot|ttf)$">
ExpiresByType image/gif A2592000
ExpiresByType image/png A2592000
ExpiresByType image/jpeg A2592000
ExpiresByType text/css A2592000
ExpiresByType text/javascript A2592000
ExpiresByType application/x-javascript A2592000
ExpiresByType application/x-font-woff A2592000
ExpiresByType image/svg+xml A2592000
ExpiresByType application/vnd.ms-fontobject A2592000
ExpiresByType application/x-font-ttf A2592000
## I think it's likely dangerous to enable this for the entire domain.
## I'm nearly positive we only need to do so for the WebFonts.
## For now I'm going to keep this disabled.
#Header add Access-Control-Allow-Origin "*"
</FilesMatch>
</Directory>
</IfModule>
AddType application/x-font-woff .woff
AddType application/vnd.ms-fontobject .eot
# TTF doesn't have an official MIME type, but I really don't want to use application/octet-stream for it
AddType application/x-font-ttf .ttf
Alias /w/images <%= scope['mediawiki::mediawiki_images_location'] %>
Alias /w <%= scope['mediawiki::mediawiki_location'] %>
Alias /wiki <%= scope['mediawiki::mediawiki_location'] %>/index.php
# Redirect old /Article_Name urls
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/w/
RewriteCond %{REQUEST_URI} !^/wiki/
RewriteRule ^/(.*)$ https://<%= @vhost_name %>/wiki/$1 [L,R]
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
ErrorLog /var/log/apache2/<%= @vhost_name %>_error.log
CustomLog /var/log/apache2/<%= @vhost_name %>_access.log combined
ServerSignature Off
</VirtualHost> </VirtualHost>