You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
3.8 KiB
125 lines
3.8 KiB
#!/usr/bin/python3
|
|
|
|
# Dependencies:
|
|
# pip3 install crossplane
|
|
# https://github.com/nginxinc/crossplane
|
|
|
|
import sys
|
|
import crossplane
|
|
import pprint
|
|
import shlex
|
|
from subprocess import run
|
|
|
|
import certbot_config as config
|
|
|
|
# Resolve 'include' directive (non-recursive)
|
|
def resolve_include(response, directive):
|
|
result = []
|
|
for index in directive['includes']:
|
|
# Join parsed included file to result list
|
|
result.extend(response['config'][index]['parsed'])
|
|
return result
|
|
|
|
# Resolve all 'include' directives (recursive)
|
|
def resolve_includes(response, directives=None):
|
|
# Assume resolve of first file in response if not specified
|
|
if directives is None:
|
|
directives = response['config'][0]['parsed']
|
|
|
|
# Rebuild directive tree with includes in-place
|
|
result = []
|
|
for directive in directives:
|
|
if 'includes' in directive:
|
|
# Resolve include and recursively resolve includes in included file
|
|
result.extend(resolve_includes(response, resolve_include(response, directive)))
|
|
continue
|
|
|
|
if 'block' in directive:
|
|
# Recursively traverse block statement
|
|
directive['block'] = resolve_includes(response, directive['block'])
|
|
|
|
result.append(directive)
|
|
|
|
return result
|
|
|
|
# Search in directives using matching function
|
|
def search_directives(directives, fn, recursive=False):
|
|
result = []
|
|
|
|
for directive in directives:
|
|
if fn(directive):
|
|
# Append directive matched by search function to result list
|
|
result.append(directive)
|
|
elif recursive and 'block' in directive:
|
|
# Recursively search block statement
|
|
result.extend(search_directives(directive['block'], fn, True))
|
|
|
|
return result
|
|
|
|
# Recursive search in directives, steered by matching functions
|
|
def resolve_path(directives, fns):
|
|
# Pick the first search function in the list and perform a flat search
|
|
results = search_directives(directives, fns[0])
|
|
if len(results) > 0 and len(fns) > 1:
|
|
# A match for the first path element was found, recurse subtree to find next match
|
|
return resolve_path(results[0]['block'], fns[1:])
|
|
else:
|
|
# No further matches were found
|
|
return results
|
|
|
|
# Get all HTTP server directives
|
|
def get_servers(directives):
|
|
return resolve_path(directives, [
|
|
lambda d: ('directive', 'http') in d.items(),
|
|
lambda d: ('directive', 'server') in d.items()])
|
|
|
|
# Generate { server_name: root } dictionary of virtual hosts
|
|
def get_vhosts(directives):
|
|
servers = get_servers(directives)
|
|
vhosts = []
|
|
for server in servers:
|
|
# Retrieve server name directive
|
|
server_name = search_directives(server['block'], lambda d: ('directive', 'server_name') in d.items())
|
|
if len(server_name) == 0:
|
|
continue
|
|
|
|
for name in server_name[0]['args']:
|
|
# Apply server alias to vhosts list
|
|
vhosts.append(name)
|
|
|
|
return vhosts
|
|
|
|
# Parse nginx.conf file with Crossplane
|
|
response = crossplane.parse(config.nginx_config_file)
|
|
if response['errors']:
|
|
pprint.pprint(response['errors'])
|
|
sys.exit(1)
|
|
|
|
# Resolve all includes (combine into to one big config tree)
|
|
directives = resolve_includes(response)
|
|
|
|
# Retrieve all virtual hostnames
|
|
vhosts = [ name for name in get_vhosts(directives) if not name in config.ignored_domains ]
|
|
|
|
if config.primary_domain in vhosts:
|
|
# Ensure the primary hostname comes first
|
|
vhosts.remove(config.primary_domain)
|
|
vhosts.insert(0, config.primary_domain)
|
|
else:
|
|
print('Primary domain is missing from configuration')
|
|
sys.exit(1)
|
|
|
|
print()
|
|
print('Found virtual hosts:')
|
|
for name in vhosts:
|
|
print(' {}'.format(name))
|
|
print()
|
|
|
|
# Build certbot command line
|
|
cmd = config.certbot_command
|
|
for name in vhosts:
|
|
cmd.extend(['-d', name])
|
|
|
|
print('Running', ' '.join(cmd))
|
|
print()
|
|
run(cmd)
|
|
|