diff --git a/.gitignore b/.gitignore index 68f590c..94b6bcc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,8 @@ *.pem *.srl -# Local +# Local .env configuration .env + +# Here are the local configurations like certificates and more later +etc/ \ No newline at end of file diff --git a/README.md b/README.md index 0fcf2f0..e87f829 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,35 @@ ## Installation +### .env file + +Copy and adjust `containers/.env.example` to `containers/.env` + +``` +cp containers/.env.example containers/.env +``` + +### Generate SSL certificate + Create certificates for HTTPS ```bash -chmod u+x ./scripts/generate-certificates.sh -./scripts/generate-certificates.sh +./scripts/generate-certicates.sh ``` -You need to add the generated certificate `certificates/docker.rootCA.crt` to your browser authorities and trust related websites. +You need to add the generated certificate `etc/certificates/pontsun.rootCA.crt` to your browser authorities and trust related websites. + +If you have [step-cli](https://smallstep.com/docs/cli/) or are on OS X, this should happen automatically. + +### Setup local DNS + +See + +- [Docker installation for Mac](docs/docker-installation-for-mac.md) +- [Docker installation for Ubuntu](docs/docker-installation-for-ubuntu.md) + +for now. + +### Start pontsun with traefik and portainer -Start Traefik and Portainer ```bash cd containers docker-compose up -d diff --git a/build/build.sh b/build/build.sh new file mode 100755 index 0000000..55fa51f --- /dev/null +++ b/build/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +cd $DIR +docker build -t liip/pontsun-helper:latest helper diff --git a/build/helper/Dockerfile b/build/helper/Dockerfile new file mode 100644 index 0000000..aa3837b --- /dev/null +++ b/build/helper/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine:3.10 + +RUN set -ex; \ + # Install Bash and OpenSSL + apk add --no-cache bash openssl \ No newline at end of file diff --git a/config/openssl.cnf b/config/openssl.cnf index 5302f87..543dde8 100644 --- a/config/openssl.cnf +++ b/config/openssl.cnf @@ -11,5 +11,3 @@ keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [ alt_names ] -DNS.1 = ${ENV::PROJECT_NAME}.${ENV::PROJECT_EXTENSION} -DNS.2 = *.${ENV::PROJECT_NAME}.${ENV::PROJECT_EXTENSION} diff --git a/containers/.env.example b/containers/.env.example index 6727809..12adba8 100644 --- a/containers/.env.example +++ b/containers/.env.example @@ -1,5 +1,4 @@ ### Project settings - COMPOSE_PROJECT_NAME=traefik PROJECT_NAME=pontsun PROJECT_EXTENSION=test @@ -12,6 +11,9 @@ PONTSUN_HTTPS_PORT=443 # name of the docker network used for pontsun PONTSUN_NETWORK=pontsun +# Where to store local config files, could also be ~/.pontsun +PONTSUN_DIR_ETC=../etc + ### Images tags PORTAINER_TAG=1.19.2 TRAEFIK_TAG=1.7.2-alpine diff --git a/containers/docker-compose.yml b/containers/docker-compose.yml index c779d32..9ed5ca1 100644 --- a/containers/docker-compose.yml +++ b/containers/docker-compose.yml @@ -22,7 +22,7 @@ services: - '${PONTSUN_HTTP_PORT}:80' - '${PONTSUN_HTTPS_PORT}:443' volumes: - - ../certificates/:/certs/ + - ${PONTSUN_DIR_ETC:-../etc/}/certificates/:/certs/ - /var/run/docker.sock:/var/run/docker.sock labels: - 'traefik.enable=true' diff --git a/etc/.gitkeep b/etc/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/generate-certificates.sh b/scripts/generate-certificates.sh index d864843..d5183a1 100755 --- a/scripts/generate-certificates.sh +++ b/scripts/generate-certificates.sh @@ -1,25 +1,24 @@ -#!/bin/bash +#!/usr/bin/env bash set -e -# Load env file -set -a -test -f $(dirname $0)/../containers/.env && source $(dirname $0)/../containers/.env -set +a +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd $(dirname $0)/../certificates +. $DIR/helper/env.sh -if [ -f $PROJECT_NAME.crt ]; then - echo "Certificate already exists." -else - subj="/C=CH/ST=FR/L=Fribourg/O=Liip/OU=Pontsun/CN=$PROJECT_NAME.$PROJECT_EXTENSION" +mkdir -p $PONTSUN_DIR_ETC/certificates/ - openssl genrsa -out $PROJECT_NAME.rootCA.key 4096 - openssl req -x509 -new -nodes -key $PROJECT_NAME.rootCA.key -sha256 -days 1024 -out $PROJECT_NAME.rootCA.crt -subj "$subj" - - openssl genrsa -out $PROJECT_NAME.key 4096 - openssl req -new -sha256 -subj "$subj" -key $PROJECT_NAME.key -out $PROJECT_NAME.csr -config ../config/openssl.cnf - openssl x509 -req -in $PROJECT_NAME.csr -CA $PROJECT_NAME.rootCA.crt -CAkey $PROJECT_NAME.rootCA.key -CAcreateserial -out $PROJECT_NAME.crt -days 365 -extensions v3_req -extfile ../config/openssl.cnf - - cat $PROJECT_NAME.crt $PROJECT_NAME.key > $PROJECT_NAME.pem - chmod 600 $PROJECT_NAME.key $PROJECT_NAME.pem -fi +docker run --rm -v $PONTSUN_DIR/:/generate/ -v $PONTSUN_DIR_ETC/certificates/:/certs/ -it liip/pontsun-helper:latest /generate/scripts/helper/docker-generate-certificates.sh $1 +CERT_PATH=$PONTSUN_DIR_ETC/certificates/$PROJECT_NAME.rootCA.crt +# disable set -e, since which will return an exit code, when step is not installed +set +e +# check if step is installed and use that. https://smallstep.com/docs/cli/ +STEP=$(which step) +set -e +if [[ ! -z $STEP ]]; then + if ! $STEP certificate verify $CERT_PATH 2> /dev/null; then + echo "Installing $CERT_PATH into your system truststore" + $STEP certificate install -all $CERT_PATH + fi +elif [[ "$OSTYPE" == "darwin"* ]]; then # use direct OS X methods, if step isn't installed + $PONTSUN_DIR/scripts/helper/install-cert-macos.sh $CERT_PATH +fi \ No newline at end of file diff --git a/scripts/helper/docker-generate-certificates.sh b/scripts/helper/docker-generate-certificates.sh new file mode 100755 index 0000000..becdeda --- /dev/null +++ b/scripts/helper/docker-generate-certificates.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +set -e + +# Load env file +set -a +test -f $(dirname $0)/../../containers/.env && source $(dirname $0)/../../containers/.env +set +a + +# check if there are certs in the local directory +cd /certs/ +if [ -f $PROJECT_NAME.crt ] && [ -z $1 ]; then + echo "Certificate already exists in local certificates directory." +else + if [ -f $PROJECT_NAME.crt ]; then + # get existing alt names from the current cert, so that we can reuse them + EXISTING_ALT_NAMES=$(certtool -i < /certs/$PROJECT_NAME.crt | grep DNSname | cut -f 2 -d ":" | sed -e 's/^[[:space:]]*//') + ALT_NAMES=${EXISTING_ALT_NAMES} + else + ALT_NAMES=$PROJECT_NAME.$PROJECT_EXTENSION + ALT_NAMES=${ALT_NAMES}$'\n'*.$PROJECT_NAME.$PROJECT_EXTENSION + fi + # add additional altnames + if [[ ! -z $1 ]]; then + ALT_NAMES=${ALT_NAMES}$'\n'$1 + ALT_NAMES=${ALT_NAMES}$'\n'*.$1 + fi + + # sort them and make them uniq to avoid duplicates + ALT_NAMES=$(echo $"${ALT_NAMES}" | sort | uniq) + EXISTING_ALT_NAMES=$(echo $"${EXISTING_ALT_NAMES}" | sort | uniq) + + if [[ $ALT_NAMES == $EXISTING_ALT_NAMES ]]; then + echo "Certificate wouldn't change, don't generate a new one." + exit 0; + fi + + I=1 + #write the correct config lines + while read -r line; do + if [[ ! -z $line ]]; then + ALT_NAMES_CONFIG=$ALT_NAMES_CONFIG$'\n'"DNS.$I = $line" + ((I++)) + fi + done <<< "$ALT_NAMES" + + subj="/C=CH/ST=FR/L=Fribourg/O=Liip/CN=$PROJECT_NAME.$PROJECT_EXTENSION" + # don't regenerate rootCA, if it already exists + if [ ! -f $PROJECT_NAME.rootCA.key ]; then + openssl genrsa -out $PROJECT_NAME.rootCA.key 4096 + openssl req -x509 -new -nodes -key $PROJECT_NAME.rootCA.key -sha256 -days 1024 -out $PROJECT_NAME.rootCA.crt -subj "$subj" + fi + openssl genrsa -out $PROJECT_NAME.key 4096 + openssl req -new -sha256 -subj "$subj" -key $PROJECT_NAME.key -out $PROJECT_NAME.csr -config <(cat /generate/config/openssl.cnf <(printf "$ALT_NAMES_CONFIG")) + openssl x509 -req -in $PROJECT_NAME.csr -CA $PROJECT_NAME.rootCA.crt -CAkey $PROJECT_NAME.rootCA.key -CAcreateserial -out $PROJECT_NAME.crt -days 365 -extensions v3_req -extfile <(cat /generate/config/openssl.cnf <(printf "$ALT_NAMES_CONFIG")) + + cat $PROJECT_NAME.crt $PROJECT_NAME.key > $PROJECT_NAME.pem + chmod 600 $PROJECT_NAME.key $PROJECT_NAME.pem +fi + diff --git a/scripts/helper/env.sh b/scripts/helper/env.sh new file mode 100644 index 0000000..76ffe8f --- /dev/null +++ b/scripts/helper/env.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +PONTSUN_DIR_REL=${PONTSUN_DIR:-$DIR/../../} +PONTSUN_DIR="$( cd ${PONTSUN_DIR_REL} && pwd )" +PONTSUN_DIR_ETC=${PONTSUN_DIR_ETC:-$PONTSUN_DIR/etc/} + +# Load env file +set -a +# load env variables but only if not set already + +if [[ ! -f $PONTSUN_DIR/containers/.env ]]; then + RED='\033[0;31m' + NC='\033[0m' # No Color + + printf "${RED}${PONTSUN_DIR}/containers/.env does not exist!${NC}\nPlease copy it with:\n" + printf "cp ${PONTSUN_DIR}/containers/.env.example ${PONTSUN_DIR}/containers/.env\n" + printf "and adjust it.\n" + exit 1 +fi +test -f $PONTSUN_DIR/containers/.env && source <(grep -v '^\s*#' $PONTSUN_DIR/containers/.env | sed -E 's|^ *([^=]+)=(.*)$|: ${\1=\2}; export \1|g') + diff --git a/scripts/helper/install-cert-macos.sh b/scripts/helper/install-cert-macos.sh new file mode 100755 index 0000000..bfcb6ce --- /dev/null +++ b/scripts/helper/install-cert-macos.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -e + +# base from https://gist.github.com/koop/84254d5214495e6fc49db3284c9b7772 +# Usage +# $ ./install-cert-macos.sh "/path/to/cert" + +CERT_PATH=$1 + +# First, grab the SHA-1 from the provided SSL cert. +CERT_SHA1=$(openssl x509 -in "$CERT_PATH" -sha1 -noout -fingerprint | cut -d "=" -f2 | sed "s/://g") + +# Next, grab the SHA-1s of any standard.dev certs in the keychain. +# Don't return an error code if nothing is found. +EXISTING_CERT_SHAS=$(security find-certificate -a -c "$PROJECT_NAME.$PROJECT_EXTENSION" -Z /Library/Keychains/System.keychain | grep "SHA-1") || true +echo "$EXISTING_CERT_SHAS" | grep -q "$CERT_SHA1" || { + echo "Installing $CERT_PATH into your Keychain" + sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$CERT_PATH" +} \ No newline at end of file