Merge pull request from resin-io/vpn-certs

generate vpn certs + multiple bootstrap fixes
This commit is contained in:
Will Boyce 2018-10-08 12:54:06 +01:00 committed by GitHub
commit adb1f1eea7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 326 additions and 264 deletions

4
Makefile Normal file

@ -0,0 +1,4 @@
.PHONY: lint
lint:
shellcheck scripts/*

5
Vagrantfile vendored

@ -24,10 +24,11 @@ Vagrant.configure('2') do |config|
config.vm.provision :shell, inline: 'apt-get update && apt-get install -y nodejs && rm -rf /var/lib/apt/lists/*'
# FIXME: remove `docker login`
config.vm.provision :shell, inline: "docker login --username resindev --password #{ENV.fetch('DOCKERHUB_PASSWORD')}"
config.vm.provision :shell, privileged: false, inline: "docker login --username resindev --password #{ENV.fetch('DOCKERHUB_PASSWORD')}"
config.vm.provision :shell, privileged: false,
inline: "cd /home/vagrant/open-balena && ./scripts/start-project #{ENV.fetch('OPENBALENA_PROJECT_NAME', '')} #{ENV.fetch('OPENBALENA_HOST_NAME', '')}"
# FIXME: -n/-d should only be passed if the relevant ENV var is set
inline: "cd /home/vagrant/open-balena && ./scripts/start-project -p -n #{ENV.fetch('OPENBALENA_PROJECT_NAME', 'demo')} -d #{ENV.fetch('OPENBALENA_DOMAIN', 'openbalena.local')}"
config.vm.provision :shell, privileged: false,
inline: 'cd /home/vagrant/open-balena && ./scripts/run-fig-command up -d || true',
run: 'always'

@ -134,6 +134,10 @@ services:
SENTRY_DSN:
VPN_HAPROXY_USEPROXYPROTOCOL: 'true'
VPN_SERVICE_API_KEY: ${OPENBALENA_VPN_SERVICE_API_KEY}
VPN_OPENVPN_CA_CRT: ${OPENBALENA_VPN_CA}
VPN_OPENVPN_SERVER_CRT: ${OPENBALENA_VPN_SERVER_CRT}
VPN_OPENVPN_SERVER_KEY: ${OPENBALENA_VPN_SERVER_KEY}
VPN_OPENVPN_SERVER_DH: ${OPENBALENA_VPN_SERVER_DH}
# FIXME: remove all of the following
API_SERVICE_API_KEY: __unused__
PROXY_SERVICE_API_KEY: __unused__5
@ -176,6 +180,8 @@ services:
- img.${OPENBALENA_HOST_NAME}
environment:
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
BALENA_HAPROXY_CRT: ${OPENBALENA_ROOT_CRT}
BALENA_HAPROXY_KEY: ${OPENBALENA_ROOT_KEY}
HAPROXY_HOSTNAME: ${OPENBALENA_HOST_NAME}
# FIXME: remove the following

@ -1,9 +1,10 @@
#!/bin/sh
#!/bin/bash -eu
CA_B64="$BALENA_ROOT_CA"
CA_FILE=/etc/ssl/private/root.chain.pem
mkdir -p $(dirname "$CA_FILE")
echo "$CA_B64" | base64 -d >"$CA_FILE"
exec haproxy -f /usr/local/etc/haproxy/haproxy.cfg
HAPROXY_CHAIN=/etc/ssl/private/open-balena.pem
mkdir -p "$(dirname "${HAPROXY_CHAIN}")"
(
echo "${BALENA_HAPROXY_CRT}" | base64 -d
echo "${BALENA_HAPROXY_KEY}" | base64 -d
echo "${BALENA_ROOT_CA}" | base64 -d
) > "${HAPROXY_CHAIN}"
exec haproxy -f /usr/local/etc/haproxy/haproxy.cfg

@ -47,7 +47,7 @@ backend redirect-to-https-in
frontend https-in
mode http
option forwardfor
bind 127.0.0.1:444 ssl crt /etc/ssl/private/root.chain.pem accept-proxy
bind 127.0.0.1:444 ssl crt /etc/ssl/private/open-balena.pem accept-proxy
reqadd X-Forwarded-Proto:\ https
acl host_api hdr_dom(host) -i "api.${HAPROXY_HOSTNAME}"

@ -1,79 +0,0 @@
'use strict';
var crypto = require('crypto');
var fs = require('fs');
var base32 = (function() {
// Extracted from https://github.com/chrisumbel/thirty-two
// to avoid having to install packages for this script.
var charTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
var byteTable = [
0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff
];
function quintetCount(buff) {
var quintets = Math.floor(buff.length / 5);
return buff.length % 5 == 0 ? quintets: quintets + 1;
}
return function(plain) {
if (!Buffer.isBuffer(plain)) {
plain = new Buffer(plain);
}
var i = 0;
var j = 0;
var shiftIndex = 0;
var digit = 0;
var encoded = new Buffer(quintetCount(plain) * 8);
/* byte by byte isn't as pretty as quintet by quintet but tests a bit
faster. will have to revisit. */
while(i < plain.length) {
var current = plain[i];
if(shiftIndex > 3) {
digit = current & (0xff >> shiftIndex);
shiftIndex = (shiftIndex + 5) % 8;
digit = (digit << shiftIndex) | ((i + 1 < plain.length) ?
plain[i + 1] : 0) >> (8 - shiftIndex);
i++;
} else {
digit = (current >> (8 - (shiftIndex + 5))) & 0x1f;
shiftIndex = (shiftIndex + 5) % 8;
if(shiftIndex == 0) i++;
}
encoded[j] = charTable.charCodeAt(digit);
j++;
}
for (i = j; i < encoded.length; i++) {
encoded[i] = 0x3d; //'='.charCodeAt(0)
}
return encoded;
}
})();
function joseKeyId(der) {
var hasher = crypto.createHash('sha256');
hasher.update(der);
var b32 = base32(hasher.digest().slice(0, 30)).toString('ascii');
var chunks = [];
for (var i = 0; i < b32.length; i += 4) {
chunks.push(b32.substr(i, 4));
}
return chunks.join(':');
}
var derFilePath = process.argv[2];
var der = fs.readFileSync(derFilePath);
process.stdout.write(joseKeyId(der));

33
scripts/gen-root-ca Executable file

@ -0,0 +1,33 @@
#!/bin/bash -eu
usage() {
echo "usage: $0 COMMON_NAME [OUT]"
echo
echo " COMMON_NAME the domain name the certificate is valid for, eg. example.com"
echo " OUT path to output directory generated files will be placed in"
echo
}
if [ -z "$1" ]; then
usage
exit 1
fi
CMD="$(realpath "$0")"
DIR="$(dirname "${CMD}")"
CN="$1"
OUT="$(realpath "${2:-.}")"
# shellcheck source=scripts/ssl-common.sh
source "${DIR}/ssl-common.sh"
# Create a secret key and CA file for the self-signed CA
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" init-pki 2>/dev/null
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" --days="${CA_EXPIRY_DAYS}" --req-cn="ca.${CN}" build-ca nopass 2>/dev/null
ROOT_CA="${ROOT_PKI}/ca.crt"
echo "ROOT_CA=${ROOT_CA//$OUT/\$OUT}"
# update indexes and generate CRLs
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" update-db 2>/dev/null
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" gen-crl 2>/dev/null

@ -1,55 +0,0 @@
#!/bin/sh
set -e
CN=$1
OUT=${2:-.}
CA="${CN}-ca"
CA_FILE="${OUT}/ca"
CN_FILE="${OUT}/root"
CN_EXPIRY_DAYS=730
CA_EXPIRY_DAYS=3650
usage() {
echo "usage: $0 HOST_NAME [OUT]"
echo
echo " HOST_NAME the domain name the certificate is valid for, eg. example.com"
echo " OUT path to output directory generated files will be placed in"
echo
}
if [ -z "$CN" ]; then
usage
exit 1
fi
cat > "${CN_FILE}.v3.ext" <<STR
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.${CN}
STR
# Create a secret key and CA file for the self-signed CA
openssl genrsa -out "${CA_FILE}.key" 2048 2>/dev/null
openssl req -x509 -new -nodes -sha256 -days $CA_EXPIRY_DAYS -key "${CA_FILE}.key" -subj "/CN=${CA}" -out "${CA_FILE}.pem" 2>/dev/null
# Create a secret key and Certificate Signing Request (CSR) for the domain
openssl genrsa -out "${CN_FILE}.key" 2048 2>/dev/null
openssl req -new -key "${CN_FILE}.key" -subj "/CN=${CN}" -out "${CN_FILE}.csr" 2>/dev/null
# Sign the request with the self-signed CA and extension file
openssl x509 -req -sha256 -days $CN_EXPIRY_DAYS -in "${CN_FILE}.csr" -CA "${CA_FILE}.pem" -CAkey "${CA_FILE}.key" -CAcreateserial -out "${CN_FILE}.pem" -extfile "${CN_FILE}.v3.ext" 2>/dev/null
# Create the custom certificate chain file
cat "${CN_FILE}.pem" "${CA_FILE}.pem" "${CN_FILE}.key" >"${CN_FILE}.chain.pem"
# Cleanup
rm "${CA_FILE}.key" "${CA_FILE}.pem" "${CN_FILE}.key" "${CN_FILE}.pem" "${CN_FILE}.csr" "${CN_FILE}.v3.ext"
echo "ROOT_CA=${CN_FILE}.chain.pem"

