Compare commits

...

10 Commits

Author SHA1 Message Date
1e7531e632 chore: update and use consistent project name 2021-07-23 18:36:14 -06:00
b2a02407b9 refactor(compose): use proper image tag 2021-07-23 18:29:38 -06:00
f196697569 chore(dockerfile): re-enable extra labels 2021-07-23 18:22:40 -06:00
f77961145e struct(entrypoint): add notation for HTTP-only mode 2021-07-23 18:20:47 -06:00
3001dbca73 feature(entrypoint): show loaded certificate
- warn if private key missing
- change error return codes
2021-07-23 18:19:36 -06:00
5ded2bc320 refactor(entrypoint): use lowercase boolean values
- convert boolean environment vars to lowercase
- allows single use between shell and JavaScript
2021-07-23 18:04:48 -06:00
a184866de3 refactor(entrypoint): remove export function
- never a need to export keypair, cert is always enough
2021-07-23 17:53:42 -06:00
c48e985d23 feature(entrypoint): generate self-signed certificate
- generate via specific invocation
- auto-generate if SSL and no mounted certificate found
- allow specifying hostname for certificate
2021-07-23 17:19:58 -06:00
e8d238f3c3 feature(compose): sample stack with NGINX server 2021-07-23 14:48:50 -06:00
8f35aaef11 feature: allow non-secure connections
- default to secure
- parameterize via env var
2021-07-23 13:31:01 -06:00
6 changed files with 273 additions and 40 deletions

View File

@ -1,15 +1,18 @@
# LiveReload-npm server supporting SSL/TLS
# node-livereload server supporting SSL/TLS
# allow dynamic building by specifying base image elements as build-args
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" ]
@ -23,8 +26,8 @@ RUN apk --update --no-cache add \
# labels
LABEL org.opencontainers.image.authors="Asif Bacchus <asif@asifbacchus.dev>"
LABEL org.opencontainers.image.title="livereload npm"
LABEL org.opencontainers.image.description="Dockerized npm livereload supporting TLS and running under limited user account. Environment variables allow specifying files to watch/exclude and notification delay."
LABEL org.opencontainers.image.title="node-livereload-tls"
LABEL org.opencontainers.image.description="Dockerized node-livereload supporting TLS and running under limited user account. Environment variables allow specifying files to watch/exclude and notification delay."
LABEL org.opencontainers.image.url="https://git.asifbacchus.dev/ab-docker/livereload"
LABEL org.opencontainers.image.documentation="https://git.asifbacchus.dev/ab-docker/livereload/raw/branch/master/README.md"
LABEL org.opencontainers.image.source="https://git.asifbacchus.dev/ab-docker/livereload.git"
@ -39,8 +42,10 @@ ENV LR_EXTS="html,xml,css,js,jsx,ts,tsx,php,py"
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
# install node-livereload as node user then switch back to root user
USER node
WORKDIR /home/node
RUN mkdir -p .npm-global/bin .npm-global/lib \
@ -50,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
@ -62,13 +69,12 @@ WORKDIR /home/node
ENTRYPOINT [ "/sbin/tini", "--", "/usr/local/bin/entrypoint.sh" ]
# set build timestamp and version labels
# TODO: uncomment when done testing
#ARG INTERNAL_VERSION
#ARG BUILD_DATE
#LABEL org.opencontainers.image.version="16.5.0, 0.9.3"
#LABEL org.opencontainers.image.vendor="NODE.js, node-livereload"
#LABEL dev.asifbacchus.image.name="livereload-tls-npm"
#LABEL dev.asifbacchus.image.version=${INTERNAL_VERSION}
#LABEL org.opencontainers.image.created=${BUILD_DATE}
ARG INTERNAL_VERSION
ARG BUILD_DATE
LABEL org.opencontainers.image.version="16.5.0, 0.9.3"
LABEL org.opencontainers.image.vendor="NODE.js, node-livereload"
LABEL dev.asifbacchus.image.name="node-livereload-tls"
LABEL dev.asifbacchus.image.version=${INTERNAL_VERSION}
LABEL org.opencontainers.image.created=${BUILD_DATE}
#EOF

View File

@ -1,33 +1,76 @@
#!/bin/sh
#
# entrypoint script for livereload-tls-npm container
# entrypoint script for node-livereload-tls container
#
# functions
certificateCheckExist() {
if [ -n "$(find /certs/ -type d -empty -print)" ]; then
printf "noexist"
elif ! [ -r "/certs/fullchain.pem" ]; then
printf "noread_certificate"
elif ! [ -r "/certs/privkey.pem" ]; then
printf "noread_key"
else
printf "ok"
fi
}
certificateGenerateNew() {
# generate self-signed certificate and export as PFX
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
# 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 "Remember to import 'fullchain.pem' to the trusted store on any client machines or you will get warnings.\n\n"
}
certificateShow() {
printf "\nCurrently loaded certificate:\n"
exit 0
certStatus="$(certificateCheckExist)"
case "$certStatus" in
noexist)
printf "[ERROR]: No certificate is loaded (certificate directory empty).\n\n"
exit 51
;;
noread_certificate)
printf "[ERROR]: Cannot read loaded certificate.\n\n"
exit 52
;;
noread_key)
printf "\n[WARNING]: Cannot find private key associated with certificate!\n\n"
;;
esac
if ! openssl x509 -noout -text -nameopt align,multiline -certopt no_pubkey,no_sigdump -in /certs/fullchain.pem; then
printf "\n[ERROR]: Unable to display loaded certificate.\n\n"
exit 52
fi
}
certificateExport() {
printf "\nExporting currently loaded certificate:\n"
exit 0
convertCaseLower() {
printf "%s" "$1" | tr "[:upper:]" "[:lower:]"
}
# default variable values
doCertExport=0
doCertNew=0
doCertShow=0
doServer=0
doShell=0
# clean-up boolean environment variables for this script and JavaScript
enableHTTPS="$(convertCaseLower "$LR_HTTPS")"
enableDebug="$(convertCaseLower "$LR_DEBUG")"
export LR_HTTPS="$enableHTTPS"
export LR_DEBUG="$enableDebug"
# process action parameter
case "$1" in
listen | server | run | start)
@ -42,19 +85,44 @@ new-cert)
show-cert)
doCertShow=1
;;
export-cert)
doCertExport=1
;;
*)
# invalid or unknown option
printf "\nUnknown action requested: %s\n" "$1"
printf "Valid actions: [listen | server | run | start] | shell | new-cert | show-cert | export-cert\n\n"
printf "Valid actions: [listen | server | run | start] | shell | new-cert | show-cert\n\n"
exit 1
;;
esac
# action: run server
if [ "$doServer" -eq 1 ]; then
printf "Starting node-livereload-tls server:\n"
# https pre-flight check
if [ "$enableHTTPS" = "true" ]; then
printf "[SSL/TLS mode enabled]\n"
certStatus="$(certificateCheckExist)"
case "$certStatus" in
noexist)
printf "[Generating certificate]\n"
certificateGenerateNew
;;
noread_certificate)
printf "[Checking mounted certificate]"
printf "\nERROR: SSL/TLS mode selected but unable to read certificate!\n\n"
exit 52
;;
noread_key)
printf "[Checking mounted certificate]"
printf "\nERROR: SSL/TLS mode selected but unable to read private key!\n\n"
exit 53
;;
ok)
printf "[Certificate OK]\n"
;;
esac
else
printf "[HTTP mode enabled]\n"
fi
exec node livereload.js
exit "$?"
fi
@ -73,13 +141,16 @@ if [ "$doShell" -eq 1 ]; then
fi
# action: generate new self-signed certificate
if [ "$doCertNew" -eq 1 ]; then certificateGenerateNew; fi
if [ "$doCertNew" -eq 1 ]; then
certificateGenerateNew
exit 0
fi
# action: show loaded certificate
if [ "$doCertShow" -eq 1 ]; then certificateShow; fi
# action: export loaded certificate
if [ "$doCertExport" -eq 1 ]; then certificateExport; fi
if [ "$doCertShow" -eq 1 ]; then
certificateShow
exit 0
fi
# failsafe exit - terminate with code 99: this code should never be executed!
exit 99
@ -87,11 +158,12 @@ 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
# 51: certificate directory empty
# 52: unable to read certificate/chain
# 53: unable to read private key
# 55: unable to generate new certificate
# 56: unable to export certificate, likely write error
# 99: code error
#EOF

View File

@ -4,13 +4,8 @@
let livereload = require('livereload');
// set createServer options
const https = require('https');
const fs = require('fs');
const options = {
https: {
cert: fs.readFileSync('/certs/fullchain.pem'),
key: fs.readFileSync('/certs/privkey.pem')
},
port: process.env.LR_PORT,
exts: process.env.LR_EXTS,
exclusions: process.env.LR_EXCLUDE,
@ -19,6 +14,14 @@ const options = {
debug: process.env.LR_DEBUG
};
if (process.env.LR_HTTPS) {
options.https = {
cert: fs.readFileSync('/certs/fullchain.pem'),
key: fs.readFileSync('/certs/privkey.pem')
};
}
// start server
let server = livereload.createServer(options);
server.watch('/watch')

16
build/selfsigned.cnf Normal file
View File

@ -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}

