#!/bin/sh # SPDX-License-Identifier: Apache-2.0 # (c) 2024, Konstantin Demin set -ef # Prevent core dumps set +e ulimit -Sc 0 ulimit -Hc 0 set -e # not needed anyway unset LD_PRELOAD binary=$(command -v vault) ; : "${binary:?}" binary=$(readlink -e "${binary}") ; : "${binary:?}" have_capability() { # local cap=$1 setpriv -d | awk -F: '/^Capability bounding set:/{print $2}' | grep -Eiq "(^\s*|,)$1(,|\$)" } have_ipc_lock=1 have_capability ipc_lock || have_ipc_lock=0 # ref: https://pkg.go.dev/strconv#ParseBool gobool_to_int() { # local value=$1 # local default=$2 case "$1" in 1 | t | T | TRUE | true | True ) echo 1 ;; 0 | f | F | FALSE | false | False ) echo 0 ;; * ) echo "${2:-error}" ;; esac } int_to_gobool() { # local int=$1 case "$1" in 0 ) echo false ;; 1 ) echo true ;; esac } # ref: https://developer.hashicorp.com/vault/docs/configuration#disable_mlock with_mlock=$(gobool_to_int "${VAULT_DISABLE_MLOCK:-0}" 1) with_mlock=$((1 - with_mlock)) # VAULT_CONFIG_DIR isn't exposed as a volume but you can compose additional # config files in there if you use this image as a base, or use # VAULT_LOCAL_CONFIG below. VAULT_CONFIG_DIR=${VAULT_CONFIG_DIR:-/vault/config} case "$1" in vault ) # no adjustments are required - running Vault with directly specified command and options : ;; -* ) # running with options set -- vault "$@" ;; server ) shift dev_mode=0 have_dev_listen=0 for i ; do case "$i" in -dev | -dev-tls ) dev_mode=1 ;; -dev-listen-address ) have_dev_listen=1 ;; esac done ; unset i # adjust environment for dev mode if [ "${dev_mode}" = 1 ] && [ "${have_dev_listen}" = 0 ] ; then export VAULT_DEV_LISTEN_ADDRESS="${VAULT_DEV_LISTEN_ADDRESS:-"0.0.0.0:8200"}" fi # not needed anymore unset dev_mode have_dev_listen set -- vault server \ -config="${VAULT_CONFIG_DIR}" \ "$@" ;; version ) set -- vault "$@" ;; * ) if vault --help "$1" 2>&1 | grep -Fq "vault $1" ; then # We can't use the return code to check for the existence of a subcommand, so # we have to use grep to look for a pattern in the help output. set -- vault "$@" fi ;; esac if [ "$1" != 'vault' ] ; then unset SKIP_PERMCHECK SKIP_IPC_LOCK SKIP_VAULT_PRECONF SKIP_CHOWN SKIP_SETCAP SKIP_PERMCHECK=1 SKIP_IPC_LOCK=1 SKIP_VAULT_PRECONF=1 SKIP_CHOWN=1 SKIP_SETCAP=1 fi if [ "${SKIP_PERMCHECK:-0}" != 1 ] ; then # Due to OpenShift environment compatibility, we have to allow group write # access to the Vault configuration. This requires us to disable the stricter # file permissions checks introduced in Vault v1.11.0. # ref: https://developer.hashicorp.com/vault/docs/configuration with_permcheck=0 x=none # Vault 1.11 and older if [ -n "${VAULT_DISABLE_FILE_PERMISSIONS_CHECK}" ] ; then x=$(gobool_to_int "${VAULT_DISABLE_FILE_PERMISSIONS_CHECK}") case "$x" in 0 | 1 ) with_permcheck=$((1 - x)) ;; esac fi # Vault 1.12 and newer if [ -n "${VAULT_ENABLE_FILE_PERMISSIONS_CHECK}" ] ; then x=$(gobool_to_int "${VAULT_ENABLE_FILE_PERMISSIONS_CHECK}") case "$x" in 0 | 1 ) with_permcheck=$x ;; esac fi unset x # adjust variables set -a VAULT_ENABLE_FILE_PERMISSIONS_CHECK=$(int_to_gobool "${with_permcheck}") VAULT_DISABLE_FILE_PERMISSIONS_CHECK=$(int_to_gobool $((1 - with_permcheck)) ) set +a unset with_permcheck fi unset SKIP_PERMCHECK if [ "${SKIP_IPC_LOCK:-0}" != 1 ] ; then if [ "${have_ipc_lock}" = 1 ] ; then set +e echo "Limits before:" # sed -En '1p;/[Ll]ocked/p' < /proc/$$/limits awk 'NR==1{print} /[Ll]ocked/{print}' /proc/$$/limits ulimit -Hl "${MEMLOCK_LIMIT:-unlimited}" x=$(ulimit -Hl) ulimit -Sl "$x" unset x echo "Limits after:" # sed -En '1p;/[Ll]ocked/p' < /proc/$$/limits awk 'NR==1{print} /[Ll]ocked/{print}' /proc/$$/limits set -e fi >&2 if [ "${with_mlock}" = 1 ] && [ "${have_ipc_lock}" = 0 ] ; then echo echo '!!!' echo echo "missing CAP_IPC_LOCK privilege: please run container with \`--cap-add IPC_LOCK'" echo 'ref: https://developer.hashicorp.com/vault/docs/configuration#disable_mlock' echo echo '!!!' echo fi >&2 fi unset SKIP_IPC_LOCK if [ "${SKIP_VAULT_PRECONF:-0}" != 1 ] ; then # You can also set the VAULT_LOCAL_CONFIG environment variable to pass some # Vault configuration JSON without having to bind any volumes. VAULT_LOCAL_CONFIG_FILE=${VAULT_LOCAL_CONFIG_FILE:-"${VAULT_CONFIG_DIR}/local.json"} while [ -n "${VAULT_LOCAL_CONFIG}" ] ; do touch "${VAULT_LOCAL_CONFIG_FILE}" || break printf '%s' "${VAULT_LOCAL_CONFIG}" > "${VAULT_LOCAL_CONFIG_FILE}" || break break done unset VAULT_LOCAL_CONFIG VAULT_LOCAL_CONFIG_FILE # Allow setting VAULT_REDIRECT_ADDR and VAULT_CLUSTER_ADDR using an interface # name instead of an IP address. The interface name is specified using # VAULT_REDIRECT_INTERFACE and VAULT_CLUSTER_INTERFACE environment variables. If # VAULT_*_ADDR is also set, the resulting URI will combine the protocol and port # number with the IP of the named interface. have_iproute2=1 command -v ip >/dev/null 2>&1 || have_iproute2=0 err_iproute2_missing() { printf '%s\n' "$1: iproute2 (/usr/sbin/ip) is missing" >&2 } err_iface_missing() { printf '%s\n' "$1: interface error" >&2 } err_ipv4_addr_missing() { printf '%s\n' "$1: no IPv4 addresses are present on interface" >&2 } iface_exist() { # local if_name=$1 ip link show dev "$1" >/dev/null } get_iface_ipv4_addr () { # local if_name=$1 # local uri_template=$2 ip address show dev "$1" \ | awk -v "uri=$2" \ '/^\s*inet\s/ { ip=gensub(/(.+)\/.+/, "\\1", "g", $2) print gensub(/^(.+:\/\/).+(:.+)$/, "\\1" ip "\\2", "g", uri) exit }' } while [ -n "${VAULT_REDIRECT_INTERFACE}" ] ; do new_vault_redirect_addr=${VAULT_REDIRECT_ADDR:-"http://0.0.0.0:8200"} errmsg_vault_redirect_interface="unable to adjust VAULT_REDIRECT_ADDR='${new_vault_redirect_addr}' with VAULT_REDIRECT_INTERFACE='${VAULT_REDIRECT_INTERFACE}'" if [ "${have_iproute2}" = 0 ] ; then err_iproute2_missing "${errmsg_vault_redirect_interface}" break fi if ! iface_exist "${VAULT_REDIRECT_INTERFACE}" ; then err_iface_missing "${errmsg_vault_redirect_interface}" break fi set +e new_vault_redirect_addr=$(get_iface_ipv4_addr "${VAULT_REDIRECT_INTERFACE}" "${new_vault_redirect_addr}") set -e if [ -z "${new_vault_redirect_addr}" ] ; then err_ipv4_addr_missing "${errmsg_vault_redirect_interface}" break fi if [ -n "${VAULT_REDIRECT_ADDR}" ] && [ "${VAULT_REDIRECT_ADDR}" != "${new_vault_redirect_addr}" ]; then echo "changing VAULT_REDIRECT_ADDR: ${VAULT_REDIRECT_ADDR} -> ${new_vault_redirect_addr}" >&2 fi export VAULT_REDIRECT_ADDR="${new_vault_redirect_addr}" echo "using interface ${VAULT_REDIRECT_INTERFACE} for VAULT_REDIRECT_ADDR: ${VAULT_REDIRECT_ADDR}" >&2 break done unset VAULT_REDIRECT_INTERFACE new_vault_redirect_addr errmsg_vault_redirect_interface while [ -n "${VAULT_CLUSTER_INTERFACE}" ] ; do new_vault_cluster_addr=${VAULT_CLUSTER_ADDR:-"https://0.0.0.0:8201"} errmsg_vault_cluster_interface="unable to adjust VAULT_CLUSTER_ADDR='${new_vault_cluster_addr}' with VAULT_CLUSTER_INTERFACE='${VAULT_CLUSTER_INTERFACE}'" if [ "${have_iproute2}" = 0 ] ; then err_iproute2_missing "${errmsg_vault_cluster_interface}" break fi if ! iface_exist "${VAULT_CLUSTER_INTERFACE}" ; then err_iface_missing "${errmsg_vault_cluster_interface}" break fi set +e new_vault_cluster_addr=$(get_iface_ipv4_addr "${VAULT_CLUSTER_INTERFACE}" "${new_vault_cluster_addr}") set -e if [ -z "${new_vault_cluster_addr}" ] ; then err_ipv4_addr_missing "${errmsg_vault_cluster_interface}" break fi if [ -n "${VAULT_CLUSTER_ADDR}" ] && [ "${VAULT_CLUSTER_ADDR}" != "${new_vault_cluster_addr}" ] ; then echo "changing VAULT_CLUSTER_ADDR: ${VAULT_CLUSTER_ADDR} -> ${new_vault_cluster_addr}" >&2 fi export VAULT_CLUSTER_ADDR="${new_vault_cluster_addr}" echo "using ${VAULT_CLUSTER_INTERFACE} for VAULT_CLUSTER_ADDR: ${VAULT_CLUSTER_ADDR}" break done unset VAULT_CLUSTER_INTERFACE new_vault_cluster_addr errmsg_vault_cluster_interface fi unset SKIP_VAULT_PRECONF while [ "${SKIP_CHOWN:-0}" != 1 ] ; do vault_uid=$(id -u vault) || break fix_ownership() { x=0 for i ; do [ -n "$i" ] || continue if ! [ -d "$i" ] ; then # NB: running through `env' to avoid shell glitch env printf 'not a directory: %q\n' "$i" >&2 continue fi # NB: running through `env' to avoid shell glitch x=$(env stat -Lc '%u' "$i") if [ "$x" = "${vault_uid}" ] ; then # nothing to fix (probably) continue fi if ! chown -R vault:vault "$i" ; then # NB: running through `env' to avoid shell glitch env printf 'Could not chown %q (may not have appropriate permissions)\n' "$i" >&2 fi done unset x i } find_fast() { find "$@" -printf . -quit | grep -Fq . } check_ownership() { for i ; do [ -n "$i" ] || continue if ! [ -d "$i" ] ; then # NB: running through `env' to avoid shell glitch env printf 'not a directory: %q\n' "$i" >&2 continue fi find_fast "$i/" ! -uid "${vault_uid}" || continue echo "files not owned by Vault:" >&2 set +e find "$i/" ! -uid "${vault_uid}" -ls set -e done unset i } fix_ownership /vault/config /vault/logs /vault/file check_ownership /vault/config /vault/logs /vault/file break done unset SKIP_CHOWN if [ "${SKIP_SETCAP:-0}" != 1 ] ; then have_libcap2=1 command -v setcap >/dev/null 2>&1 || have_libcap2=0 err_libcap2_missing() { printf '%s\n' "$1: libcap2-bin (/usr/sbin/setcap) is missing" >&2 } drop_ipc_lock=0 # try running Vault with CAP_IPC_LOCK while [ "${with_mlock}" = 1 ] ; do errmsg_vault_mlock="unable to run Vault with CAP_IPC_LOCK" if [ "${have_libcap2}" = 0 ] ; then err_libcap2_missing "${errmsg_vault_mlock}" break fi if ! setcap cap_ipc_lock=+ep "${binary}" ; then echo "${errmsg_vault_mlock}: setcap failed (read-only filesystem?)" break fi # In the case vault has been started in a container without IPC_LOCK privileges if ! "${binary}" -version >/dev/null 2>&1 ; then echo "${errmsg_vault_mlock}" drop_ipc_lock=1 break fi break done unset errmsg_vault_mlock # not needed anymore unset have_libcap2 if [ "${drop_ipc_lock}" = 1 ] ; then # ignore any error setcap cap_ipc_lock=-ep "${binary}" || : fi unset drop_ipc_lock fi unset SKIP_SETCAP # not needed anymore unset have_ipc_lock have_iproute2 with_mlock # In case of Docker, where swap may be enabled, we # still require mlocking to be available. So this script # was executed as root to make this happen, however, # we're now rerunning the entrypoint script as the Vault # user but no longer need to run setup code for setcap # or chowning directories (previously done on the first run). if [ "$(id -u)" = '0' ] ; then export SKIP_PERMCHECK=1 SKIP_IPC_LOCK=1 SKIP_VAULT_PRECONF=1 SKIP_CHOWN=1 SKIP_SETCAP=1 exec \ setpriv --reuid=vault --regid=vault --init-groups \ env USER=vault LOGNAME=vault HOME=/home/vault SHELL=/bin/sh \ "$0" "$@" else exec "$@" fi