34
scripts/gen-root-cert Executable file

@ -0,0 +1,34 @@
#!/bin/bash -eu
usage() {
echo "usage: $0 COMMON_NAME [OUT]"
echo
echo " COMMON_NAME the domain name the certificate is valid for, eg. example.com"
echo " OUT path to output directory generated files will be placed in"
echo
}
if [ -z "$1" ]; then
usage
exit 1
fi
CMD="$(realpath "$0")"
DIR="$(dirname "${CMD}")"
CN="$1"
OUT="$(realpath "${2:-.}")"
# shellcheck source=scripts/ssl-common.sh
source "${DIR}/ssl-common.sh"
# generate default CSR and sign (root + wildcard)
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" --days="${CRT_EXPIRY_DAYS}" --subject-alt-name="DNS:${CN}" build-server-full "*.${CN}" nopass 2>/dev/null
ROOT_CRT="${ROOT_PKI}"'/issued/*.'"${CN}"'.crt'
ROOT_KEY="${ROOT_PKI}"'/private/*.'"${CN}"'.key'
echo "ROOT_CRT=${ROOT_CRT//$OUT/\$OUT}"
echo "ROOT_KEY=${ROOT_KEY//$OUT/\$OUT}"
# update indexes and generate CRLs
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" update-db 2>/dev/null
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" gen-crl 2>/dev/null