32
docker-compose.yml Normal file
View File

@ -0,0 +1,32 @@
#
# node-livereload-tls stack
#
version: '2.4'
services:
ab-nginx:
image: docker.asifbacchus.dev/nginx/ab-nginx:latest
container_name: ab-nginx
volumes:
- ./certs/certs:ro
- ${WATCHDIR}:/usr/share/nginx/html:ro
- ./nginx/config:/etc/nginx/config:ro
ports:
- "8080:80"
- "8443:443"
env_file: livereload.params
user: "8080:${GID:-8080}"
livereload:
image: docker.asifbacchus.dev/livereload/livereload:latest
container_name: livereload
volumes:
- ./certs:certs:ro
- ${WATCHDIR}:/watch:ro
ports:
- "35729:35729"
env_file: livereload.params
user: "9999:${GID:-9999}"
command: listen
#EOF

104
livereload.params Normal file
View File

@ -0,0 +1,104 @@
#
# Parameters for node-livereload-tls stack:
# This file makes it easier to customize your node-livereload-tls stack deployment by providing centralized configuration options.
# This file is *not required* since all values have (sane) default settings.
# There is *no* sensitive information in this file.
#
# COMMON PARAMETERS
# TZ:
# Timezone used in logs and console messages. No effect on operation, purely aesthetic.
# REQUIRED: NO
# DEFAULT: Etc/UTC
# VALID OPTIONS: Any valid IANA TZ formatted timezone. Refer to https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
#TZ=Etc/UTC
# GID:
# You may wish to change the GroupID of the container's user. This allows it access certain resources on the host like certificates or files.
# REQUIRED: NO
# DEFAULT:
# VALID OPTIONS: Any valid UID/GID
#GID=
# WATCHDIR:
# Directory containing files you want to monitor for changes and trigger a browser reload.
# REQUIRED: NO
# DEFAULT:
# VALID OPTIONS: Any valid directory on the host
#WATCHDIR=
#
# AB-NGINX parameters
# refer to https://git.asifbacchus.dev/ab-docker/ab-nginx/wiki for more details
# SERVER_NAMES:
# Space-delimited list of names to which the server should respond. This needs to match any certificates being used.
# REQUIRED: NO
# DEFAULT: _
# VALID OPTIONS: Any valid hostnames for your environment
#SERVER_NAMES=_
# TLS13_ONLY:
# Use and accept only TLS version 1.3 connections. If false, both TLS versions 1.2 and 1.3 will be accepted.
# REQUIRED: NO
# DEFAULT: TRUE
# VALID OPTIONS: TRUE, FALSE
#TLS13_ONLY=TRUE
#
# node-livereload parameters
# LR_PORT:
# Port on which the server should listen. Virtually all clients expect the default setting.
# REQUIRED: NO
# DEFAULT: 35729
# VALID OPTIONS: Any valid TCP port number that does not conflict within your environment
#LR_PORT=35729
# LR_EXTS:
# Comma-delimited list of extensions to watch for changes and trigger a browser reload. This list *must* be quoted.
# REQUIRED: NO
# DEFAULT: "html,xml,css,js,jsx,ts,tsx,php,py"
# VALID OPTIONS: Any valid file extension(s)
#LR_EXTS="html,xml,css,js,jsx,ts,tsx,php,py"
# LR_EXCLUDE:
# Comma-delimited list of files/directories to exclude from monitoring. This list *must* be quoted.
# REQUIRED: NO
# DEFAULT: ".git/,.svn/,.vscode/,.idea/"
# VALID OPTIONS: Any valid files or directories/
#LR_EXCLUDE=".git/,.svn/,.vscode/,.idea/"
# LR_DELAY:
# Amount of time in milliseconds before detecting a change and sending a trigger for a browser reload. Useful if you need to allow time for background recompilation, etc.
# REQUIRED: NO
# DEFAULT: 500
# VALID OPTIONS: Any integer representing a number of milliseconds (ms)
#LR_DELAY=500
# LR_DEBUG:
# Whether or not to print diagnostic debugging messages about the server's operation. Usually a good idea to leave this set to 'true'.
# REQUIRED: NO
# DEFAULT: true
# VALID OPTIONS: true, false
#LR_DEBUG=true
# LR_HTTPS:
# Whether or not to enable SSL/TLS on the server's listening port. This may be required depending on your domain and environment configuration.
# REQUIRED: NO
# DEFAULT: true
# 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