Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better certificate generation #1

Open
wants to merge 1 commit into
base: legacy
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
*.pem
*.srl

# Local
# Local .env configuration
.env

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No whitespace; add comment please

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a comment, left the whitespace

# Here are the local configurations like certificates and more later
etc/
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
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
Expand Down
7 changes: 7 additions & 0 deletions build/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's really nothing less ugly?

DIR=$(realpath $(dirname $(dirname $0)))

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's even worse (or not really possible at all) in Posix sh

https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh#29834779


cd $DIR
docker build -t liip/pontsun-helper:latest helper
5 changes: 5 additions & 0 deletions build/helper/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM alpine:3.10

RUN set -ex; \
# Install Bash and OpenSSL
apk add --no-cache bash openssl
2 changes: 0 additions & 2 deletions config/openssl.cnf
Original file line number Diff line number Diff line change
Expand Up @@ -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}
4 changes: 3 additions & 1 deletion containers/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
### Project settings

COMPOSE_PROJECT_NAME=traefik
PROJECT_NAME=pontsun
PROJECT_EXTENSION=test
Expand All @@ -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
2 changes: 1 addition & 1 deletion containers/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Empty file added etc/.gitkeep
Empty file.
39 changes: 19 additions & 20 deletions scripts/generate-certificates.sh
Original file line number Diff line number Diff line change
@@ -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
59 changes: 59 additions & 0 deletions scripts/helper/docker-generate-certificates.sh
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not specified in the README.md that we need to copy the .env file and without it it's not working by default. We should maybe warn the user if we don't find some env variables and stop the script with an explicit message.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does that now. Plus I added some section to the README

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

24 changes: 24 additions & 0 deletions scripts/helper/env.sh
Original file line number Diff line number Diff line change
@@ -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')

19 changes: 19 additions & 0 deletions scripts/helper/install-cert-macos.sh
Original file line number Diff line number Diff line change
@@ -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"
}