I've had this hanging out as a draft for a while, and never got it
polished up as well as I'd like; but in case it's useful to anyone,
here's some notes on my recent migration to nginx.
I run civilfritz.net on a VPS, but I do my best to keep as low a monthly
payment as possible. That means running on the smallest (lowest-memory)
VM available from my provider: a 1GB Linode.
1GB used to seem like a lot of memory; but when I'm trying to run a
Minecraft server alongside a preforking Apache server alongside a Salt
master, it fills up quickly.
I've wanted to try moving to a lighter-weight webserver for a while; so
today I'm porting my Apache config to Nginx.
sites-available/civilfritz.net
civilfritz.net runs as a pair of Apache virtual hosts to support
http
and https
. I want the majority of the configuration between
the vhosts to be identical, so I include a separate common
configuration file in each.
The http
vhost includes the common config, as well as a rewrite for
the ikiwiki /auth
section. (Authentication should only happen over
https
, but attempts to authenticate over http
should be redirected
there.)
# apache http vhost
<VirtualHost *:80>
RewriteEngine on
RewriteRule ^/auth(|/.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
Include sites-available/civilfritz.net-common
</VirtualHost>
The transition to nginx was pretty simple. The ikiwiki /auth
section
is a virtually equivalent rewrite rule, and the include directive is
also similar.
# nginx http vhost
server
{
listen 80;
rewrite ^/auth(|/.*)$ https://$server_name:443$request_uri? permanent;
include sites-available/civilfritz.net-common;
}
The https
vhost also includes the common config, as well as the
requisite ssl config. To support http basic authentication, an
instance of pwauth
is configured as an external authentication
module, which proxies to PAM.
# apache https vhost
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/civilfritz.net.pem
SSLCertificateKeyFile /etc/ssl/private/civilfritz.net.key
AddExternalAuth pwauth /usr/sbin/pwauth
SetExternalAuthMethod pwauth pipe
<Location />
AuthType Basic
AuthBasicProvider external
AuthExternal pwauth
AuthName "civilfritz.net"
</Location>
Include sites-available/civilfritz.net-common
<Location /auth>
Require valid-user
</Location>
</VirtualHost>
Again, the nginx vhost starts out similarly. Listen on tcp 443,
initialize the requisite certificate and key, and include the common
config.
pwauth
is an Apache-specific interface, so I wasn't able to use it
to proxy to pam in nginx; but the auth_pam
module works well enough
and, since I'm not trying to use PAM to auth directly against local
unix files (I'm using sssd to access kerberos), I still don't have to
run the server as root.
# nginx ssl vhost
server
{
listen 443 ssl;
ssl_certificate /etc/ssl/certs/civilfritz.net.pem;
ssl_certificate_key /etc/ssl/private/civilfritz.net.key;
include sites-available/civilfritz.net-common;
location /auth
{
auth_pam "civilfritz.net";
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_index ikiwiki.cgi;
fastcgi_param REMOTE_USER $remote_user;
}
}
The semantics of Nginx basic authentication differ from Apache. In
Apache I was able to set AuthName
globally (at /
) and then require
authentication arbitrarily at lower points in the tree. Here, the
inclusion of the auth_pam
directive implies an auth requirement; so
I'll have to repeat the authentication realm ("civilfritz.net")
anywhere I want to authenticate.
The biggest difference, though, is how Nginx handles cgi. Whereas
Apache builds-in cgi execution for nominated files or directories,
Nginx proxies all cgi execution through an external interface: here,
fastcgi. A packed-in fastcgi_params
file contains some useful
default cgi environment variables, but omits REMOTE_USER
. I set here
so that ikiwiki can determine what user has authenticated.
sites-available/civilfritz.net-common
The vast majority of my local config is in the common file included by
both vhosts.
# Apache initial config
ServerAdmin anderbubble@gmail.com
DirectoryIndex index.html
ServerName civilfritz.net
ServerAlias www.civilfritz.net
LogLevel warn
ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
DocumentRoot /srv/www/wiki
RewriteEngine on
Alias /robots.txt /srv/www/robots.txt
Alias /minecraft/overview /srv/www/minecraft-overviewer
<Location /users/janderson/private>
Require user janderson
</Location>
<Directory />
Options FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
</Directory>
<Directory /srv/www>
Order allow,deny
Allow from all
</Directory>
<Directory /srv/www/wiki>
AddHandler cgi-script .cgi
Order allow,deny
Allow from all
Options +ExecCGI
ErrorDocument 404 /ikiwiki.cgi
ExpiresActive on
ExpiresDefault "access plus 0 seconds"
Header set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
Header set Pragma "no-cache"
</Directory>
<Location /gitweb>
Order allow,deny
Allow from all
DirectoryIndex index.cgi
</Location>
<Directory /home/*/public_html/>
AllowOverride FileInfo AuthConfig Limit Indexes Options=ExecCGI
</Directory>
WSGIApplicationGroup %{GLOBAL}
New Nginx config
sites-available/civilfritz.net-common
index index.html;
server_name civilfritz.net www.civilfritz.net;
root /srv/www/wiki/;
location /
{
error_page 404 /ikiwiki-404.cgi;
expires -1;
}
location /robots.txt
{
alias /srv/www/robots.txt;
}
location /minecraft/overview
{
alias /srv/www/minecraft-overviewer;
}
location /ikiwiki.cgi
{
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_index ikiwiki.cgi;
}
location /ikiwiki-404.cgi
{
internal;
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_param REDIRECT_URL $request_uri;
# also needed to remove explicit 200
fastcgi_param REDIRECT_STATUS 404;
}
location ~ /gitweb/(index|gitweb).cgi
{
root /usr/share/;
gzip off;
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
location /gitweb/
{
root /usr/share/;
gzip off;
index index.cgi;
}
location ~ ^/~(.+?)(/.*)?$
{
alias /home/$1/public_html$2;
autoindex on;
}
Salt state
nginx.sls
nginx:
pkg:
- installed
service:
- running
- enable: True
- reload: True
- watch:
- pkg: nginx
civilfritz/www.sls
include:
- nginx
[...]
/etc/nginx/sites-enabled/default:
file:
- absent
- watch_in:
- service: nginx
/etc/nginx/sites-enabled/civilfritz.net:
file:
- symlink
- target: /etc/nginx/sites-available/civilfritz.net
- require:
- file: /etc/nginx/sites-available/civilfritz.net
- watch_in:
- service: nginx
/etc/nginx/sites-available/civilfritz.net:
file:
- managed
- source: salt://civilfritz/nginx-sites/civilfritz.net
- user: root
- group: root
- mode: 0644
- require:
- file: /etc/nginx/sites-available/civilfritz.net-common
- watch_in:
- service: nginx
/etc/nginx/sites-available/civilfritz.net-common:
file:
- managed
- source: salt://civilfritz/nginx-sites/civilfritz.net-common
- user: root
- group: root
- mode: 0644
- watch_in:
- service: nginx
/srv/www/wiki/ikiwiki-404.cgi:
file:
- symlink
- target: /srv/www/wiki/ikiwiki.cgi