Skip to content

Instantly share code, notes, and snippets.

@jjones646
Last active April 29, 2016 05:59
Show Gist options
  • Save jjones646/dd2c6c7455d1ae4f98ea to your computer and use it in GitHub Desktop.
Save jjones646/dd2c6c7455d1ae4f98ea to your computer and use it in GitHub Desktop.
A bare-bones setup that generates the NGINX configurations needed for hosting multiple WordPress websites on a single host. Supports individual SSL control.
#!/usr/bin/env python2
import os
import re
import sys
import argparse
import configparser
import pystache
from os.path import abspath, isfile, islink, join
parser = argparse.ArgumentParser(description='Generate NGINX site configurations from a config file describing each site\'s domain.')
parser.add_argument('--cfile', metavar='config-file', type=argparse.FileType('r'), default='sites.conf', help='configuration file for nginx sites templates to generate')
parser._parse_known_args(sys.argv[:1], argparse.Namespace())
args = parser.parse_args()
# get the absolute path to the file and make sure it exists
cfile = abspath(args.cfile.name)
if not isfile(cfile):
print('unable to access {}'.format(cfile))
sys.exit(1)
# read in the config file
config = configparser.ConfigParser()
config.read(cfile)
# read in the template file
subTemplate = ''
templateFn = abspath('/etc/nginx/snippets/wordpress-multi.conf')
with open(templateFn, 'r') as fp:
for line in fp:
line = line.partition('#')[0]
subTemplate += 4*' ' + line
siteTemplate = ''
templateFn = abspath('/etc/nginx/snippets/site-template.conf')
with open(templateFn, 'r') as fp:
for line in fp:
line = line.partition('#')[0]
siteTemplate += line
for c in config.sections():
if config.has_option(c, 'domain'):
# read in the values from the config file
domain = config.get(c, 'domain')
rootDir = config.get(c, 'root_dir')
port = config.get(c, 'port')
sslKey = config.get(c, 'ssl_key')
sslCert = config.get(c, 'ssl_cert')
# create the template
subTemplateFilled = pystache.render(subTemplate, {'canonical_domain': domain, 'root_dir': rootDir})
siteBlock = pystache.render(siteTemplate, {'sub_server_block': subTemplateFilled, 'listening_port': port, 'ssl_key': sslKey, 'ssl_cert': sslCert})
# clean up a bit
siteBlockClean = os.linesep.join([s for s in siteBlock.splitlines() if s])
# write out the configuration
outFile = abspath(join('/etc/nginx/sites-available', domain + '.conf'))
print('writing configuration for {} to {}'.format(c, outFile))
with open(outFile, "w") as fp:
fp.write(siteBlockClean)
# enable the config
print('enabling site for {}'.format(c))
linkName = abspath(join('/etc/nginx/sites-enabled', domain + '.conf'))
if not islink(linkName):
os.symlink(outFile, linkName)
#===========================================================
# Title: site-template.conf
# Description: NGINX snippet for establishing an SSL
# frontend proxy for a locally running server.
# Author: Jonathan Jones
# Date: Apr. 29, 2016
# Version: 0.1
# Nginx Version: 1.6.2
# Usage: Place file in /etc/nginx/snippets,
# and use 'include snippets/...' for
# use in other files. Place within
# a 'server {}' block.
#===========================================================
# Requests to https.
server {
listen {{listening_port}};
ssl on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate {{ssl_cert}};
ssl_certificate_key {{ssl_key}};
include snippets/wordpress-global.conf;
include snippets/redirect.conf;
{{sub_server_block}}
include snippets/denygit.conf;
}
[example1]
domain = example1.com
port = 443
root_dir = /usr/share/nginx/sites/example1.com
ssl_cert = /etc/ssl/example1.com.crt
ssl_key = /etc/ssl/example1.com.key
[example2]
domain = example2.com
port = 443
root_dir = /usr/share/nginx/sites/example2.com
ssl_cert = /etc/ssl/example2.com.crt
ssl_key = /etc/ssl/example2.com.key
#===========================================================
# Title: wordpress-global.conf
# Description: NGINX snippet for establishing some of the
# global declarations needed in a server block.
# Author: Jonathan Jones
# Date: Feb. 18, 2016
# Version: 0.1
# Nginx Version: 1.6.2
# Usage: Place file in /etc/nginx/snippets,
# and use 'include snippets/...' for
# use in other files. Place within
# a 'server {}' block.
#===========================================================
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. {
deny all;
}
# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
#===========================================================
# Title: wordpress-multi.conf
# Description: NGINX snippet for establishing some of the
# server declarations needed for hosting
# multiple wordpress websites on a single
# server.
# Author: Jonathan Jones
# Date: Feb. 21, 2016
# Version: 0.1
# Nginx Version: 1.6.2
# Usage: Place file in /etc/nginx/snippets,
# and use 'include snippets/...' for
# use in other files. Place within
# a 'server {}' block.
#===========================================================
location / {
try_files $uri $uri/ /index.php?$args;
}
# Directives to send expires headers and turn off 404 error logging.
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 24h;
log_not_found off;
}
location ~ ^/[_0-9a-zA-Z-]+/files/(.*)$ {
try_files /wp-content/blogs.dir/$blogid/files/$2 /wp-includes/ms-files.php?file=$2 ;
access_log off;
log_not_found off;
expires max;
}
# Rewrite multisite '.../wp-.*' and '.../*.php'.
if (!-e $request_filename) {
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 last;
rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 last;
}
# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ \.php$ {
# Zero-day exploit defense.
# http://forum.nginx.org/read.php?2,88845,page=3
# Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi.
# Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine. And then cross your fingers that you won't get hacked.
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php;
}
#===========================================================
# Title: wordpress-single.conf
# Description: NGINX snippet for establishing some of the
# server declarations needed for a single hosted
# wordpress server.
# Author: Jonathan Jones
# Date: Feb. 18, 2016
# Version: 0.1
# Nginx Version: 1.6.2
# Usage: Place file in /etc/nginx/snippets,
# and use 'include snippets/...' for
# use in other files. Place within
# a 'server {}' block.
#===========================================================
location / {
try_files $uri $uri/ /index.php?$args;
}
# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;
}
# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment