406 lines
12 KiB
Bash
Executable File
406 lines
12 KiB
Bash
Executable File
#!/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
|