MoinMoin on Nginx - shawfdong/hyades GitHub Wiki
As an experiment for layered security, I maintain a personal wiki, using the MoinMoin Wiki Engine, on the server maia.ucsc.edu. The following are the building blocks:
- MoinMoin: a wiki software implemented in Python – MediaWiki is written in PHP[1]
- uWSGI server: running MoinMoin as a WSGI application
- nginx: as a reverse proxy server, passing requests to the uWSGI server, via the uwsgi wire protocol[2]
- SSL Server Certificate
- SSL Client Certificates
- HTTP Basic Authentication
MoinMoin is written in Python. On a fully patched CentOS 6 system, such as maia.ucsc.edu, the stock Python is ancient:
# python --version Python 2.6.6but it is sufficient to run MoinMoin, as the latest MoinMoin (version 1.9.7) only requires Python 2.4.
1. Download the latest release of MoinMoin (moin-1.9.7.tar.gz) from http://moinmo.in/MoinMoinDownload.
2. The web root for static contents is /var/www/maia. Unpack the tarball at /var/www, outside of the web root:
# tar xvzf moin-1.9.7.tar.gz -C /var/www/
3. Copy the static contents of MoinMoin to the web root:
# mkdir -p /var/www/maia/static # cp -r /var/www/moin-1.9.7/MoinMoin/web/static/htdocs/* /var/www/maia/static/
4. Fix the permission – the wiki data must be writable by the uWSGI server, which runs as uid = nginx & gid = nginx (see below):
# chown -R dong:dong /var/www/maia # chown -R dong:dong /var/www/moin-1.9.7 # chown -R nginx:nginx /var/www/moin-1.9.7/wiki
5. Adapt /var/www/moin-1.9.7/wikiconfig.py:
import os
from MoinMoin.config import multiconfig, url_prefix_static
class LocalConfig(multiconfig.DefaultConfig):
wikiconfig_dir = os.path.abspath(os.path.dirname(__file__))
instance_dir = os.path.join(wikiconfig_dir, 'wiki')
# Where your own wiki pages are (make regular backups of this directory):
data_dir = os.path.join(instance_dir, 'data', '') # path with trailing /
# Where system and help pages are (you may exclude this from backup):
data_underlay_dir = os.path.join(instance_dir, 'underlay', '') # path with trailing /
DesktopEdition = False
acl_rights_before = u"FrankDong:read,write,delete,revert,admin"
acl_rights_default = u"All:none"
surge_action_limits = None # no surge protection
sitename = u"Frank Dong's Wiki"
url_prefix_static = '/static'
logo_string = u'<img src="%s/common/kailash-logo.png" alt="Kailash">' % url_prefix_static
page_front_page = u'FrontPage'
secrets = 'xxxxxxxxxx'
superuser = [u"FrankDong", ]
language_default = 'en'
language_ignore_browser = True
try:
from wikiconfig_local import Config
except ImportError, err:
if not str(err).endswith('wikiconfig_local'):
raise
Config = LocalConfig
MoinMoin is a WSGI application. Here is how to install and set up a uWSGI server on CentOS 6.
1. Install pip, e.g., install the python-pip package from the EPEL repository:
# yum install python-pip
2. Install uWSGI:
# pip install uWSGI
or upgrade to the latest version of uWSGI:
# pip install -U uWSGI
3. Create a configuration file for uWSGI (/etc/uwsgi/moin.xml)[3][4]:
<uwsgi> <socket>/var/lib/uwsgi/moin.sock</socket> <uid>nginx</uid> <wsgi-file>/var/www/moin-1.9.7/wiki/server/moin.wsgi</wsgi-file> <master /> <workers>4</workers> <daemonize>/dev/null</daemonize> </uwsgi>
4. Create an init script for uWSGI (/etc/init.d/uwsgi)[5]:
#! /bin/sh
#
# chkconfig: - 83 17
# description: uWSGI
# processname: uwsgi
# config: /etc/uwsgi.conf
# config: /etc/sysconfig/uwsgi
# pidfile: /var/run/uwsgi/uwsgi.pid
#
### BEGIN INIT INFO
# Provides: uwsgi
# Required-Start: $local_fs $remote_fs $network $named
# Required-Stop: $local_fs $remote_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop uWSGI
# Description: uWSGI
### END INIT INFO
# Standard LSB functions
#. /lib/lsb/init-functions
# Source function library.
. /etc/init.d/functions
# Check that networking is up.
. /etc/sysconfig/network
# Additional environment file
if [ -f /etc/sysconfig/uwsgi ]; then
. /etc/sysconfig/uwsgi
fi
if [ "$NETWORKING" = "no" ]
then
exit 0
fi
RETVAL=0
prog="uwsgi"
pidfile=${PIDFILE-/var/run/uwsgi.pid}
lockfile=${LOCKFILE-/var/lock/subsys/uwsgi}
xmlfile=${XMLFILE-/etc/uwsgi/moin.xml}
start () {
echo -n $"Starting $prog: "
dir=$(dirname ${pidfile})
[ -d $dir ] || mkdir $dir
/usr/bin/uwsgi -x ${xmlfile} --pidfile ${pidfile}
RETVAL=$?
[ $RETVAL -eq 0 ] && touch ${lockfile}
}
stop () {
echo -n $"Stopping $prog: "
killproc -p ${pidfile} uwsgi
# or equivalently
# /usr/bin/uwsgi --stop ${pidfile}
RETVAL=$?
echo
if [ $RETVAL -eq 0 ] ; then
rm -f ${lockfile} ${pidfile}
fi
}
restart () {
stop
start
}
reload () {
echo -n $"Reloading $prog: "
killproc -p ${pidfile} uwsgi -HUP
# or equivalently
# /usr/bin/uwsgi --reload ${pidfile}
RETVAL=$?
echo
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status -p ${pidfile} uwsgi
RETVAL=$?
;;
restart)
restart
;;
reload)
reload
;;
*)
echo $"Usage: $0 {start|stop|status|restart|reload}"
RETVAL=2
;;
esac
exit $RETVAL
5. Start uWSGI:
# chkconfig --add uwsgi # service start uwsgi
Here is the configuration for nginx (/etc/nginx/conf.d/maia.conf):
server {
listen 80;
server_name maia.ucsc.edu;
location ^~ / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl;
server_name maia.ucsc.edu;
ssl_certificate /etc/ssl/maia.crt;
ssl_certificate_key /etc/ssl/maia.key;
ssl_client_certificate /etc/ssl/dong-root-ca.crt;
ssl_crl /etc/ssl/crl.pem;
ssl_verify_client on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
root /var/www/maia;
index index.html;
autoindex on;
auth_basic "Basic Authentication Required";
auth_basic_user_file htpasswd;
# Prevent access to any file starting with a dot
location ~ /\. { access_log off; log_not_found off; deny all; }
# Prevent access to any files ending with a ~
location ~ ~$ { access_log off; log_not_found off; deny all; }
# Do not log access to robots.txt, to keep the logs cleaner
location = /robots.txt { access_log off; log_not_found off; }
# Do not log access to the favicon, to keep the logs cleaner
location = /favicon.ico { access_log off; log_not_found off; }
location = /_.gif {
expires max;
empty_gif;
}
# Keep images and CSS around in browser cache for as long as possible
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
location /wiki {
include uwsgi_params;
uwsgi_param SCRIPT_NAME /wiki;
uwsgi_modifier1 30;
uwsgi_pass unix:/var/lib/uwsgi/moin.sock;
}
error_page 403 404 /_.gif;
error_page 500 502 503 504 /_.gif;
}