@ -1,42 +1,44 @@
#!/bin/sh
set -e
CMD=$0
DIR=$(dirname "$CMD")
CN=$1
OUT=${2:-.}
CERT_FILE="${OUT}/token-auth"
EXPIRY_DAYS=730
#!/bin/bash -eu
usage() {
echo "usage: $0 HOST_NAME [OUT]"
echo "usage: $0 COMMON_NAME [OUT]"
echo
echo " HOST_NAME the domain name the certificate is valid for, eg. example.com"
echo " OUT path to output directory generated files will be placed in"
echo " COMMON_NAME the domain name the certificate is valid for, eg. example.com"
echo " OUT path to output directory generated files will be placed in"
echo
}
keyid() {
# FIXME: do this in bash or python, not node
nodejs "${DIR}/_keyid.js" "$1"
}
if [ -z "$CN" ]; then
if [ -z "$1" ]; then
usage
exit 1
fi
openssl ecparam -name prime256v1 -genkey -noout -out "${CERT_FILE}.pem" 2>/dev/null
openssl req -x509 -new -nodes -days "${EXPIRY_DAYS}" -key "${CERT_FILE}.pem" -subj "/CN=api.${CN}" -out "${CERT_FILE}.crt" 2>/dev/null
openssl ec -in "${CERT_FILE}.pem" -pubout -outform DER -out "${CERT_FILE}".der 2>/dev/null
keyid "${CERT_FILE}".der >"${CERT_FILE}".kid
CMD="$(realpath "$0")"
DIR="$(dirname "${CMD}")"
# Cleanup
rm "${CERT_FILE}.der"
CN="$1"
OUT="$(realpath "${2:-.}")"
echo "PUB=${CERT_FILE}.crt"
echo "KEY=${CERT_FILE}.pem"
echo "KID=${CERT_FILE}.kid"
# shellcheck source=scripts/ssl-common.sh
source "${DIR}/ssl-common.sh"
keyid() {
local der
der="$(openssl ec -in "$1" -pubout -outform DER 2>/dev/null)"
python -c "import sys as S; from base64 import b32encode as B; import hashlib as H; h = H.sha256(); h.update(S.argv[1].encode('ascii')); s = B(h.digest()[:30]).decode('ascii'); S.stdout.write(':'.join([s[i:i+4] for i in range(0, len(s), 4)]))" "${der}"
}
# generate api CSR and sign
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" --days=730 --use-algo=ec --curve=prime256v1 build-server-full "api.${CN}" nopass 2>/dev/null
JWT_CRT="${ROOT_PKI}/issued/api.${CN}.crt"
JWT_KEY="${ROOT_PKI}/private/api.${CN}.key"
echo "JWT_CRT=${JWT_CRT//$OUT/\$OUT}"
echo "JWT_KEY=${JWT_KEY//$OUT/\$OUT}"
# update indexes and generate CRLs
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" update-db 2>/dev/null
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" gen-crl 2>/dev/null
# generate key ID
JWT_KID="$(keyid "${JWT_CRT}")"
echo "JWT_KID=${JWT_KID//$OUT/\$OUT}"

53
scripts/gen-vpn-certs Executable file

@ -0,0 +1,53 @@
#!/bin/bash -eu
usage() {
echo "usage: $0 COMMON_NAME [OUT]"
echo
echo " COMMON_NAME the domain name the certificate is valid for, eg. example.com"
echo " OUT path to output directory generated files will be placed in"
echo
}
if [ -z "$1" ]; then
usage
exit 1
fi
CMD="$(realpath "$0")"
DIR="$(dirname "${CMD}")"
CN="$1"
OUT="$(realpath "${2:-.}")"
# shellcheck source=scripts/ssl-common.sh
source "${DIR}/ssl-common.sh"
VPN_PKI="$(realpath "${OUT}/vpn")"
# generate VPN sub-CA
"$easyrsa_bin" --pki-dir="${VPN_PKI}" init-pki 2>/dev/null
"$easyrsa_bin" --pki-dir="${VPN_PKI}" --days="${CA_EXPIRY_DAYS}" --req-cn="vpn-ca.${CN}" build-ca nopass subca 2>/dev/null
# import sub-CA CSR into root PKI, sign, and copy back to vpn PKI
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" import-req "${VPN_PKI}/reqs/ca.req" "vpn-ca" 2>/dev/null
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" sign-req ca "vpn-ca" 2>/dev/null
cp "${ROOT_PKI}/issued/vpn-ca.crt" "${VPN_PKI}/ca.crt"
VPN_CA="${VPN_PKI}/ca.crt"
echo "VPN_CA=${VPN_CA//$OUT/\$OUT}"
# generate and sign vpn server certificate
"$easyrsa_bin" --pki-dir="${VPN_PKI}" --days="${CRT_EXPIRY_DAYS}" build-server-full "vpn.${CN}" nopass 2>/dev/null
VPN_CRT="${VPN_PKI}/issued/vpn.${CN}.crt"
VPN_KEY="${VPN_PKI}/private/vpn.${CN}.key"
echo "VPN_CRT=${VPN_CRT//$OUT/\$OUT}"
echo "VPN_KEY=${VPN_KEY//$OUT/\$OUT}"
# generate vpn dhparams (keysize of 2048 will do, 4096 can wind up taking hours to generate)
"$easyrsa_bin" --pki-dir="${VPN_PKI}" --keysize=2048 gen-dh 2>/dev/null
VPN_DH="${VPN_PKI}/dh.pem"
echo "VPN_DH=${VPN_DH//$OUT/\$OUT}"
# update indexes and generate CRLs
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" update-db 2>/dev/null
"$easyrsa_bin" --pki-dir="${VPN_PKI}" update-db 2>/dev/null
"$easyrsa_bin" --pki-dir="${ROOT_PKI}" gen-crl 2>/dev/null
"$easyrsa_bin" --pki-dir="${VPN_PKI}" gen-crl 2>/dev/null

@ -1,40 +1,58 @@
#!/bin/sh
set -e
HOST_NAME="$1"
ROOT_CA="$2"
TOKEN_AUTH_PUB="$3"
TOKEN_AUTH_KEY="$4"
TOKEN_AUTH_KID="$5"
#!/bin/bash -eu
usage() {
echo "usage: $0 HOST_NAME ROOT_CA TOKEN_AUTH_PUB TOKEN_AUTH_KEY TOKEN_AUTH_KID"
echo "usage: $0"
echo
echo "Required Variables:"
echo
echo " DOMAIN"
echo " ROOT_CA Path to root CA certificate"
echo " ROOT_CRT Path to root/wildcard certificate"
echo " ROOT_KEY Path to root/wildcard private key"
echo " JWT_CRT Path to Token Auth certificate"
echo " JWT_KEY Path to Token Auth private key"
echo " JWT_KID The KeyID for the Token Auth certificate"
echo " VPN_CA Path to the VPN sub-CA certificate"
echo " VPN_CRT Path to the VPN server certificate"
echo " VPN_KEY Path to the VPN server private key"
echo " VPN_DH Path to the VPN server Diffie Hellman parameters"
echo
}
for var in DOMAIN ROOT_CA ROOT_CRT ROOT_KEY JWT_CRT JWT_KEY JWT_KID VPN_CA VPN_CRT VPN_KEY VPN_DH; do
if [ -z "${!var-}" ]; then
usage
exit 1
fi
done
randstr() {
LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom | fold -w ${1:-32} | head -n 1
LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom | fold -w "${1:-32}" | head -n 1
}
b64encode() {
cat "$1" | base64 --wrap=0 2>/dev/null || cat "$1" | base64 --break=0
cat "$@" | base64 --wrap=0 2>/dev/null || cat "$@" | base64 --break=0
}
if [ -z "$HOST_NAME" ] || [ -z "$ROOT_CA" ] || [ -z "$TOKEN_AUTH_PUB" ] || [ -z "$TOKEN_AUTH_KEY" ] || [ -z "$TOKEN_AUTH_KID" ]; then
usage
exit 1
fi
b64encode_str() {
echo -n "$@" | base64 --wrap=0 - 2>/dev/null || echo -n "$@" | base64 --break=0 -
}
cat <<STR
export OPENBALENA_DEBUG=true
export OPENBALENA_PRODUCTION_MODE=false
export OPENBALENA_HOST_NAME=$HOST_NAME
export OPENBALENA_HOST_NAME=$DOMAIN
export OPENBALENA_JWT_SECRET=$(randstr 32)
export OPENBALENA_RESINOS_REGISTRY_CODE=$(randstr 32)
export OPENBALENA_ROOT_CA=$(b64encode "$ROOT_CA")
export OPENBALENA_TOKEN_AUTH_PUB=$(b64encode "$TOKEN_AUTH_PUB")
export OPENBALENA_TOKEN_AUTH_KEY=$(b64encode "$TOKEN_AUTH_KEY")
export OPENBALENA_TOKEN_AUTH_KID=$(b64encode "$TOKEN_AUTH_KID")
export OPENBALENA_ROOT_CRT=$(b64encode "${ROOT_CRT}")
export OPENBALENA_ROOT_KEY=$(b64encode "${ROOT_KEY}")
export OPENBALENA_TOKEN_AUTH_PUB=$(b64encode "$JWT_CRT")
export OPENBALENA_TOKEN_AUTH_KEY=$(b64encode "$JWT_KEY")
export OPENBALENA_TOKEN_AUTH_KID=$(b64encode_str "$JWT_KID")
export OPENBALENA_VPN_CA=$(b64encode "$VPN_CA")
export OPENBALENA_VPN_SERVER_CRT=$(b64encode "$VPN_CRT")
export OPENBALENA_VPN_SERVER_KEY=$(b64encode "$VPN_KEY")
export OPENBALENA_VPN_SERVER_DH=$(b64encode "$VPN_DH")
export OPENBALENA_VPN_SERVICE_API_KEY=$(randstr 32)
STR

@ -1,36 +1,32 @@
#!/bin/sh
set -e
CMD=$0
DIR=$(dirname "$CMD")
HOST_NAME=$1
#!/bin/bash -eu
SERVICES="api registry vpn db"
SERVICES="${SERVICES} img devices" # FIXME: remove
usage() {
echo "usage: $0 HOST_NAME"
echo "usage: $0 DOMAIN"
echo
echo " HOST_NAME the domain name to add host entries for, eg. example.com"
echo " DOMAIN the domain name to add host entries for, eg. example.com"
echo
}
if [ -z "$HOST_NAME" ]; then
if [ -z "$1" ]; then
usage
exit 1
fi
DOMAIN="$1"
# We need sudo to write to /etc/hosts, so first write to a temp file and then
# append all entries to hosts file.
tmp=$(mktemp --tmpdir openbalena.XXXX)
tmp="$(mktemp --tmpdir openbalena.XXXX)"
for service in $SERVICES; do
name="${service}.${HOST_NAME}"
if ! grep "\s$name" /etc/hosts >/dev/null 2>&1 ; then
name="${service}.${DOMAIN}"
if ! grep "\\s$name" /etc/hosts >/dev/null 2>&1 ; then
echo "adding $name"
echo "127.0.0.1 $name" >>$tmp
echo "127.0.0.1 $name" >>"${tmp}"
fi
done
cat $tmp | sudo tee -a /etc/hosts >/dev/null
rm -f $tmp
# shellcheck disable=SC2024
sudo tee -a /etc/hosts >/dev/null <"${tmp}"
rm -f "${tmp}"

