Skip to content
Snippets Groups Projects
Unverified Commit f30804a9 authored by Michael Todorovic's avatar Michael Todorovic Committed by GitHub
Browse files

fix: allow the container to be started as non-root (#13214)

parent 0fb872bf
No related branches found
No related tags found
No related merge requests found
......@@ -24,121 +24,131 @@ if [ ! -z "$RDS_HOSTNAME" ]; then
export MB_DB_PORT=$RDS_PORT
fi
# Setup Java Options
JAVA_OPTS="${JAVA_OPTS} -XX:+IgnoreUnrecognizedVMOptions"
JAVA_OPTS="${JAVA_OPTS} -Dfile.encoding=UTF-8"
JAVA_OPTS="${JAVA_OPTS} -Dlogfile.path=target/log"
JAVA_OPTS="${JAVA_OPTS} -XX:+ExitOnOutOfMemoryError"
JAVA_OPTS="${JAVA_OPTS} -server"
# Avoid running metabase (or any server) as root where possible
# If you want to use specific user and group ids that matches an existing
# account on the host pass them in $MGID and $MUID when starting metabase
MGID=${MGID:-2000}
MUID=${MUID:-2000}
# create the group if it does not exist
# TODO: edit an existing group if MGID has changed
getent group metabase > /dev/null 2>&1
group_exists=$?
if [ $group_exists -ne 0 ]; then
addgroup -g $MGID -S metabase
if [ ! -z "$JAVA_TIMEZONE" ]; then
JAVA_OPTS="${JAVA_OPTS} -Duser.timezone=${JAVA_TIMEZONE}"
fi
# create the user if it does not exist
# TODO: edit an existing user if MGID has changed
id -u metabase > /dev/null 2>&1
user_exists=$?
if [[ $user_exists -ne 0 ]]; then
adduser -D -u $MUID -G metabase metabase
fi
# detect if the container is started as root or not
# if non-root, it's likely we run in a k8s environment with well maintained permissions
# if root, we need to check some permissions in order to exec metabase with a non-root user
# In that case, the container is run as root, metabase is run as a non-root user
if [ $(id -u) -ne 0 ]; then
# Launch the application
# exec is here twice on purpose to ensure that metabase runs as PID 1 (the init process)
# and thus receives signals sent to the container. This allows it to shutdown cleanly on exit
exec /bin/sh -c "exec java $JAVA_OPTS -jar /app/metabase.jar $@"
else
# Avoid running metabase (or any server) as root where possible
# If you want to use specific user and group ids that matches an existing
# account on the host pass them in $MGID and $MUID when starting metabase
MGID=${MGID:-2000}
MUID=${MUID:-2000}
#
## create the group if it does not exist
## TODO: edit an existing group if MGID has changed
getent group metabase > /dev/null 2>&1
group_exists=$?
if [ $group_exists -ne 0 ]; then
addgroup -g $MGID -S metabase
fi
db_file=${MB_DB_FILE:-/metabase.db}
# create the user if it does not exist
# TODO: edit an existing user if MGID has changed
id -u metabase > /dev/null 2>&1
user_exists=$?
if [[ $user_exists -ne 0 ]]; then
adduser -D -u $MUID -G metabase metabase
fi
# In order to run metabase as a non-root user in docker, we need to handle various
# cases where we where previously ran as root and have an existing database that
# consists of a bunch of files, that are owned by root, sitting in a directory that
# may only be writable by root. It's not safe to simply change the ownership or
# permissions of an unknown directory that may be a volume mounted on the host, so
# we will need to detect this and make a place that is going to be safe to set
# permissions on.
db_file=${MB_DB_FILE:-/metabase.db}
# So first some preliminary checks:
# In order to run metabase as a non-root user in docker, we need to handle various
# cases where we where previously ran as root and have an existing database that
# consists of a bunch of files, that are owned by root, sitting in a directory that
# may only be writable by root. It's not safe to simply change the ownership or
# permissions of an unknown directory that may be a volume mounted on the host, so
# we will need to detect this and make a place that is going to be safe to set
# permissions on.
# 1. Does this container have an existing H2 database file?
# 2. or an existing H2 database in it's own directory,
# 3. or neither?
# So first some preliminary checks:
# 1. Does this container have an existing H2 database file?
# 2. or an existing H2 database in it's own directory,
# 3. or neither?
# is there a pre-existing files only database without a metabase specific directory?
if ls $db_file\.* > /dev/null 2>&1; then
db_exists=true
else
db_exists=false
fi
# is it an old style file
if [[ -d "$db_file" ]]; then
db_directory=true
else
db_directory=false
fi
# If the db exits, and it's just some files in a shared directory we could do
# serious damage to peoples home or /tmp directories if we where to set the
# permissions on that directory to allow metabase to create db-lock and db-part
# file there. To keep them safe we make a new directory with the same name and
# move the db file into the new directory. If we where passed the name of a
# directory rather than a specific file, then we are safe to set permissions on
# that directory so there is no need to move anything.
# is there a pre-existing files only database without a metabase specific directory?
if ls $db_file\.* > /dev/null 2>&1; then
db_exists=true
else
db_exists=false
fi
# is it an old style file
if [[ -d "$db_file" ]]; then
db_directory=true
else
db_directory=false
fi
# an example file would look like /tmp/metabase.db/metabase.db.mv.db
new_db_dir=$(dirname $db_file)/$(basename $db_file)
# If the db exits, and it's just some files in a shared directory we could do
# serious damage to peoples home or /tmp directories if we where to set the
# permissions on that directory to allow metabase to create db-lock and db-part
# file there. To keep them safe we make a new directory with the same name and
# move the db file into the new directory. If we where passed the name of a
# directory rather than a specific file, then we are safe to set permissions on
# that directory so there is no need to move anything.
if [[ $db_exists = "true" && ! $db_directory = "true" ]]; then
mkdir $new_db_dir
mv $db_file\.* $new_db_dir/
fi
# an example file would look like /tmp/metabase.db/metabase.db.mv.db
new_db_dir=$(dirname $db_file)/$(basename $db_file)
# and for the new install case we create the directory
if [[ $db_exists = "false" && $db_directory = "false" ]]; then
mkdir $new_db_dir
fi
if [[ $db_exists = "true" && ! $db_directory = "true" ]]; then
mkdir $new_db_dir
mv $db_file\.* $new_db_dir/
fi
# the case where the DB exists and is a directory, there is nothing to do
# so nothing happens here. This will be the normal case.
# and for the new install case we create the directory
if [[ $db_exists = "false" && $db_directory = "false" ]]; then
mkdir $new_db_dir
fi
# next we tell metabase use the files we just moved into the directory
# or create the files in that directory if they don't exist.
export MB_DB_FILE=$new_db_dir/$(basename $db_file)
# the case where the DB exists and is a directory, there is nothing to do
# so nothing happens here. This will be the normal case.
# TODO: print big scary warning if they are configuring an ephemeral instance
# next we tell metabase use the files we just moved into the directory
# or create the files in that directory if they don't exist.
export MB_DB_FILE=$new_db_dir/$(basename $db_file)
chown metabase:metabase $new_db_dir $new_db_dir/* 2>/dev/null # all that fussing makes this safe
# TODO: print big scary warning if they are configuring an ephemeral instance
# Setup Java Options
JAVA_OPTS="${JAVA_OPTS} -XX:+IgnoreUnrecognizedVMOptions"
JAVA_OPTS="${JAVA_OPTS} -Dfile.encoding=UTF-8"
JAVA_OPTS="${JAVA_OPTS} -Dlogfile.path=target/log"
JAVA_OPTS="${JAVA_OPTS} -XX:+ExitOnOutOfMemoryError"
JAVA_OPTS="${JAVA_OPTS} -server"
chown metabase:metabase $new_db_dir $new_db_dir/* 2>/dev/null # all that fussing makes this safe
if [ ! -z "$JAVA_TIMEZONE" ]; then
JAVA_OPTS="${JAVA_OPTS} -Duser.timezone=${JAVA_TIMEZONE}"
fi
# Ensure JAR file is world readable
chmod o+r /app/metabase.jar
# Ensure JAR file is world readable
chmod o+r /app/metabase.jar
# Initialize the Metabase db from H2 dump, if available
INITIAL_DB=$(ls /app/initial*.db 2> /dev/null | head -n 1)
if [ -f "${INITIAL_DB}" ]; then
echo "Initializing Metabase database from H2 database ${INITIAL_DB}..."
chmod o+r ${INITIAL_DB}
su metabase -s /bin/sh -c "exec java $JAVA_OPTS -jar /app/metabase.jar load-from-h2 ${INITIAL_DB%.mv.db} $@"
# Initialize the Metabase db from H2 dump, if available
INITIAL_DB=$(ls /app/initial*.db 2> /dev/null | head -n 1)
if [ -f "${INITIAL_DB}" ]; then
echo "Initializing Metabase database from H2 database ${INITIAL_DB}..."
chmod o+r ${INITIAL_DB}
su metabase -s /bin/sh -c "exec java $JAVA_OPTS -jar /app/metabase.jar load-from-h2 ${INITIAL_DB%.mv.db} $@"
if [ $? -ne 0 ]; then
echo "Failed to initialize database from H2 database!"
exit 1
fi
if [ $? -ne 0 ]; then
echo "Failed to initialize database from H2 database!"
exit 1
echo "Done."
fi
echo "Done."
# Launch the application
# exec is here twice on purpose to ensure that metabase runs as PID 1 (the init process)
# and thus receives signals sent to the container. This allows it to shutdown cleanly on exit
exec su metabase -s /bin/sh -c "exec java $JAVA_OPTS -jar /app/metabase.jar $@"
fi
# Launch the application
# exec is here twice on purpose to ensure that metabase runs as PID 1 (the init process)
# and thus receives signals sent to the container. This allows it to shutdown cleanly on exit
exec su metabase -s /bin/sh -c "exec java $JAVA_OPTS -jar /app/metabase.jar $@"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment