security: run Docker container as non-root user

Running containers as a non-root user is a long standing security practice.
The changes ensure that the sourcebot user is created and has the correct
level of permissions to run all its dependencies (postgres, redis and node).

Please note that as a side effect, existing mounted volumes would need to
have their ownership reviewed or it may not be able to access the files.
This is specially the case for previous versions that would create said
files as 0:0.

To fix that, users can run chown -R 1500:1500 /path/.sourcebot. The chmod
may also need to be a bit more strict in such cases, so changing that is
advised: chown -R 0750 /path/.sourcebot.

Signed-off-by: Paulo Gomes <pjbgf@linux.com>
This commit is contained in:
Paulo Gomes 2025-09-18 08:18:51 +01:00 committed by bkellam
parent 33c732855f
commit 2e3feca05f
2 changed files with 36 additions and 9 deletions

View file

@ -225,12 +225,26 @@ RUN mkdir -p /run/postgresql && \
chown -R postgres:postgres /run/postgresql && \ chown -R postgres:postgres /run/postgresql && \
chmod 775 /run/postgresql chmod 775 /run/postgresql
# To run as non-root, the user must be part of postgres, redis and node groups
RUN addgroup -g 1500 sourcebot && \
adduser -D -u 1500 -h /app -S sourcebot && \
adduser sourcebot postgres && \
adduser sourcebot redis && \
adduser sourcebot node && \
chown -R sourcebot /data && \
chown -R sourcebot /app && \
mkdir /var/log/sourcebot && \
chown sourcebot /var/log/sourcebot
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY prefix-output.sh ./prefix-output.sh COPY prefix-output.sh ./prefix-output.sh
RUN chmod +x ./prefix-output.sh RUN chmod +x ./prefix-output.sh
COPY entrypoint.sh ./entrypoint.sh COPY entrypoint.sh ./entrypoint.sh
RUN chmod +x ./entrypoint.sh RUN chmod +x ./entrypoint.sh
USER sourcebot
EXPOSE 3000 EXPOSE 3000
ENV PORT=3000 ENV PORT=3000
ENV HOSTNAME="0.0.0.0" ENV HOSTNAME="0.0.0.0"

View file

@ -77,19 +77,20 @@ fi
# Check if DATA_CACHE_DIR exists, if not create it # Check if DATA_CACHE_DIR exists, if not create it
if [ ! -d "$DATA_CACHE_DIR" ]; then if [ ! -d "$DATA_CACHE_DIR" ]; then
mkdir -p "$DATA_CACHE_DIR" mkdir -m 0750 -p "$DATA_CACHE_DIR"
fi fi
# Check if DATABASE_DATA_DIR exists, if not initialize it # Check if DATABASE_DATA_DIR exists, if not initialize it
if [ "$DATABASE_EMBEDDED" = "true" ] && [ ! -d "$DATABASE_DATA_DIR" ]; then if [ "$DATABASE_EMBEDDED" = "true" ] && [ ! -d "$DATABASE_DATA_DIR" ]; then
echo -e "\e[34m[Info] Initializing database at $DATABASE_DATA_DIR...\e[0m" echo -e "\e[34m[Info] Initializing database at $DATABASE_D\ATA_DIR...\e[0m"
mkdir -p $DATABASE_DATA_DIR && chown -R postgres:postgres "$DATABASE_DATA_DIR" mkdir -m 0750 -p $DATABASE_DATA_DIR
su postgres -c "initdb -D $DATABASE_DATA_DIR"
initdb -D "$DATABASE_DATA_DIR"
fi fi
# Create the redis data directory if it doesn't exist # Create the redis data directory if it doesn't exist
if [ "$REDIS_EMBEDDED" = "true" ] && [ ! -d "$REDIS_DATA_DIR" ]; then if [ "$REDIS_EMBEDDED" = "true" ] && [ ! -d "$REDIS_DATA_DIR" ]; then
mkdir -p $REDIS_DATA_DIR mkdir -m 0750 -p $REDIS_DATA_DIR
fi fi
if [ -z "$SOURCEBOT_ENCRYPTION_KEY" ]; then if [ -z "$SOURCEBOT_ENCRYPTION_KEY" ]; then
@ -180,13 +181,25 @@ echo "{\"version\": \"$NEXT_PUBLIC_SOURCEBOT_VERSION\", \"install_id\": \"$SOURC
# Start the database and wait for it to be ready before starting any other service # Start the database and wait for it to be ready before starting any other service
if [ "$DATABASE_EMBEDDED" = "true" ]; then if [ "$DATABASE_EMBEDDED" = "true" ]; then
su postgres -c "postgres -D $DATABASE_DATA_DIR" & postgres -D "$DATABASE_DATA_DIR" &
until pg_isready -h localhost -p 5432 -U postgres; do until pg_isready -h localhost -p 5432 -d sourcebot -U postgres; do
echo -e "\e[34m[Info] Waiting for the database to be ready...\e[0m" echo -e "\e[34m[Info] Waiting for the database to be ready...\e[0m"
sleep 1 sleep 1
# As postgres runs in the background, we must check if it is still
# running, otherwise the "until" loop will be running indefinitely.
if ! pgrep -x "postgres" > /dev/null; then
echo "postgres failed to run"
exit 1
break
fi
done done
# Check if the database already exists, and create it if it dne # Running as non-root we need to ensure the postgres account is created.
psql -U postgres -tc "SELECT 1 FROM pg_roles WHERE rolname='postgres'" | grep -q 1 \
|| createuser postgres -s
# Check if the database already exists, and create it if it doesn't
EXISTING_DB=$(psql -U postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sourcebot'") EXISTING_DB=$(psql -U postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sourcebot'")
if [ "$EXISTING_DB" = "1" ]; then if [ "$EXISTING_DB" = "1" ]; then