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; }