@ -1,13 +1,11 @@
#!/bin/sh
#!/bin/bash -eu
set -e
CMD=$0
DIR=$(dirname "$CMD")
BASE_DIR=$(dirname "$DIR")
CMD="$0"
DIR="$(dirname "$CMD")"
BASE_DIR="$(dirname "$DIR")"
echo_bold() {
printf "\033[1m${@}\033[0m\n"
printf "\\033[1m%s\\033[0m\\n" "$@"
}
PROJECT_FILE="${BASE_DIR}/.project"
@ -16,16 +14,17 @@ if [ ! -f "$PROJECT_FILE" ]; then
echo_bold 'See README.md for help.'
exit 1
fi
PROJECT=$(cat "$PROJECT_FILE")
PROJECT="$(cat "$PROJECT_FILE")"
if [ ! -f "${PROJECT}/activate" ]; then
echo_bold 'No project activated. Please create or select an existing one first.'
echo_bold 'See README.md for help.'
exit 1
fi
PROJECT_NAME=$(basename "$PROJECT")
PROJECT_NAME="$(basename "$PROJECT")"
. "${PROJECT}/activate"; docker-compose \
--project-name $PROJECT_NAME \
# shellcheck source=/dev/null
source "${PROJECT}/activate"; docker-compose \
--project-name "${PROJECT_NAME}" \
-f "${BASE_DIR}/compose/services.yml" \
-f "${PROJECT}/docker-compose.yml" \
"$@"
"$@"

