From: Dan Scott Date: Sun, 16 Jul 2017 21:07:45 +0000 (-0700) Subject: Script to generate nginx & apache configs X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=b669758a3b318236f0b52dea5ba704b319815db1;p=contrib%2FConifer.git Script to generate nginx & apache configs Given a set of hostnames, generate the appropriate nginx proxy and apache config files. Also spit out a certbot command to generate a letsencrypt certificate based on those hostnames. Signed-off-by: Dan Scott --- diff --git a/Open-ILS/src/support-scripts/webserver_config.py b/Open-ILS/src/support-scripts/webserver_config.py new file mode 100755 index 0000000000..0b74e6c2f7 --- /dev/null +++ b/Open-ILS/src/support-scripts/webserver_config.py @@ -0,0 +1,406 @@ +#!/usr/bin/env python3 +import os, os.path + +ssl_certname = 'boreal-test.concat.ca'; + +# /etc/nginx/concat_ssl.conf +ssl_conf = """listen 443 ssl; # managed by Certbot +ssl_certificate /etc/letsencrypt/live/{certname}/fullchain.pem; # managed by Certbot +ssl_certificate_key /etc/letsencrypt/live/{certname}/privkey.pem; # managed by Certbot +include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot + +if ($scheme != "https") {{ + return 301 https://$host$request_uri; +}} # managed by Certbot + +# generate with openssl dhparam -out dhparams.pem 2048 +ssl_dhparam /etc/letsencrypt/dhparams.pem; + +# From https://mozilla.github.io/server-side-tls/ssl-config-generator/ +ssl_session_tickets off; + +# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) +add_header Strict-Transport-Security max-age=15768000; + +# OCSP Stapling --- +# fetch OCSP records from URL in ssl_certificate and cache them +ssl_stapling on; +ssl_stapling_verify on; +""".format(certname=ssl_certname) + +# /etc/nginx/concat_headers.conf +headers_conf = """proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme;""" + +# /etc/nginx/osrf_sockets.conf +sockets_conf = """location /osrf-websocket-translator { + proxy_pass https://localhost:7682; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Needed for websockets proxying. + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Raise the default nginx proxy timeout values to an arbitrarily + # high value so that we can leverage osrf-websocket-translator's + # timeout settings. + proxy_connect_timeout 5m; + proxy_send_timeout 1h; + proxy_read_timeout 1h; +}""" + +# /etc/nginx/sites-available/conifer-test +server_block = """server {{ + listen 80; + server_name {hostname}; + + include /etc/nginx/concat_ssl.conf; + include /etc/nginx/osrf_sockets.conf; + + location / {{ + proxy_pass https://localhost:7443; + include /etc/nginx/concat_headers.conf; + }} +}} +""" + +domains = { + 'algoma.concat.ca': { + 'templates': ['algoma'], + 'locale': 'fr_ca', + 'physical_loc': 111 + }, + 'boreal.concat.ca': { + 'templates': ['boreal'], + 'locale': 'fr_ca', + 'default_locale': 'fr-CA', + 'physical_loc': 135 + }, + 'ccrconnect.concat.ca': { + 'locale': 'fr_ca', + 'physical_loc': 144 + }, + 'crc.concat.ca': { + 'templates': ['laurentian'], + 'locale': 'fr_ca', + 'default_locale': 'fr-CA', + 'physical_loc': 130 + }, + 'hrsrh.concat.ca': { + 'templates': ['hrsrh'], + 'locale': 'fr_ca', + 'physical_loc': 115 + }, + 'cdev1.concat.ca': { + 'locale': 'fr_ca', + }, + 'hsn.concat.ca': { + 'templates': ['hrsrh'], + 'locale': 'fr_ca', + 'physical_loc': 115 + }, + 'huntington.concat.ca': { + 'templates': ['huntington'], + 'locale': 'fr_ca', + 'physical_loc': 104 + }, + 'laurentian.concat.ca': { + 'templates': ['laurentian'], + 'locale': 'fr_ca', + 'physical_loc': 105, + 'robots': 'osul' + }, + 'laurentienne.concat.ca': { + 'templates': ['laurentian'], + 'locale': 'fr_ca', + 'default_locale': 'fr-CA', + 'physical_loc': 105 + }, + 'medb.concat.ca': { + 'templates': ['laurentian'], + 'locale': 'fr_ca', + 'physical_loc': 117 + }, + 'mediacentre.concat.ca': { + 'templates': ['laurentian'], + 'locale': 'fr_ca', + 'physical_loc': 108 + }, + 'mrc.concat.ca': { + 'templates': ['laurentian'], + 'locale': 'fr_ca', + 'physical_loc': 131 + }, + 'nosm.concat.ca': { + 'templates': ['nosm'], + 'locale': 'fr_ca', + 'physical_loc': 125 + }, + 'sah.concat.ca': { + 'locale': 'fr_ca', + 'physical_loc': 116 + }, + 'sjcg.concat.ca': { + 'templates': ['sjcg'], + 'locale': 'fr_ca', + 'physical_loc': 133 + }, + 'uhearst.concat.ca': { + 'templates': ['uhearst'], + 'locale': 'fr_ca', + 'default_locale': 'fr-CA', + 'physical_loc': 114 + }, + 'usudbury.concat.ca': { + 'templates': ['laurentian', 'usudbury'], + 'locale': 'fr_ca', + 'physical_loc': 107 + }, + 'www.concat.ca': { + 'locale': 'fr_ca', + }, + '49thregiment.concat.ca': { + 'physical_loc': 134 + } +} + +missing_test_domains = ( + 'cfof-test.concat.ca', + 'medb-test.concat.ca', +) + +def mutate_test_hostname(host): + "Generate test hostname for a single string" + if not host.startswith('cdev1'): + x = host.partition('.') + host = ''.join((x[0], '-test', x[1], x[2])) + return host + +def mutate_test_hostnames(test=True): + """Generate proper test hostnames""" + testdomains = [] + + for host in sorted(domains.keys()): + if test: + host = mutate_test_hostname(host) + testdomains.append(host) + return testdomains + +def generate_config(test=True): + """Generate nginx config files""" + os.makedirs('nginx/sites-available', exist_ok=True) + os.makedirs('apache2/sites-available', exist_ok=True) + with open(os.path.join('nginx', 'concat_ssl.conf'), 'w') as f: + f.write(ssl_conf) + + with open(os.path.join('nginx', 'concat_headers.conf'), 'w') as f: + f.write(headers_conf) + + with open(os.path.join('nginx', 'osrf_sockets.conf'), 'w') as f: + f.write(sockets_conf) + + with open(os.path.join('nginx/sites-available', 'conifer.conf'), 'w') as f: + for host in mutate_test_hostnames(test): + f.write(server_block.format(hostname=host)) + + with open(os.path.join('apache2/sites-available', 'conifer.conf'), 'w') as f: + f.write(generate_apache_vhost(test)) + +def generate_certbot(test=True): + """Generate certbot command""" + certbot = 'certbot --nginx run ' + for host in mutate_test_hostnames(test): + if host in missing_test_domains: + continue + certbot = certbot + " -d {hostname}".format(hostname=host) + print(certbot) + +def generate_apache_vhost(test=True): + """Generate apache2/sites-available/eg.conf""" + vhost = ApacheVHost.apache_eg_conf + for hostname in sorted(domains.keys()): + ahost = ApacheVHost(hostname, test) + vhost = vhost + ahost.vhost() + return vhost + +class ApacheVHost: + # /etc/apache2/sites-available/eg.conf + apache_eg_conf = """LogLevel info +# - log locally +# CustomLog /var/log/apache2/access.log combined +# ErrorLog /var/log/apache2/error.log +# - log to syslog +CustomLog "|/usr/bin/logger -p local7.info" common +ErrorLog syslog:local7 + +# ---------------------------------------------------------------------------------- +# Set up Perl +# ---------------------------------------------------------------------------------- + +# - needed by CGIs +PerlRequire /etc/apache2/eg_startup +PerlChildInitHandler OpenILS::WWW::Reporter::child_init +PerlChildInitHandler OpenILS::WWW::SuperCat::child_init +PerlChildInitHandler OpenILS::WWW::AddedContent::child_init +PerlChildInitHandler OpenILS::WWW::AutoSuggest::child_init +PerlChildInitHandler OpenILS::WWW::PhoneList::child_init +PerlChildInitHandler OpenILS::WWW::EGWeb::child_init + +# ---------------------------------------------------------------------------------- +# Set some defaults for our working directories +# ---------------------------------------------------------------------------------- + + Require all granted + + +# ---------------------------------------------------------------------------------- +# XUL directory +# ---------------------------------------------------------------------------------- + + Options Indexes FollowSymLinks + AllowOverride None + Require all granted + + +# ---------------------------------------------------------------------------------- +# Remove the language portion from the URL +# ---------------------------------------------------------------------------------- +AliasMatch ^/opac/.*/skin/(.*)/(.*)/(.*) /openils/var/web/opac/skin/$1/$2/$3 +AliasMatch ^/opac/.*/extras/slimpac/(.*) /openils/var/web/opac/extras/slimpac/$1 +AliasMatch ^/opac/.*/extras/selfcheck/(.*) /openils/var/web/opac/extras/selfcheck/$1 + +# ---------------------------------------------------------------------------------- +# System config CGI scripts go here +# ---------------------------------------------------------------------------------- +Alias /cgi-bin/offline/ "/openils/var/cgi-bin/offline/" + + AddHandler cgi-script .cgi .pl + AllowOverride None + Options None + Require host 10.0.0.0/8 + Options FollowSymLinks ExecCGI Indexes + + +# ---------------------------------------------------------------------------------- +# Updates folder +# ---------------------------------------------------------------------------------- +Alias /updates/ "/openils/var/updates/pub/" + + + ForceType cgi-script + + + ForceType cgi-script + + + ForceType cgi-script + + + ForceType cgi-script + + AllowOverride None + Options None + Options ExecCGI + Require all granted + + +# ---------------------------------------------------------------------------------- +# OPTIONAL: Set how long the client will cache our content. Change to suit +# ---------------------------------------------------------------------------------- +ExpiresActive On +ExpiresDefault "access plus 1 month" +ExpiresByType text/html "access plus 18 hours" +ExpiresByType application/xhtml+xml "access plus 18 hours" +ExpiresByType application/x-javascript "access plus 18 hours" +ExpiresByType application/javascript "access plus 18 hours" +ExpiresByType text/css "access plus 50 minutes" + +# ---------------------------------------------------------------------------------- +# Set up our SSL virtual host +# ---------------------------------------------------------------------------------- +#Listen 443 + + DocumentRoot "/openils/var/web" + ServerName localhost:443 + ServerAlias 127.0.0.1:443 + + # - absorb the shared virtual host settings + Include eg_vhost.conf + Include eg_vhost_ssl.conf + + +""" + + apache_robots = """Alias /robots.txt /openils/var/web/{host}_robots.txt +""" + + apache_template = """ + PerlAddVar OILSWebTemplatePath '/openils/var/templates_{template}'""" + + apache_locale = """ + PerlAddVar OILSWebLocale '{locale}' + PerlAddVar OILSWebLocale '/openils/var/data/locale/opac/{locale_upper}.po'""" + + apache_default_locale = """ + PerlAddVar OILSWebDefaultLocale "{locale}" +""" + + apache_physical_loc = "SetEnv physical_loc {location}" + + apache_vhost = """ + + DocumentRoot '/openils/var/web' + ServerName https://{hostname}:443 + + # - absorb the shared virtual host settings + Include eg_vhost.conf + Include eg_vhost_ssl.conf + {robots} + {template}{locale}{default_locale} + + {physical_loc} + +""" + + def __init__(self, hostname, test=True): + self.hostname = hostname + host = domains[self.hostname] + if test: + self.hostname = mutate_test_hostname(hostname) + self.robots = '' + self.default_locale = '' + self.locale = '' + self.physical_loc = '' + self.templates = '' + if 'robots' in host: + self.robots = ApacheVHost.apache_robots.format(host=host['robots']) + if 'default_locale' in host: + self.default_locale = ApacheVHost.apache_default_locale.format(locale=host['default_locale']) + if 'locale' in host: + locale = host['locale'] + locale_upper = ''.join((locale[0:2], '-', locale[3:5].upper())) + self.locale = ApacheVHost.apache_locale.format(locale=locale, locale_upper=locale_upper) + if 'physical_loc' in host: + self.physical_loc = ApacheVHost.apache_physical_loc.format(location=host['physical_loc']) + if 'templates' in host: + for template in host['templates']: + self.templates = self.templates + ApacheVHost.apache_template.format(template=template) + + def vhost(self): + return ApacheVHost.apache_vhost.format( + hostname=self.hostname, + robots=self.robots, + template=self.templates, + locale = self.locale, + default_locale = self.default_locale, + physical_loc = self.physical_loc + ) + +if __name__ == '__main__': + test = True + generate_config(test) + generate_certbot(test)