diff --git a/README.md b/README.md
index 10a7a11..026d2f2 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,9 @@ Variable | Usage
`MYSQL_ROOT_PASSWORD` | Database root password
`MYSQL_APP_USER` | Database application user
`MYSQL_APP_PASSWORD` | Database application password
+`CLIENT_CERT_EMAIL` | email address for client certificate generated by `setup_test_ca.sh`
+`CLIENT_CERT_USERNAME` | user name for client certificate generated by `setup_test_ca.sh`
+`CLIENT_CERT_PASSWORD` | PKCS#12 keystore password for client certificate generated by `setup_test_ca.sh`
```shell
echo -e "MYSQL_ROOT_PASSWORD=$(openssl rand -base64 18)\nMYSQL_APP_USER=cacert_dev\nMYSQL_APP_PASSWORD=$(openssl rand -base64 18)" > .env
@@ -43,3 +46,7 @@ docker-compose up
```
After these steps you should be able to reach the CAcert application at https://test.cacert.localhost:8443/.
+The test manager application is reachable at https://mgr.cacert.localhost:9443/.
+
+A client certificate is created by `setup_test_ca.sh` and is placed in `testca/certs/clientcert.p12`
+which can be imported in a browser to support client certificate authentication.
diff --git a/application.Dockerfile b/application.Dockerfile
index f870bb7..89583f3 100644
--- a/application.Dockerfile
+++ b/application.Dockerfile
@@ -40,7 +40,12 @@ RUN apt-get update \
STOPSIGNAL SIGWINCH
COPY docker/apache-foreground /usr/local/bin/
-COPY testca/ /usr/local/etc/testca/
+COPY testca/root/ca.crt.pem /usr/local/share/ca-certificates/testca_root.crt
+COPY testca/class3/ca.crt.pem /usr/local/share/ca-certificates/testca_class3.crt
+COPY testca/certs/test.cacert.localhost.crt.pem testca/certs/secure.test.cacert.localhost.crt.pem /etc/ssl/certs/
+COPY testca/certs/test.cacert.localhost.key.pem testca/certs/secure.test.cacert.localhost.key.pem /etc/ssl/private/
+COPY testca/certs/cachain.crt.pem /etc/ssl/certs/combined.crt
+
COPY docker/apache-virtualhost.conf /etc/apache2/sites-available/
COPY docker/cacert.conf /etc/apache2/conf-available/
COPY docker/php5-cacert.ini /etc/php5/mods-available/cacert.ini
diff --git a/docker-compose.yml b/docker-compose.yml
index 5b0cf8b..6151567 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -19,6 +19,12 @@ services:
dockerfile: smtp.Dockerfile
volumes:
- maildir:/home/catchall/Maildir
+ mail:
+ build:
+ context: .
+ dockerfile: mail.Dockerfile
+ volumes:
+ - maildir:/home/catchall/Maildir
application:
build:
context: .
@@ -46,6 +52,19 @@ services:
- smtp
volumes:
- ./cacert-software:/www
+ mgr:
+ build:
+ context: .
+ dockerfile: mgr.Dockerfile
+ env_file:
+ - ./.env
+ ports:
+ - "9443:443"
+ depends_on:
+ - db
+ - mail
+ volumes:
+ - ./cacert-mgr:/var/www
volumes:
db: { }
diff --git a/docker/apache-foreground b/docker/apache-foreground
index e0c24d9..f83ae5a 100755
--- a/docker/apache-foreground
+++ b/docker/apache-foreground
@@ -4,16 +4,6 @@ set -eux
# Apache gets grumpy about PID files pre-existing
rm -f /run/apache2/apache2.pid
-cp /usr/local/etc/testca/certs/test.cacert.localhost.crt.pem /etc/ssl/certs/
-cp /usr/local/etc/testca/certs/test.cacert.localhost.key.pem /etc/ssl/private/
-(
- openssl x509 -in /usr/local/etc/testca/class3/ca.crt.pem
- openssl x509 -in /usr/local/etc/testca/root/ca.crt.pem
-) >/etc/ssl/certs/combined.crt
-
-cp /usr/local/etc/testca/certs/secure.test.cacert.localhost.crt.pem /etc/ssl/certs/
-cp /usr/local/etc/testca/certs/secure.test.cacert.localhost.key.pem /etc/ssl/private/
-
cp /usr/local/etc/application/feed.rss /www/pages/index/feed.rss
make -C /www/locale
diff --git a/docker/apache-mgr-foreground b/docker/apache-mgr-foreground
new file mode 100755
index 0000000..e7dc445
--- /dev/null
+++ b/docker/apache-mgr-foreground
@@ -0,0 +1,17 @@
+#!/bin/sh
+set -eux
+
+# Apache gets grumpy about PID files pre-existing
+rm -f /run/apache2/apache2.pid
+
+sed "s/@MYSQL_MGR_USER@/${MYSQL_MGR_USER}/g; s/@MYSQL_MGR_PASSWORD@/${MYSQL_MGR_PASSWORD}/g" \
+ /usr/local/etc/mgr-application.ini > /var/www/manager/application/configs/application.ini
+
+mysql -u "${MYSQL_MGR_USER}" -h db "-p${MYSQL_MGR_PASSWORD}" mgr <<-EOF
+REPLACE INTO system_user (id, system_role_id, login, user_client_crt_s_dn_i_dn)
+VALUES (2, 2,'${CLIENT_CERT_EMAIL}','/CN=${CLIENT_CERT_USERNAME}///C=AU/O=CAcert Inc./CN=Class 3 Test CA');
+EOF
+
+apache2ctl start "$@"
+
+exec tail -F --follow=name --retry /var/log/apache2/error.log
diff --git a/docker/apache-mgr-virtualhost.conf b/docker/apache-mgr-virtualhost.conf
new file mode 100644
index 0000000..707958b
--- /dev/null
+++ b/docker/apache-mgr-virtualhost.conf
@@ -0,0 +1,21 @@
+
+ ServerName mgr.cacert.localhost
+ ServerAlias www.mgr.cacert.localhost
+ DocumentRoot /var/www/manager/public
+
+ SSLEngine on
+ SSLStrictSNIVHostCheck on
+ SSLProtocol all -SSLv2 -SSLv3 -TLSv1
+ SSLHonorCipherOrder on
+ SSLCipherSuite kEECDH:kEDH:AESGCM:ALL:!3DES!RC4:!LOW:!EXP:!MD5:!aNULL:!eNULL
+ SSLCertificateFile /etc/ssl/certs/mgr.cacert.localhost.crt.pem
+ SSLCertificateKeyFile /etc/ssl/private/mgr.cacert.localhost.key.pem
+ SSLCertificateChainFile /etc/ssl/certs/combined.crt
+
+ SSLCACertificateFile /etc/ssl/certs/combined.crt
+ SSLVerifyClient require
+ SSLVerifyDepth 2
+ SSLOptions +StdEnvVars
+
+ Header always set Strict-Transport-Security "max-age=31536000"
+
diff --git a/docker/initdb.sh b/docker/initdb.sh
index 4a1cc21..83b34dd 100755
--- a/docker/initdb.sh
+++ b/docker/initdb.sh
@@ -4,15 +4,15 @@ set -eux
mysql -h localhost -u root "-p$MYSQL_ROOT_PASSWORD" <<-EOF
CREATE database cacert CHARSET latin1 COLLATE latin1_swedish_ci;
-CREATE USER $MYSQL_APP_USER@'%' IDENTIFIED BY '$MYSQL_APP_PASSWORD';
-GRANT CREATE TEMPORARY TABLES ON cacert.* TO $MYSQL_APP_USER@'%';
-GRANT SELECT, INSERT, UPDATE, DELETE ON cacert.* TO $MYSQL_APP_USER@'%';
+CREATE database mgr CHARSET utf8 COLLATE utf8_unicode_ci;
EOF
for script in /db_migrations/*.sh; do
sh "$script" -h localhost -u root "-p$MYSQL_ROOT_PASSWORD" cacert
done
+mysql -h localhost -u root "-p$MYSQL_ROOT_PASSWORD" mgr ca.cnf <certs/cachain.crt.pem
+fi
+
+if [ ! -f certs/cats.cacert.localhost.crt.pem ]; then
+ openssl req -new -keyout certs/cats.cacert.localhost.key.pem -nodes \
+ -out certs/cats.cacert.localhost.csr.pem -subj "/CN=cats.cacert.localhost" \
+ -addext "subjectAltName=DNS:cats.cacert.localhost,DNS:www.cats.cacert.localhost"
+ openssl ca -config ca.cnf \
+ -name class3_ca \
+ -in certs/cats.cacert.localhost.csr.pem \
+ -out certs/cats.cacert.localhost.crt.pem \
+ -rand_serial \
+ -extensions server_ext \
+ -batch
+fi
+if [ ! -f certs/cats-client.cacert.localhost.crt.pem ]; then
+ openssl req -new -keyout certs/cats-client.cacert.localhost.key.pem -nodes \
+ -out certs/cats-client.cacert.localhost.csr.pem -subj "/CN=cats.cacert.localhost" \
+ -addext "subjectAltName=DNS:cats.cacert.localhost"
+ openssl ca -config ca.cnf \
+ -name class3_ca \
+ -in certs/cats-client.cacert.localhost.csr.pem \
+ -out certs/cats-client.cacert.localhost.crt.pem \
+ -rand_serial \
+ -extensions client_ext \
+ -batch
+fi
+if [ ! -f certs/mgr.cacert.localhost.crt.pem ]; then
+ openssl req -new -keyout certs/mgr.cacert.localhost.key.pem -nodes \
+ -out certs/mgr.cacert.localhost.csr.pem -subj "/CN=mgr.cacert.localhost" \
+ -addext "subjectAltName=DNS:mgr.cacert.localhost,DNS:www.mgr.cacert.localhost"
+ openssl ca -config ca.cnf \
+ -name class3_ca \
+ -in certs/mgr.cacert.localhost.csr.pem \
+ -out certs/mgr.cacert.localhost.crt.pem \
+ -rand_serial \
+ -extensions server_ext \
+ -batch
+fi
+if [ ! -f certs/secure.test.cacert.localhost.crt.pem ]; then
+ openssl req -new -keyout certs/secure.test.cacert.localhost.key.pem -nodes \
+ -out certs/secure.test.cacert.localhost.csr.pem -subj "/CN=secure.test.cacert.localhost" \
+ -addext "subjectAltName=DNS:secure.test.cacert.localhost"
+ openssl ca -config ca.cnf \
+ -name class3_ca \
+ -in certs/secure.test.cacert.localhost.csr.pem \
+ -out certs/secure.test.cacert.localhost.crt.pem \
+ -rand_serial \
+ -extensions server_ext \
+ -batch
+fi
+if [ ! -f certs/test.cacert.localhost.crt.pem ]; then
+ openssl req -new -keyout certs/test.cacert.localhost.key.pem -nodes \
+ -out certs/test.cacert.localhost.csr.pem -subj "/CN=test.cacert.localhost" \
+ -addext "subjectAltName=DNS:test.cacert.localhost,DNS:www.test.cacert.localhost"
+ openssl ca -config ca.cnf \
+ -name class3_ca \
+ -in certs/test.cacert.localhost.csr.pem \
+ -out certs/test.cacert.localhost.crt.pem \
+ -rand_serial \
+ -extensions server_ext \
+ -batch
+fi
+
+if [ ! -f certs/testclient.p12 ]; then
+ openssl req -new -keyout certs/testclient.key.pem -nodes \
+ -out certs/testclient.csr.pem -subj "/CN=${CLIENT_CERT_USERNAME}" \
+ -addext "subjectAltName=email:${CLIENT_CERT_EMAIL}"
+ openssl ca -config ca.cnf \
+ -name class3_ca \
+ -in certs/testclient.csr.pem \
+ -out certs/testclient.crt.pem \
+ -rand_serial \
+ -extensions client_ext \
+ -batch
+ openssl pkcs12 -export -out certs/testclient.p12 \
+ -passout "pass:${CLIENT_CERT_PASSWORD}" \
+ -chain -CAfile certs/cachain.crt.pem \
+ -inkey certs/testclient.key.pem \
+ -in certs/testclient.crt.pem \
+ -name "${CLIENT_CERT_USERNAME}"
+fi