Skip to content

Commit

Permalink
Add support for local certificates
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Feb 14, 2023
1 parent ba170d9 commit 300adf5
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
local.yml
assets/certs/certs
assets/certs/ca
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,41 @@ When you change them, use `bin/moodle-docker-compose down && bin/moodle-docker-c
| `MOODLE_DOCKER_APP_RUNTIME` | no | 'ionic3' or 'ionic5' | not set | Set this to indicate the runtime being used in the Moodle app. In most cases, this can be ignored because the runtime is guessed automatically (except on Windows using the `.cmd` binary). In case you need to set it manually and you're not sure which one it is, versions 3.9.5 and later should be using Ionic 5. |
| `MOODLE_DOCKER_APP_NODE_VERSION` | no | [node](https://hub.docker.com/_/node) image version tag | not set | Node version to run the app. In most cases, this can be ignored because the version is parsed from the project's `.nvmrc` file. This will only be used when the runtime is `ionic5` and the app is running from the local filesystem. |

## SSL certificates

If you wish, you can generate a self-signed certificate repository and create your own certificates for your containers.

Certificates should be placed into the `assets/certs/certs` directory, with the certificate authority placed into `assets/certs/ca`.

If moodle-docker-compose detects the certificate for a local certificate authority at `asserts/certs/ca/ca.pem` then an additional SSL configuration will be included.

To make this easier, a helper has been created for Linux and MacOS which will:

- generate a new certificate authority if required
- generate certificates
- offer to install your new Certificate Authority


The helper can be used as follows:

```
./assets/certs/createcerts.sh hostname [alternative-hostname]
```

You will need to run a separate command for each host you wish to generate a certificate for, for example:

```
./asserts/certs/createcerts.sh webserver webserver.container.docker.internal
./asserts/certs/createcerts.sh exttests exttests.container.docker.internal
```

### Local hostnames

If you wish to access your containers using their certificates, you will need to add either:

- entries to your `/etc/hosts` or `%WinDir%\System32\Drivers\Etc\Hosts` file; or
- relevant DNS entries to your local DNS resolver (for example dnsmasqd).

## Local customisations

In some situations you may wish to add local customisations, such as including additional containers, or changing existing containers.
Expand Down
9 changes: 9 additions & 0 deletions assets/certs/443-default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<VirtualHost *:443>
ServerName webserver
DocumentRoot /var/www/html
ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
SSLEngine on
SSLCertificateFile "/etc/ssl/certs/moodle/webserver.crt"
SSLCertificateKeyFile "/etc/ssl/certs/moodle/webserver.key"
</VirtualHost>
19 changes: 19 additions & 0 deletions assets/certs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# IMPORTANT

These are test Certificates and Keys only!

Do NOT use them outside of a closed development environment.

DO NOT commit them to the git repository.

## Generating new certificates

A helper script exists at the project root and takes a list of names for the certificate.

For example:

```
./createcerts.sh webserver webserver.container.docker.internal
```

Each argument is used as a subject alternative name for the certificate.
155 changes: 155 additions & 0 deletions assets/certs/createcerts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#!/bin/bash

CERTDIR="`pwd`/assets/certs/"

CACONF="${CERTDIR}/openssl.cnf"
CAKEY="${CERTDIR}/ca/ca.key"
CACERT="${CERTDIR}/ca/ca.pem"

if [ -f "$CAKEY" ] && [ -f "${CACERT}" ]; then
echo "Using existing CA private key"
echo
else
# Generate the private key for the CA:
echo "Generating the key and certificate for the CA server"
mkdir -p "${CERTDIR}/ca"
mkdir -p "${CERTDIR}/certs"

# Generate the key and certificate for the CA.
cat <<EOF | openssl req -config ${CACONF} -nodes -new -x509 -keyout "${CAKEY}" -out "${CACERT}"
AU
Western Australia
Perth
Moodle Pty Ltd
Moodle LMS
EOF

echo "Generated an OpenSSL Certificate Authority"
touch "${CERTDIR}/ca/index.txt"
echo '01' > "${CERTDIR}/ca/serial.txt"
echo
echo "You should add this certificate to your root certificate store."

OS=`uname -s`
if [ "${OS}" = "Darwin" ]
then
echo "You can use the following command:"
echo "sudo security add-trusted-cert -d -r trustRoot -k '/Library/Keychains/System.keychain' ${CACERT}"
read -p "Do you want me to do that for you now? " yn
case $yn in
[Yy]* ) sudo security add-trusted-cert -d -r trustRoot -k '/Library/Keychains/System.keychain' "${CACERT}"; break;;
esac
fi

if [ "${OS}" = "Linux" ]
then
echo "You can use the following command:"
echo "sudo cp ${CERTDIR}/ca/ca.pem usr/local/share/ca-certificates/moodle-docker-ca.crt && sudo update-ca-certificates"
read -p "Do you want me to do that for you now? " yn
case $yn in
[Yy]* ) sudo cp "${CERTDIR}/ca/ca.pem" usr/local/share/ca-certificates/moodle-docker-ca.crt && sudo update-ca-certificates; break;;
esac

fi
fi

if [ "$#" -lt 1 ]
then
echo "Usage: Must supply at least one hostname."
exit 1
fi

# The first hostname is canonical.
DOMAIN=$1

HOSTKEY="${CERTDIR}/certs/${DOMAIN}.key"
HOSTCSR="${CERTDIR}/certs/${DOMAIN}.csr"
HOSTCRT="${CERTDIR}/certs/${DOMAIN}.crt"
HOSTEXT="${CERTDIR}/certs/${DOMAIN}.ext"

# Create a private key for the dev site:
echo
echo "Generating a private key for the $DOMAIN dev site"
echo
openssl genrsa -out "${HOSTKEY}" 2048

echo "Generating a CSR for $DOMAIN"
cat <<EOF | openssl req -nodes -new -key "${HOSTKEY}" -out "${HOSTCSR}"
AU
Western Australia
Perth
Moodle Pty Ltd
Moodle LMS
EOF
echo

DNSCOUNT=1
for var in "$@"
do
DNS=$(cat <<-EOF
${DNS}
DNS.${DNSCOUNT} = ${var}
EOF
)
DNSCOUNT=$((DNSCOUNT + 1))
done

cat > "${HOSTEXT}" << EOF
[ req ]
default_bits = 2048
default_keyfile = ${HOSTKEY}
distinguished_name = server_distinguished_name
req_extensions = server_req_extensions
string_mask = utf8only
[ server_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = AU
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Western Australia
localityName = Locality Name (eg, city)
localityName_default = Perth
organizationName = Organization Name (eg, company)
organizationName_default = Moodle Pty Ltd
organizationalUnitName = Organizational Unit (eg, division)
organizationalUnitName_default = Moodle LMS
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = ${DOMAIN}
emailAddress = Email Address
emailAddress_default = [email protected]
[ server_req_extensions ]
subjectKeyIdentifier = hash
basicConstraints = CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alternate_names
[ alternate_names ]
$DNS
EOF

#Next run the command to create the certificate: using our CSR, the CA private key, the CA certificate, and the config file:
echo "Generating a certificate for $DOMAIN"
cat <<EOF | openssl req -config "${HOSTEXT}" -newkey rsa:2048 -sha256 -nodes -out "${HOSTCSR}" -outform PEM
AU
Western Australia
Perth
Moodle Pty Ltd
Moodle LMS
EOF
echo

echo "Signing the request"
openssl ca -config "${CACONF}" -policy signing_policy -extensions signing_req -out "${HOSTCRT}" -infiles "${HOSTCSR}"
4 changes: 4 additions & 0 deletions assets/certs/enable_ssl.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -Eeo pipefail

a2enmod ssl
4 changes: 4 additions & 0 deletions assets/certs/install_ca.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -Eeo pipefail

update-ca-certificates -v
85 changes: 85 additions & 0 deletions assets/certs/openssl.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
HOME = .
RANDFILE = $ENV::HOME/.rnd

####################################################################
[ ca ]
default_ca = CA_default # The default ca section

[ CA_default ]

default_days = 365 # How long to certify for
default_crl_days = 30 # How long before next CRL
default_md = sha256 # Use public key default MD
preserve = no # Keep passed DN ordering

x509_extensions = ca_extensions # The extensions to add to the cert

email_in_dn = no # Don't concat the email in the DN
copy_extensions = copy # Required to copy SANs from CSR to cert

base_dir = assets/certs
certificate = $base_dir/ca/ca.pem # The CA certifcate
private_key = $base_dir/ca/ca.key # The CA private key
new_certs_dir = $base_dir/certs # Location for new certs after signing
database = $base_dir/ca/index.txt # Database index file
serial = $base_dir/ca/serial.txt # The current serial number

unique_subject = no # Set to 'no' to allow creation of
# several certificates with same subject.


####################################################################
[ req ]
default_bits = 4096
default_keyfile = cakey.pem
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
string_mask = utf8only

####################################################################
[ ca_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = AU

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Western Australia

localityName = Locality Name (eg, city)
localityName_default = Perth

organizationName = Organization Name (eg, company)
organizationName_default = Moodle Pty Ltd

organizationalUnitName = Organizational Unit (eg, division)
organizationalUnitName_default = Moodle LMS

commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = Testing CA

emailAddress = Email Address
emailAddress_default = [email protected]

####################################################################
[ ca_extensions ]

subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints = critical, CA:true
keyUsage = keyCertSign, cRLSign

####################################################################
[ signing_policy ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

####################################################################
[ signing_req ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
26 changes: 26 additions & 0 deletions assets/docker-php-entrypoint-moodle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -Eeo pipefail

docker_process_init_files() {
echo
local f
for f; do
case "$f" in
*.sh)
if [ -x "$f" ]; then
echo "$0: running $f"
"$f"
else
echo "$0: sourcing $f"
. "$f"
fi
;;
esac
done
}

echo "Running entrypoint files from /docker-entrypoint-initdb.d/*"
docker_process_init_files /docker-entrypoint-initdb.d/*

echo "Starting docker=php-entrypoint with $@"
/usr/local/bin/docker-php-entrypoint "$@"
3 changes: 3 additions & 0 deletions base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ services:
volumes:
- "${MOODLE_DOCKER_WWWROOT}:/var/www/html"
- "${ASSETDIR}/web/apache2_faildumps.conf:/etc/apache2/conf-enabled/apache2_faildumps.conf"
- "${ASSETDIR}/docker-php-entrypoint-moodle:/usr/local/bin/docker-php-entrypoint-moodle"
environment:
MOODLE_DOCKER_DBNAME: moodle
MOODLE_DOCKER_DBUSER: moodle
MOODLE_DOCKER_DBPASS: "m@0dl3ing"
MOODLE_DOCKER_BROWSER: firefox
MOODLE_DOCKER_WEB_HOST: "${MOODLE_DOCKER_WEB_HOST}"
entrypoint: 'docker-php-entrypoint-moodle'
command: 'apache2-foreground'
exttests:
image: moodlehq/moodle-exttests
volumes:
Expand Down
5 changes: 5 additions & 0 deletions bin/moodle-docker-compose
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ fi
dockercompose="${dockercompose} -f ${basedir}/base.yml"
dockercompose="${dockercompose} -f ${basedir}/service.mail.yml"

if [ -f assets/certs/ca/ca.pem ]
then
dockercompose="${dockercompose} -f ${basedir}/ssl.yml"
fi

# PHP Version.
export MOODLE_DOCKER_PHP_VERSION=${MOODLE_DOCKER_PHP_VERSION:-8.0}

Expand Down
5 changes: 5 additions & 0 deletions bin/moodle-docker-compose.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ SET COMPOSE_CONVERT_WINDOWS_PATHS=true
SET DOCKERCOMPOSE=docker-compose -f "%BASEDIR%\base.yml"
SET DOCKERCOMPOSE=%DOCKERCOMPOSE% -f "%BASEDIR%\service.mail.yml"

IF EXIST "%ASSETDIR%\certs\ca\ca.pem" (
REM A Certificate Authority certificate exists. Add the ssl configuration.
SET DOCKERCOMPOSE=%DOCKERCOMPOSE% -f "%BASEDIR%\ssl.yml"
)

IF "%MOODLE_DOCKER_PHP_VERSION%"=="" (
SET MOODLE_DOCKER_PHP_VERSION=8.0
)
Expand Down
Loading

0 comments on commit 300adf5

Please sign in to comment.