I have a new openBalena server running. I used the “-c” option with the quickstart script and this is working fine. But I noticed that it still generated all the certificates under config/certs as it did without the -c option. Also, I see that the acme certs are mounted and used by haproxy, but the vpn container references the OPENBALENA_VPN_* variables which contain cert info from config/certs.
So, my question is, are the acme certificates sufficient for everything themselves, or are they only used for some things, while the VPN (openvpn) still requires the certs under config/certs? Basically, I would like to know if acme will handle all my certificate renewals for everything, or I will have to renew those certs under config/certs as well?
Hi
We also use ACME and the VPN server certifcate on one of our Open Balena servers expired recently. After digging into the code I found out you have to manually renew the VPN certificates after 2 years.
It’s been a while since I worked on it, but I wrote a bash script based on on the script you use to set up openbalena. I believe the new openbalena versions include a cert-manager container that handles this cert stuff for you.
The script I wrote was specific for our setup so you’ll have update some paths and variables. Also note that this is most likely not compatible with the OpenBalena rework from mid 2024. (Haven’t had the time to update our setup yet, I just bump the container tags till I’m forced to upgrade)
#!/bin/bash
#
# Renews the OpenVPN server certificate (renew) or creates a new CA if it is
# missing (create-ca). The relevant files are then base64 encoded and put in
# the the required .env files for the docker-compose stack.
#------------------------------------------------------------------------------
# Bash settings
#------------------------------------------------------------------------------
# Enable "Bash strict mode"
set -o errexit # abort on nonzero exitstatus
# set -o nounset # abort on unbound variable
set -o pipefail # don't mask errors in piped commands
#------------------------------------------------------------------------------
# Variables
#------------------------------------------------------------------------------
ACTION=$1
CN="<%= @public_url -%>"
CERTS_DIR="/opt/openbalena/config/certs"
VPN_PKI_DIR="${CERTS_DIR}/vpn"
VPN_ENV_FILE="/opt/openbalena/config/vpn.env"
API_ENV_FILE="/opt/openbalena/config/api.env"
# From openbalena source code
CA_EXPIRY_DAYS=3650
CRT_EXPIRY_DAYS=730
#------------------------------------------------------------------------------
# Functions
#------------------------------------------------------------------------------
function update_env() {
local VAR_NAME=$1
local NEW_VALUE=$2
local FILE=$3
# If the variable exists, update its value
if grep -q ${VAR_NAME} ${FILE}; then
sed -i "s/^${VAR_NAME}=.*/${VAR_NAME}=${NEW_VALUE}/" ${FILE}
else
echo "${VAR_NAME} not in ${FILE}"
fi
}
function update_env_files() {
# Base64 encode files
echo " - Base64 encoding files..."
OPENBALENA_VPN_CA=$(b64file "${VPN_PKI_DIR}/ca.crt")
OPENBALENA_VPN_CA_CHAIN=$(b64file "${VPN_PKI_DIR}/ca.crt")
OPENBALENA_VPN_SERVER_CRT=$(b64file "${VPN_PKI_DIR}/issued/vpn.${CN}.crt")
OPENBALENA_VPN_SERVER_KEY=$(b64file "${VPN_PKI_DIR}/private/vpn.${CN}.key")
OPENBALENA_VPN_SERVER_DH=$(b64file "${VPN_PKI_DIR}/dh.pem")
# Update API env file
echo " - Updating API env file..."
update_env "DEVICE_CONFIG_OPENVPN_CA" "${OPENBALENA_VPN_CA}" "${API_ENV_FILE}"
# Update VPN env file
echo " - Updating VPN env file..."
update_env "VPN_OPENVPN_CA_CRT" "${OPENBALENA_VPN_CA}" "${VPN_ENV_FILE}"
update_env "VPN_OPENVPN_SERVER_CRT" "${OPENBALENA_VPN_SERVER_CRT}" "${VPN_ENV_FILE}"
update_env "VPN_OPENVPN_SERVER_KEY" "${OPENBALENA_VPN_SERVER_KEY}" "${VPN_ENV_FILE}"
update_env "VPN_OPENVPN_SERVER_DH" "${OPENBALENA_VPN_SERVER_DH}" "${VPN_ENV_FILE}"
}
function setup_easyrsa() {
easyrsa_dir="$(mktemp -dt easyrsa.XXXXXXXX)"
easyrsa_url="https://github.com/OpenVPN/easy-rsa/releases/download/v3.1.6/EasyRSA-3.1.6.tgz"
echo " - Downloading easy-rsa..."
(cd "${easyrsa_dir}"; curl -sL "${easyrsa_url}" | tar xz --strip-components=1)
easyrsa_bin="${easyrsa_dir}/easyrsa"
trap "rm -rf \"${easyrsa_dir}\"" EXIT
}
function b64encode() {
echo "$@" | base64 --wrap=0 2>/dev/null || echo "$@" | base64 --break=0 2>/dev/null
}
function b64file() {
b64encode "$(cat "$@")"
}
function create_ca() {
# 1. Setup easy-rsa
echo " - Setting up easy-rsa..."
setup_easyrsa
# 2. Initialize PKI
echo " - Initializing PKI..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl init-pki
# 3. Create VPN CA
echo " - Creating CA..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl --days="${CA_EXPIRY_DAYS}" --req-cn="vpn-ca.${CN}" build-ca nopass
# 4. Generate and sign vpn server certificate
echo " - Generating and signing VPN server cert..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl --days="${CRT_EXPIRY_DAYS}" build-server-full "vpn.${CN}" nopass
# 5. Generate vpn dhparams (keysize of 2048 will do, 4096 can wind up taking hours to generate)
echo " - Generating DH params..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl --keysize=2048 gen-dh &> /dev/null
# 6. Update indexes and generate CRLs
echo " - Updating indexes..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl update-db
echo " - Generating CRL's..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl gen-crl
# 7. Update env files
update_env_files
echo "VPN server CA created and env files updated!"
}
function renew_server_cert() {
# 1. Setup easy-rsa
echo " - Setting up easy-rsa..."
setup_easyrsa
# 2. Renew vpn server certificate
echo " - Renewing VPN server certificate..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl --days="${CRT_EXPIRY_DAYS}" renew "vpn.${CN}"
# 3. Revoking old vpn server certificate
echo " - Revoking old VPN server certificate..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl revoke-renewed "vpn.${CN}"
# 4. Generating new CRL
echo " - Generating CRL's..."
"$easyrsa_bin" --pki-dir="${VPN_PKI_DIR}" --silent-batch --silent-ssl gen-crl
# 5. Update env files
update_env_files
echo "VPN server certificate renewed and env files updated!"
}
#------------------------------------------------------------------------------
# Main
#------------------------------------------------------------------------------
# Ensure CERTS_DIR exists
mkdir -p ${CERTS_DIR}
# Ensure vpn.env exits
if [ ! -f "${VPN_ENV_FILE}" ]; then
echo "File ${VPN_ENV_FILE} does not exists."
exit 1
fi
# Ensure api.env exits
if [ ! -f "${API_ENV_FILE}" ]; then
echo "File ${API_ENV_FILE} does not exists."
exit 1
fi
# Handle arguments
case ${ACTION} in
create-ca)
# Check if ca.crt already exists
if [ -f "${VPN_PKI_DIR}/ca.crt" ]; then
echo "Certificate authority already exists."
exit 1
fi
echo "=> Creating new CA and certs"
create_ca
;;
renew)
# Check if ca.crt already exists
if [ ! -f "${VPN_PKI_DIR}/ca.crt" ]; then
echo "No certificate authority found."
exit 1
fi
echo "=> Renewing the server certificate"
renew_server_cert
;;
*)
echo "Invalid action. Please use either 'create-ca' or 'renew'"
exit 1
;;
esac