Skip to content

How this site is built and updated

We've been (very) infrequently updating this blog thing since 2006. In that time it's had a number of homes: it probably started as a VM on a machine under the desk in my office, then it spent years flipping between running directly and indirectly (as a VM) on servers in our datacenter. After our old hardware started failing, we moved it to the cloud, running on an EC2 server. Now, it's running in a container. Initially, this blog was created to test and document this new fangled SAML authentication protocol, starting with mod_shib (from Shibboleth v1. When Shibboleth v2 was released, we switched to SAML 2.0 and mod_shib2.

In late 2014, our team started deploying Drupal websites on AWS using Docker images running on CoreOS. As part of that effort we created a base LAMP image using Ubuntu. We also added SimpleSAMLphp to the image to support authentication, since it had features that were a better fit for our environment than mod_shib2 (mainly that there was no separate daemon, like shibd, and that it could store session data in the same database as Drupal, making load balancing much simpler).

In early 2015 we started merging the two efforts, resulting in this new containerized, load-balanced, and easily updated version of our blog.

In early 2015 we started building WordPress images on top of our base LAMP image, and we finally moved this blog to a container in 2016, using the simplesamlphp-authentication plugin (despite the warning, it continues to work with the latest version of WordPress).

All the code is available on code.stanford.edu.

SimpleSAMLphp Setup

Since perhaps more people are interested in how to use SimpleSAMLphp with WordPress than how to run WordPress in a container, I'll cover the SSP configuration first.

As mentioned above, we reused the base Ubuntu + Apache 2.4.x + PHP + SimpleSAMLphp image we created for running Drupal in containers, so only two files need to be added to the container to configure SimpleSAMLphp (see below for how they're added in the Dockerfile). We use a slightly modified version of saml20-idp-remote.php which defines metadata (in SimpleSAMLphp's native format) for all the Stanford IdPs.

The second file is authsources.php - again it's mostly the same as the version included in the base image, but we added support for signing authentication requests and receiving encrypted assertions:


<?php

$config = array(

  'default-sp' => array(
    'saml:SP',

    'authproc' => array(
      20 => 'saml:NameIDAttribute',
    ),

    'entityID' => $_SERVER['ENV_ENTITY_ID'],
    'idp' => $_SERVER['ENV_IDP'],

    'acs.Bindings' => array(
        'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
    ),

    'certificate' => 'itarch.pem',
    'privatekey' => 'itarch.key',

    'SingleLogoutServiceBinding' => array (),

  ),
);

The IdP and entityID values are injected into the container using environment variables.

Running WordPress in a Container

Unlike our more complicated images, where we use packer, this image is just built with a simple Dockerfile.

Configuration via Environment Variables

Our LAMP image relies on some environment variables being passed into the container when it starts. For the database configuration, we re-used environment variable names from Amazon's Elastic Beanstalk PaaS, which we used during our early prototyping for a cloud-based Drupal; for the AWS S3 media plugin we have to include AWS Access and Secret Keys. The remaining variables are used to select the SAML IdP to use (set to a name from saml20-idp-remote.php) and the SAML EntityID for our blog.


ENV_IDP=https://idpproxy.itlab.stanford.edu/idp
ENV_ENTITY_ID=https://itarch.stanford.edu/
RDS_DB_NAME=itarch
RDS_USERNAME=itarch
RDS_PASSWORD=SECRET
RDS_HOSTNAME=db.itarch.stanford.edu
AWS_ACCESS_KEY=SECRET
AWS_SECRET_KEY=SECRET

In our containerized environment every application has a unique git repository on code.stanford.edu, but all the environment configuration and unit definitions for containers is stored in a separate git repository with more restricted access. The unit file for this blog looks like this:


[Unit]
Description=itarch Site
Requires=docker.service
After=docker.service

[Service]
Environment=HOME=/home/core
EnvironmentFile=/etc/environment
EnvironmentFile=/var/lib/apps/sites/itarch/envvars
TimeoutStartSec=1200
ExecStartPre=/usr/bin/docker pull ${ENV_IMAGE}
ExecStartPre=/var/lib/apps/bin/cert_sync itarch
ExecStartPre=-/usr/bin/docker rm itarch-%i.service
ExecStart=/usr/bin/docker run --rm \
  --name itarch-%i.service -h itarch-%i \
  --env-file=/var/lib/apps/sites/itarch/envvars \
  -v /dev/log:/dev/log \
  -v /var/lib/apps-data/sites/itarch/certs:/etc/simplesamlphp/certs \
  -p ${ENV_PORT}:8080 ${ENV_IMAGE} /start.sh
ExecStop=/usr/bin/docker stop itarch-%i.service
RestartSec=5
Restart=always
ExecStartPost=/var/lib/apps/bin/confd-register -m -t http \
  -d ${ENV_DOMAIN} -n %p-%i -u ${COREOS_PRIVATE_IPV4}:${ENV_PORT}
ExecStopPost=/var/lib/apps/bin/confd-register -r -t http \
  -d ${ENV_DOMAIN} -n %p-%i

[X-Fleet]
# prevent multiple containers running on the same host
X-Conflicts=itarch@*.service
# only run on the main hosting server pool
MachineMetadata=role=hosting

The certificate and key that are mounted onto /etc/simplesamlphp/certs are the ones used for SAML encryption and signing, not the client-facing ones. The containers for the site are behind a cluster of HAProxy load balancers (also containerized); the calls to conf-register when each container starts or stops cause a separate process to rebuild the HAProxy configurations then signal the HAProxy servers to reload their configuration.

WordPress Updates

The code for core WordPress and the plugins we use are all WordPress-hosted Subversion repositories, which makes it relatively easy to automate updates with a script that updates all the local subversion repository clones.

First, we check to make sure we have no uncommitted changes:


% ls
Dockerfile       authsources.php  start.sh*        update.sh*       www/
% git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

Then we run the update script:


% ./update.sh
U    wp-admin/network/site-new.php
...
U    wp-includes/query.php
...

Fetching external item into 'wp-content/plugins/akismet':
Updated external to revision 1816638.

At revision 42486.
U    assets/lib/pandora/class-am-notification.php
...
Updated to revision 1816638.

If the updates included a core WordPress update, you can find the new WordPress version in version.php:


% grep ^\$wp_version www/wp-includes/version.php
$wp_version = '4.9.4';

Since we use git for our code management we just commit all the updates:


% git add www/
% git commit -m 'upgraded to 4.9.4'
[master 8578360] upgraded to 4.9.4
 127 files changed, 96421 insertions(+), 24956 deletions(-)
 create mode 100644 www/.svn/pristine/08/08a384840f04d252ac9edf86cd37b12239babdde.svn-base
 ...
 rewrite www/wp-admin/css/widgets-rtl.min.css (76%)
 ...

Then push them to our repo on code.stanford.edu:


% git push
...
To code.stanford.edu:et/itarch.git

Automatically Deploying Updates

When our Git repo receives new pushes it triggers a job on our Jenkins server. The Jenkins job builds a new itarch image, then pushes it out to our docker registry. Finally the job triggers another job to deploy the new image on our platform, replacing the existing containers.