Skip to content

Commit 300adf5

Browse files
committed
Add support for local certificates
1 parent ba170d9 commit 300adf5

13 files changed

+375
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
local.yml
2+
assets/certs/certs
3+
assets/certs/ca

README.md

+35
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,41 @@ When you change them, use `bin/moodle-docker-compose down && bin/moodle-docker-c
220220
| `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. |
221221
| `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. |
222222

223+
## SSL certificates
224+
225+
If you wish, you can generate a self-signed certificate repository and create your own certificates for your containers.
226+
227+
Certificates should be placed into the `assets/certs/certs` directory, with the certificate authority placed into `assets/certs/ca`.
228+
229+
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.
230+
231+
To make this easier, a helper has been created for Linux and MacOS which will:
232+
233+
- generate a new certificate authority if required
234+
- generate certificates
235+
- offer to install your new Certificate Authority
236+
237+
238+
The helper can be used as follows:
239+
240+
```
241+
./assets/certs/createcerts.sh hostname [alternative-hostname]
242+
```
243+
244+
You will need to run a separate command for each host you wish to generate a certificate for, for example:
245+
246+
```
247+
./asserts/certs/createcerts.sh webserver webserver.container.docker.internal
248+
./asserts/certs/createcerts.sh exttests exttests.container.docker.internal
249+
```
250+
251+
### Local hostnames
252+
253+
If you wish to access your containers using their certificates, you will need to add either:
254+
255+
- entries to your `/etc/hosts` or `%WinDir%\System32\Drivers\Etc\Hosts` file; or
256+
- relevant DNS entries to your local DNS resolver (for example dnsmasqd).
257+
223258
## Local customisations
224259

225260
In some situations you may wish to add local customisations, such as including additional containers, or changing existing containers.

assets/certs/443-default.conf

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<VirtualHost *:443>
2+
ServerName webserver
3+
DocumentRoot /var/www/html
4+
ErrorLog /var/log/apache2/error.log
5+
CustomLog /var/log/apache2/access.log combined
6+
SSLEngine on
7+
SSLCertificateFile "/etc/ssl/certs/moodle/webserver.crt"
8+
SSLCertificateKeyFile "/etc/ssl/certs/moodle/webserver.key"
9+
</VirtualHost>

assets/certs/README.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# IMPORTANT
2+
3+
These are test Certificates and Keys only!
4+
5+
Do NOT use them outside of a closed development environment.
6+
7+
DO NOT commit them to the git repository.
8+
9+
## Generating new certificates
10+
11+
A helper script exists at the project root and takes a list of names for the certificate.
12+
13+
For example:
14+
15+
```
16+
./createcerts.sh webserver webserver.container.docker.internal
17+
```
18+
19+
Each argument is used as a subject alternative name for the certificate.

assets/certs/createcerts.sh

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/bin/bash
2+
3+
CERTDIR="`pwd`/assets/certs/"
4+
5+
CACONF="${CERTDIR}/openssl.cnf"
6+
CAKEY="${CERTDIR}/ca/ca.key"
7+
CACERT="${CERTDIR}/ca/ca.pem"
8+
9+
if [ -f "$CAKEY" ] && [ -f "${CACERT}" ]; then
10+
echo "Using existing CA private key"
11+
echo
12+
else
13+
# Generate the private key for the CA:
14+
echo "Generating the key and certificate for the CA server"
15+
mkdir -p "${CERTDIR}/ca"
16+
mkdir -p "${CERTDIR}/certs"
17+
18+
# Generate the key and certificate for the CA.
19+
cat <<EOF | openssl req -config ${CACONF} -nodes -new -x509 -keyout "${CAKEY}" -out "${CACERT}"
20+
AU
21+
Western Australia
22+
Perth
23+
Moodle Pty Ltd
24+
Moodle LMS
25+
26+
27+
EOF
28+
29+
echo "Generated an OpenSSL Certificate Authority"
30+
touch "${CERTDIR}/ca/index.txt"
31+
echo '01' > "${CERTDIR}/ca/serial.txt"
32+
echo
33+
echo "You should add this certificate to your root certificate store."
34+
35+
OS=`uname -s`
36+
if [ "${OS}" = "Darwin" ]
37+
then
38+
echo "You can use the following command:"
39+
echo "sudo security add-trusted-cert -d -r trustRoot -k '/Library/Keychains/System.keychain' ${CACERT}"
40+
read -p "Do you want me to do that for you now? " yn
41+
case $yn in
42+
[Yy]* ) sudo security add-trusted-cert -d -r trustRoot -k '/Library/Keychains/System.keychain' "${CACERT}"; break;;
43+
esac
44+
fi
45+
46+
if [ "${OS}" = "Linux" ]
47+
then
48+
echo "You can use the following command:"
49+
echo "sudo cp ${CERTDIR}/ca/ca.pem usr/local/share/ca-certificates/moodle-docker-ca.crt && sudo update-ca-certificates"
50+
read -p "Do you want me to do that for you now? " yn
51+
case $yn in
52+
[Yy]* ) sudo cp "${CERTDIR}/ca/ca.pem" usr/local/share/ca-certificates/moodle-docker-ca.crt && sudo update-ca-certificates; break;;
53+
esac
54+
55+
fi
56+
fi
57+
58+
if [ "$#" -lt 1 ]
59+
then
60+
echo "Usage: Must supply at least one hostname."
61+
exit 1
62+
fi
63+
64+
# The first hostname is canonical.
65+
DOMAIN=$1
66+
67+
HOSTKEY="${CERTDIR}/certs/${DOMAIN}.key"
68+
HOSTCSR="${CERTDIR}/certs/${DOMAIN}.csr"
69+
HOSTCRT="${CERTDIR}/certs/${DOMAIN}.crt"
70+
HOSTEXT="${CERTDIR}/certs/${DOMAIN}.ext"
71+
72+
# Create a private key for the dev site:
73+
echo
74+
echo "Generating a private key for the $DOMAIN dev site"
75+
echo
76+
openssl genrsa -out "${HOSTKEY}" 2048
77+
78+
echo "Generating a CSR for $DOMAIN"
79+
cat <<EOF | openssl req -nodes -new -key "${HOSTKEY}" -out "${HOSTCSR}"
80+
AU
81+
Western Australia
82+
Perth
83+
Moodle Pty Ltd
84+
Moodle LMS
85+
86+
87+
EOF
88+
echo
89+
90+
DNSCOUNT=1
91+
for var in "$@"
92+
do
93+
DNS=$(cat <<-EOF
94+
${DNS}
95+
DNS.${DNSCOUNT} = ${var}
96+
EOF
97+
)
98+
DNSCOUNT=$((DNSCOUNT + 1))
99+
done
100+
101+
cat > "${HOSTEXT}" << EOF
102+
[ req ]
103+
default_bits = 2048
104+
default_keyfile = ${HOSTKEY}
105+
distinguished_name = server_distinguished_name
106+
req_extensions = server_req_extensions
107+
string_mask = utf8only
108+
109+
[ server_distinguished_name ]
110+
111+
countryName = Country Name (2 letter code)
112+
countryName_default = AU
113+
114+
stateOrProvinceName = State or Province Name (full name)
115+
stateOrProvinceName_default = Western Australia
116+
117+
localityName = Locality Name (eg, city)
118+
localityName_default = Perth
119+
120+
organizationName = Organization Name (eg, company)
121+
organizationName_default = Moodle Pty Ltd
122+
123+
organizationalUnitName = Organizational Unit (eg, division)
124+
organizationalUnitName_default = Moodle LMS
125+
126+
commonName = Common Name (e.g. server FQDN or YOUR name)
127+
commonName_default = ${DOMAIN}
128+
129+
emailAddress = Email Address
130+
emailAddress_default = [email protected]
131+
132+
[ server_req_extensions ]
133+
subjectKeyIdentifier = hash
134+
basicConstraints = CA:FALSE
135+
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
136+
subjectAltName = @alternate_names
137+
[ alternate_names ]
138+
$DNS
139+
EOF
140+
141+
#Next run the command to create the certificate: using our CSR, the CA private key, the CA certificate, and the config file:
142+
echo "Generating a certificate for $DOMAIN"
143+
cat <<EOF | openssl req -config "${HOSTEXT}" -newkey rsa:2048 -sha256 -nodes -out "${HOSTCSR}" -outform PEM
144+
AU
145+
Western Australia
146+
Perth
147+
Moodle Pty Ltd
148+
Moodle LMS
149+
150+
151+
EOF
152+
echo
153+
154+
echo "Signing the request"
155+
openssl ca -config "${CACONF}" -policy signing_policy -extensions signing_req -out "${HOSTCRT}" -infiles "${HOSTCSR}"

assets/certs/enable_ssl.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env bash
2+
set -Eeo pipefail
3+
4+
a2enmod ssl

assets/certs/install_ca.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env bash
2+
set -Eeo pipefail
3+
4+
update-ca-certificates -v

assets/certs/openssl.cnf

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
HOME = .
2+
RANDFILE = $ENV::HOME/.rnd
3+
4+
####################################################################
5+
[ ca ]
6+
default_ca = CA_default # The default ca section
7+
8+
[ CA_default ]
9+
10+
default_days = 365 # How long to certify for
11+
default_crl_days = 30 # How long before next CRL
12+
default_md = sha256 # Use public key default MD
13+
preserve = no # Keep passed DN ordering
14+
15+
x509_extensions = ca_extensions # The extensions to add to the cert
16+
17+
email_in_dn = no # Don't concat the email in the DN
18+
copy_extensions = copy # Required to copy SANs from CSR to cert
19+
20+
base_dir = assets/certs
21+
certificate = $base_dir/ca/ca.pem # The CA certifcate
22+
private_key = $base_dir/ca/ca.key # The CA private key
23+
new_certs_dir = $base_dir/certs # Location for new certs after signing
24+
database = $base_dir/ca/index.txt # Database index file
25+
serial = $base_dir/ca/serial.txt # The current serial number
26+
27+
unique_subject = no # Set to 'no' to allow creation of
28+
# several certificates with same subject.
29+
30+
31+
####################################################################
32+
[ req ]
33+
default_bits = 4096
34+
default_keyfile = cakey.pem
35+
distinguished_name = ca_distinguished_name
36+
x509_extensions = ca_extensions
37+
string_mask = utf8only
38+
39+
####################################################################
40+
[ ca_distinguished_name ]
41+
countryName = Country Name (2 letter code)
42+
countryName_default = AU
43+
44+
stateOrProvinceName = State or Province Name (full name)
45+
stateOrProvinceName_default = Western Australia
46+
47+
localityName = Locality Name (eg, city)
48+
localityName_default = Perth
49+
50+
organizationName = Organization Name (eg, company)
51+
organizationName_default = Moodle Pty Ltd
52+
53+
organizationalUnitName = Organizational Unit (eg, division)
54+
organizationalUnitName_default = Moodle LMS
55+
56+
commonName = Common Name (e.g. server FQDN or YOUR name)
57+
commonName_default = Testing CA
58+
59+
emailAddress = Email Address
60+
emailAddress_default = [email protected]
61+
62+
####################################################################
63+
[ ca_extensions ]
64+
65+
subjectKeyIdentifier = hash
66+
authorityKeyIdentifier = keyid:always, issuer
67+
basicConstraints = critical, CA:true
68+
keyUsage = keyCertSign, cRLSign
69+
70+
####################################################################
71+
[ signing_policy ]
72+
countryName = optional
73+
stateOrProvinceName = optional
74+
localityName = optional
75+
organizationName = optional
76+
organizationalUnitName = optional
77+
commonName = supplied
78+
emailAddress = optional
79+
80+
####################################################################
81+
[ signing_req ]
82+
subjectKeyIdentifier = hash
83+
authorityKeyIdentifier = keyid,issuer
84+
basicConstraints = CA:FALSE
85+
keyUsage = digitalSignature, keyEncipherment

assets/docker-php-entrypoint-moodle

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env bash
2+
set -Eeo pipefail
3+
4+
docker_process_init_files() {
5+
echo
6+
local f
7+
for f; do
8+
case "$f" in
9+
*.sh)
10+
if [ -x "$f" ]; then
11+
echo "$0: running $f"
12+
"$f"
13+
else
14+
echo "$0: sourcing $f"
15+
. "$f"
16+
fi
17+
;;
18+
esac
19+
done
20+
}
21+
22+
echo "Running entrypoint files from /docker-entrypoint-initdb.d/*"
23+
docker_process_init_files /docker-entrypoint-initdb.d/*
24+
25+
echo "Starting docker=php-entrypoint with $@"
26+
/usr/local/bin/docker-php-entrypoint "$@"

base.yml

+3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ services:
77
volumes:
88
- "${MOODLE_DOCKER_WWWROOT}:/var/www/html"
99
- "${ASSETDIR}/web/apache2_faildumps.conf:/etc/apache2/conf-enabled/apache2_faildumps.conf"
10+
- "${ASSETDIR}/docker-php-entrypoint-moodle:/usr/local/bin/docker-php-entrypoint-moodle"
1011
environment:
1112
MOODLE_DOCKER_DBNAME: moodle
1213
MOODLE_DOCKER_DBUSER: moodle
1314
MOODLE_DOCKER_DBPASS: "m@0dl3ing"
1415
MOODLE_DOCKER_BROWSER: firefox
1516
MOODLE_DOCKER_WEB_HOST: "${MOODLE_DOCKER_WEB_HOST}"
17+
entrypoint: 'docker-php-entrypoint-moodle'
18+
command: 'apache2-foreground'
1619
exttests:
1720
image: moodlehq/moodle-exttests
1821
volumes:

bin/moodle-docker-compose

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ fi
3232
dockercompose="${dockercompose} -f ${basedir}/base.yml"
3333
dockercompose="${dockercompose} -f ${basedir}/service.mail.yml"
3434

35+
if [ -f assets/certs/ca/ca.pem ]
36+
then
37+
dockercompose="${dockercompose} -f ${basedir}/ssl.yml"
38+
fi
39+
3540
# PHP Version.
3641
export MOODLE_DOCKER_PHP_VERSION=${MOODLE_DOCKER_PHP_VERSION:-8.0}
3742

bin/moodle-docker-compose.cmd

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ SET COMPOSE_CONVERT_WINDOWS_PATHS=true
2121
SET DOCKERCOMPOSE=docker-compose -f "%BASEDIR%\base.yml"
2222
SET DOCKERCOMPOSE=%DOCKERCOMPOSE% -f "%BASEDIR%\service.mail.yml"
2323

24+
IF EXIST "%ASSETDIR%\certs\ca\ca.pem" (
25+
REM A Certificate Authority certificate exists. Add the ssl configuration.
26+
SET DOCKERCOMPOSE=%DOCKERCOMPOSE% -f "%BASEDIR%\ssl.yml"
27+
)
28+
2429
IF "%MOODLE_DOCKER_PHP_VERSION%"=="" (
2530
SET MOODLE_DOCKER_PHP_VERSION=8.0
2631
)

0 commit comments

Comments
 (0)