@ -1,11 +1,8 @@
#!/bin/sh
#!/bin/bash -eu
set -e
CMD=$0
DIR=$(dirname "$CMD")
BASE_DIR=$(dirname "$DIR")
PROJECT_PATH="$1"
CMD="$0"
DIR="$(dirname "$CMD")"
BASE_DIR="$(dirname "$DIR")"
usage() {
echo "usage: $0 PROJECT_PATH"
@ -14,12 +11,13 @@ usage() {
echo
}
if [ -z "$PROJECT_PATH" ]; then
if [ -z "$1" ]; then
usage
exit 1
fi
PROJECT_DIR=$(realpath "$PROJECT_PATH")
PROJECT_PATH="$1"
PROJECT_DIR="$(realpath "$PROJECT_PATH")"
if [ ! -d "$PROJECT_DIR" ]; then
echo 'Project path refers to a directory that does not exist.'
@ -27,8 +25,8 @@ if [ ! -d "$PROJECT_DIR" ]; then
fi
if [ ! -f "${PROJECT_DIR}/activate" ]; then
echo 'Project path refers to a directory that is not a valid porject.'
echo 'Project path refers to a directory that is not a valid project.'
exit 1
fi
echo -n $PROJECT_DIR >"${BASE_DIR}/.project"
echo -n "${PROJECT_DIR}" >"${BASE_DIR}/.project"

24
scripts/ssl-common.sh Normal file

@ -0,0 +1,24 @@
#!/bin/bash -eu
# shellcheck disable=SC2034
# ensure we have `easyrsa` available
if [ -z "${easyrsa_bin-}" ] || [ ! -x "${easyrsa_bin}" ]; then
easyrsa_bin="$(command easyrsa 2>/dev/null || true)"
if [ -z "${easyrsa_bin}" ]; then
easyrsa_dir="$(mktemp -dt easyrsa.XXXXXXXX)"
easyrsa_url="https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.5/EasyRSA-nix-3.0.5.tgz"
(cd "${easyrsa_dir}"; curl -sL "${easyrsa_url}" | tar xz --strip-components=1)
easyrsa_bin="${easyrsa_dir}/easyrsa"
# shellcheck disable=SC2064
trap "rm -rf \"${easyrsa_dir}\"" EXIT
fi
export EASYRSA_BATCH=1
export EASYRSA_KEY_SIZE=4096
fi
# setup ROOT_PKI path
ROOT_PKI="$(realpath "${OUT}/root")"
# global expiry settings
CA_EXPIRY_DAYS=3650
CRT_EXPIRY_DAYS=730

@ -1,27 +1,48 @@
#!/bin/sh
set -e
#!/bin/bash -eu
CMD=$0
DIR=$(dirname "$CMD")
BASE_DIR=$(dirname "$DIR")
PROJECT_NAME=${1:-demo}
HOST_NAME=${2:-openbalena.local}
PROJECT_NAME=demo
DOMAIN=openbalena.local
usage() {
echo "usage: $0 [-h] [-p] [-n PROJECT_NAME] [-d DOMAIN]"
echo
echo " -p patch hosts - patch the host /etc/hosts file"
echo " PROJECT_NAME a name for the deployment, eg. staging. Default is 'demo'"
echo " DOMAIN the domain name this deployment will run as, eg. example.com. Default is 'openbalena.local'"
echo
}
show_help=false
patch_hosts=false
while getopts ":hpn:d:" opt; do
case "${opt}" in
h) show_help=true;;
p) patch_hosts=true;;
n) PROJECT_NAME="${OPTARG}";;
d) DOMAIN="${OPTARG}";;
*)
echo "Invalid argument: -${OPTARG}"
usage
exit 1
;;
esac
done
shift $((OPTIND-1))
PROJECT_DIR="$(pwd)/${PROJECT_NAME}"
CERTS_DIR="${PROJECT_DIR}/certs"
usage() {
echo "usage: $0 [PROJECT_NAME [HOST_NAME]]"
echo
echo " PROJECT_NAME a name for the deployment, eg. staging. Default is 'demo'"
echo " HOST_NAME the domain name this deployment will run as, eg. example.com. Default is 'openbalena.local'"
echo
}
if [ "$show_help" = "true" ]; then
usage
exit 1
fi
echo_bold() {
printf "\033[1m${@}\033[0m\n"
printf "\\033[1m%s\\033[0m\\n" "${@}"
}
if [ -d "$PROJECT_DIR" ]; then
@ -32,28 +53,34 @@ fi
echo_bold "==> Creating new project at: $PROJECT_DIR"
mkdir -p "$PROJECT_DIR" "$CERTS_DIR"
echo_bold "==> Creating root CA cert..."
"${DIR}/gen-root-ca-cert" $HOST_NAME "$CERTS_DIR"
echo_bold "==> Generating root CA cert..."
# shellcheck source=scripts/gen-root-ca
source "${DIR}/gen-root-ca" "${DOMAIN}" "${CERTS_DIR}"
echo_bold "==> Creating token auth cert..."
"${DIR}/gen-token-auth-cert" $HOST_NAME "$CERTS_DIR"
echo_bold "==> Generating root cert chain for haproxy..."
# shellcheck source=scripts/gen-root-cert
source "${DIR}/gen-root-cert" "${DOMAIN}" "${CERTS_DIR}"
echo_bold "==> Generating token auth cert..."
# shellcheck source=scripts/gen-token-auth-cert
source "${DIR}/gen-token-auth-cert" "${DOMAIN}" "${CERTS_DIR}"
echo_bold "==> Generating VPN CA, cert and dhparam (this may take a while)..."
# shellcheck source=scripts/gen-vpn-certs
source "${DIR}/gen-vpn-certs" "${DOMAIN}" "${CERTS_DIR}"
echo_bold "==> Setting up environment..."
cat >"${PROJECT_DIR}/activate" <<STR
$("${DIR}/make-env" \
$HOST_NAME \
"${CERTS_DIR}/root.chain.pem" \
"${CERTS_DIR}/token-auth.crt" \
"${CERTS_DIR}/token-auth.pem" \
"${CERTS_DIR}/token-auth.kid")
STR
# shellcheck source=scripts/make-env
cat >"${PROJECT_DIR}/activate" <(source "${DIR}/make-env")
echo_bold "==> Adding default compose file..."
cp "${BASE_DIR}/compose/template.yml" "${PROJECT_DIR}/docker-compose.yml"
# FIXME: should be explicitly requested via a flag
echo_bold "==> Patching /etc/hosts..."
"${DIR}/patch-hosts" $HOST_NAME
if [ "${patch_hosts}" = "true" ]; then
echo_bold "==> Patching /etc/hosts..."
# shellcheck source=scripts/patch-hosts
source "${DIR}/patch-hosts" "${DOMAIN}"
fi
echo_bold "==> Activating project..."
"${DIR}/select-project" "$PROJECT_DIR"
"${DIR}/select-project" "${PROJECT_DIR}"