From c48e985d23baf29e5a49695a35e1dcdbc6cfb327 Mon Sep 17 00:00:00 2001 From: Asif Bacchus Date: Fri, 23 Jul 2021 17:19:58 -0600 Subject: [PATCH] feature(entrypoint): generate self-signed certificate - generate via specific invocation - auto-generate if SSL and no mounted certificate found - allow specifying hostname for certificate --- build/Dockerfile | 12 ++++++++--- build/entrypoint.sh | 48 ++++++++++++++++++++++++++++++++++++++++---- build/selfsigned.cnf | 16 +++++++++++++++ livereload.params | 8 ++++++++ 4 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 build/selfsigned.cnf diff --git a/build/Dockerfile b/build/Dockerfile index 7944526..ba9dfee 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -5,11 +5,14 @@ ARG NODE_VERSION=16 ARG ALPINE_VERSION=3.14 FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} -# create new node user with set id from build-arg +# create new node user with set id from build-arg and create volume directories ARG NODE_UID=9999 RUN deluser --remove-home node \ && addgroup -g ${NODE_UID} -S node \ - && adduser -G node -S -u ${NODE_UID} node + && adduser -G node -S -u ${NODE_UID} node \ + && mkdir /watch /certs \ + && chown root:node /certs \ + && chmod 770 /certs # create default volumes in-case user forgets, expose default port VOLUME [ "/watch", "/certs" ] @@ -40,6 +43,7 @@ ENV LR_EXCLUDE=".git/,.svn/,.vscode/,.idea/" ENV LR_DELAY=500 ENV LR_DEBUG=true ENV LR_HTTPS=true +ENV CERT_HOSTNAME="" # install livereload npm as node user then switch back to root user USER node @@ -51,11 +55,13 @@ RUN mkdir -p .npm-global/bin .npm-global/lib \ # copy scripts and fix-up all permissions USER root +COPY [ "selfsigned.cnf", "/etc/selfsigned.cnf" ] COPY [ "livereload.js", "/home/node/livereload.js" ] COPY [ "entrypoint.sh", "/usr/local/bin/entrypoint.sh" ] RUN chown node:node /home/node/livereload.js \ && chmod 644 /home/node/livereload.js \ - && chmod 755 /usr/local/bin/entrypoint.sh + && chmod 755 /usr/local/bin/entrypoint.sh \ + && chmod 644 /etc/selfsigned.cnf # switch to node user, run entrypoint script by default USER node diff --git a/build/entrypoint.sh b/build/entrypoint.sh index 7efafcb..7b60508 100644 --- a/build/entrypoint.sh +++ b/build/entrypoint.sh @@ -6,10 +6,26 @@ # functions certificateGenerateNew() { - certificateCheckEnabled + # generate self-signed certificate printf "\nGenerating new self-signed certificate:\n" - printf "Exporting new certificate:\n" - exit 0 + # shellcheck disable=SC3028 + if [ -z "$CERT_HOSTNAME" ]; then export CERT_HOSTNAME="$HOSTNAME"; fi + if ! openssl req -new -x509 -days 365 -nodes -out /certs/fullchain.pem -keyout /certs/privkey.pem -config /etc/selfsigned.cnf; then + printf "\nUnable to generate certificate. Is your 'certs' directory writable by this container?\n\n" + exit 55 + fi + printf "Exporting pfx certificate..." + if ! openssl pkcs12 -export -in /certs/fullchain.pem -inkey /certs/privkey.pem -out "/certs/${CERT_HOSTNAME}.pfx" -name "LiveReload" -passout pass:cert1234; then + printf "\nUnable to export generated certificate as PFX.\n\n" + exit 56 + fi + + # print message to user + printf "\n\nA self-signed certificate has been generated and saved in the location mounted to '/certs' in this container.\n" + printf "The certificate and private key are PEM formatted with names 'fullchain.pem' and 'privkey.pem', respectively.\n" + printf "If you need to import them to a Windows machine, please use the '%s.pfx' file with password 'cert1234'.\n\n" "$CERT_HOSTNAME" + + if [ "$1" != "noexit" ]; then exit 0; fi } certificateShow() { @@ -25,7 +41,6 @@ certificateExport() { } certificateCheckEnabled() { - httpsEnabled="$(convertCaseUpper "$LR_HTTPS")" if [ "$httpsEnabled" != "TRUE" ]; then printf "\nSSL/TLS not enabled. Please set LR_HTTPS=TRUE if you want to enable SSL/TLS.\n" exit 1 @@ -42,6 +57,7 @@ doCertNew=0 doCertShow=0 doServer=0 doShell=0 +httpsEnabled="$(convertCaseUpper "$LR_HTTPS")" # process action parameter case "$1" in @@ -70,6 +86,29 @@ esac # action: run server if [ "$doServer" -eq 1 ]; then + printf "Starting LiveReload server:\n" + + # https pre-flight check + if [ "$httpsEnabled" = "TRUE" ]; then + printf "[SSL/TLS mode enabled]\n" + if [ -n "$(find /certs/ -type d -empty -print)" ]; then + printf "[Generating certificate]\n" + # certs directory is empty --> auto-generate certificates + certificateGenerateNew 'noexit' + else + # certs directory contains certificates --> check if they can read + printf "[Checking mounted certificate]\n" + if ! [ -r "/certs/fullchain.pem" ]; then + printf "\nERROR: SSL/TLS mode selected but unable to read certificate!\n\n" + exit 51 + fi + if ! [ -r "/certs/privkey.pem" ]; then + printf "\nERROR: SSL/TLS mode selected but unable to read private key!\n\n" + exit 52 + fi + fi + printf "[Certificate OK]\n" + fi exec node livereload.js exit "$?" fi @@ -102,6 +141,7 @@ exit 99 # exit codes: # 0: normal exit, no errors # 1: invalid or invalid parameter passed to script +# 2: interactive shell required # 50: certificate errors # 51: unable to read certificate/chain # 52: unable to read private key diff --git a/build/selfsigned.cnf b/build/selfsigned.cnf new file mode 100644 index 0000000..128ebd2 --- /dev/null +++ b/build/selfsigned.cnf @@ -0,0 +1,16 @@ +default_bits = 4096 +default_md = sha256 +distinguished_name = dn +req_extensions = san +x509_extensions = san +prompt = no + +[dn] +organizationName = LiveReload WebServer +CN = ${ENV::CERT_HOSTNAME} + +[san] +subjectAltName = @alt_names + +[alt_names] +DNS.1 = ${ENV::CERT_HOSTNAME} diff --git a/livereload.params b/livereload.params index f47a771..c2f172e 100644 --- a/livereload.params +++ b/livereload.params @@ -93,4 +93,12 @@ # VALID OPTIONS: true, false #LR_HTTPS=true +# CERT_HOSTNAME: +# Hostname to use if container is auto-generating a self-signed certificate. +# REQUIRED: NO +# DEFAULT: $HOSTNAME +# VALID OPTIONS: Any valid hostname +#CERT_HOSTNAME=$HOSTNAME + + #EOF