#!/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)