Posted in: apache, https, ngnix, wordpress

Securing WordPress behind SSL proxy

Now that I’ve got my WordPress site up and running on FreeBSD, I decided it should be accessible via https too so that I can login to it securely to prevent passwords being sent over the internet in plain text!

After a bit of resarch I discovered the best way to do this was with ngnix sitting on my freebsd firewall server, and handling the requests over ssl, and passing plain http back to my WordPress server.

Install and configure NGINX

The first thing to do was to install ngnix via the FreeBSD ports tree on to the gateway server.  This is done as follows;

cd /usr/ports/www/nginx
make install && make clean

Once ngnix is installed enable startup at boot time by adding a line to /etc/rc.conf

echo "enable_nginx='YES'" >> /etc/rc.conf

Now edit the /usr/local/etc/nginx/nginx.conf

# HTTPS server

server {
 listen 443;
 server_name bowie.rjctest.co.uk;

ssl on;
 ssl_certificate ssl/bowie.rjctest.co.uk.crt;
 ssl_certificate_key ssl/bowie.rjctest.co.uk.key;

ssl_session_timeout 5m;

access_log logs/ssl-access.log;
 error_log logs/ssl-error.log;

ssl_protocols SSLv2 SSLv3 TLSv1;
 ssl_ciphers RC4:HIGH:!aNULL:!MD5;
 ssl_prefer_server_ciphers on;
 keepalive_timeout 60;
 ssl_session_cache shared:SSL:10m;

location / {
 proxy_pass http://10.34.56.44:80;
 proxy_set_header Host $host;
 proxy_set_header X-Forwarded-Proto https;
 proxy_set_header X-Forwarded-For $remote_addr;
 }
 }

A few line need customising for a different setup. these are the server_name parameter, and the proxy_pass parameter.  The server_name should match the external host name for your site, and the proxy_pass parameter should point to the host that your apache server is running on. The proxy_set_header X-Forwarded-Proto https line is important so that WordPress knows what to do when it receives a forwarded http session.

To create a self singed cert you can follow the following:

# cd /usr/local/nginx/etc
# mkdir ssl && cd ssl
# openssl genrsa -des3 -out bowie.rjctest.co.uk.key 1024

# openssl req -new -key bowie.rjctest.co.uk -out bowie.rjctest.co.uk.csr

You can remove the pass phrase as follows;

# cp bowie.rjctest.co.uk.key bowie.rjctest.co.uk.key.bak
# openssl rsa -in bowie.rjctest.co.uk.key.bak -out bowie.rjctest.co.uk.key

An now to generate the self signed certificate.

# openssl x509 -req -days 365 -in bowie.rjctest.co.uk.csr -signkey bowie.rjctest.co.uk.key -out bowie.rjctest.co.uk.crt

I’ve moved to a proper ssl certificate from RapidSSL via ssl247.co.uk for the certificate for my site.

Changes to apache22 and WordPress

A couple of changes are also needed on the wordpress server it’s self, one for the log file format so that we see the real IP of the clients, not just that of the nginx server, and the other change to ensure https sessions are handled correctly

Firstly define the new log format with the following entry

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-A
gent}i\"" varnishcombined

Then ensure that the webserver uses the new log format with the following entry, commenting out the combined/common log format line.

#CustomLog "/var/log/httpd-access.log
CustomLog /var/log/httpd-access.log varnishcombined

you will need to restart apache to pickup these changes.

To ensure that WordPress handles https sessions properly the following changes need to be made to the wp-config.php file.  Edit the end of the file so that it looks as below.

define('WP_DEBUG', false);
define('FORCE_SSL_LOGIN', false); 
define('FORCE_SSL_ADMIN', false); 
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
 $_SERVER['HTTPS']='on'; 
/* That's all, stop editing! Happy blogging. */ 
/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
 define('ABSPATH', dirname(__FILE__) . '/');
/** Sets up WordPress vars and included files. */ 
require_once(ABSPATH . 'wp-settings.php');

The setting of the FORCE_SSL_* parameters to true will force the use of ssl for either login’s or for admin pages.  I have set this to false as I often use wordpress from the internal network which does not have access to the ssl interface of nginx.  The if ($_SERVER[‘HTTP_X_FORWARDED_PROTO’] == ‘https’) statment sets the connection url to https if the http header has http_x_forwarded_proto set to https, which is done by nginx.  No apache restart is needed for this change to take effect.

References

For reference I used the following pages to create my configuration / setup

http://www.cyberciti.biz/faq/howto-linux-unix-setup-nginx-ssl-proxy/
http://chase-seibert.github.com/blog/2011/12/21/nginx-ssl-reverse-proxy-tutorial.html