Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run-as-non-root-in-container issue #147 #177

Merged
merged 3 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 24 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
FROM python:3.12-slim

ARG USER=cm_user
ARG UID=5678
ARG APP_DIR=/app

LABEL org.opencontainers.image.source=https://github.com/leolivier/cousins-matter
LABEL org.opencontainers.image.description='Docker image for the Cousins Matter application (https://github.com/leolivier/cousins-matter)'
LABEL org.opencontainers.image.url=https://github.com/leolivier/cousins-matter
LABEL org.opencontainers.image.branch=main
LABEL org.opencontainers.image.licenses=MIT


EXPOSE 8000

# Keeps Python from generating .pyc files in the container
Expand All @@ -15,33 +18,37 @@ ENV PYTHONDONTWRITEBYTECODE=1
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

WORKDIR /app
WORKDIR ${APP_DIR}

# install lighttpd for serving static and media files, redis for chat, sqlite3 and acl for init
# install lighttpd for serving static and media files, redis for chat, sqlite3 for installing database
# We must update twice as we need first to install gpg before installing redis
RUN apt-get update &&\
apt-get install -y lighttpd lsb-release curl gpg sqlite3 acl &&\
apt-get install -y lighttpd lsb-release curl gpg sqlite3 sudo supervisor && \
curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg &&\
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" > /etc/apt/sources.list.d/redis.list &&\
apt-get update &&\
apt-get install -y redis &&\
mkdir -p "/var/log/supervisord" "/var/run/supervisord" &&\
rm -rf /var/lib/apt/lists/*

# Allows docker to cache installed dependencies between builds
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
# RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
# USER appuser

COPY . .

# runs the production server
ENTRYPOINT ["/app/scripts/entrypoint.sh"]
# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug
CMD ["supervisord", "-c", "/app/supervisord.conf"]

VOLUME [ "/app/data" ]
VOLUME [ "/app/media" ]
# This is the user that will run the application but through supervisord so
# we keep running as root for supervisord which will run things as $USER
RUN adduser --uid ${UID} --disabled-password --gecos "" ${USER} && \
chown -R ${USER}:${USER} ${APP_DIR}

# now copy all (but .dockerignore rules) with $USER as proprietary
COPY --chown=${USER}:${USER} . .

ENV USER=${USER}
ENV APP_DIR=${APP_DIR}
# runs the production server (as root)
ENTRYPOINT ["./scripts/entrypoint.sh"]
# supervisord must run as root
CMD ["supervisord", "-c", "./supervisord.conf"]

VOLUME ./data ./media
30 changes: 15 additions & 15 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
django>=5.1.4
pillow>=10.4.0
django-crispy-forms>=2.3
crispy-bulma>=0.11.0
django-environ>=0.11.2
django-email-validation>=2.0.5
django-simple-captcha>=0.6.0
daphne>=4.1.2
supervisor>=4.2.5
reportlab>=4.2.2
channels>=4.1.0
channels_redis>=4.2.0
Babel>=2.15
django-cors-headers>=4.4.0
packaging>=24.0
django~=5.1.4
pillow~=11.0.0
django-crispy-forms~=2.3
crispy-bulma~=0.11.0
django-environ~=0.11.2
django-email-validation~=2.0.5
django-simple-captcha~=0.6.0
daphne~=4.1.2
supervisor~=4.2.5
reportlab~=4.2.5
channels~=4.2.0
channels_redis~=4.2.0
babel~=2.16.0
django-cors-headers~=4.6.0
packaging~=24.2
3 changes: 0 additions & 3 deletions scripts/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
#!/bin/bash
export APP_DIR=/app
. ./scripts/prepare-envt.sh || exit 1
# add full access to /app to www-data for redis and ligthttpd
setfacl -m u:www-data:rwx /app
echo "starting $@" # as provided in the Dockerfile
exec "$@"
16 changes: 7 additions & 9 deletions scripts/prepare-envt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,17 @@ then echo "Must be run from the cousins-matter directory!"
fi

for file in lighttpd.conf supervisord.conf
do cat template.$file | sed -e "s,{%APP_DIR%},$APP_DIR,g" > $file
do cat template.$file | sed -e "s,{%APP_DIR%},$APP_DIR,g;s,{%USER%},$USER,g" > $file
done

echo "collecting statics..."
python manage.py collectstatic --no-input
sudo -u $USER python manage.py collectstatic --no-input
echo "migrating the database..."
python manage.py migrate
python manage.py check
sudo -u $USER python manage.py migrate
sudo -u $USER python manage.py check

echo "importing predefined pages"
./scripts/import-flatpages.sh
echo "import done..."

sudo=''
if [[ -n "$EUID" && $EUID -ne 0 ]];
then sudo='sudo'
fi
$sudo mkdir -p "/var/log/supervisord" "/var/run/supervisord"
echo "environment is ready..."
5 changes: 3 additions & 2 deletions template.lighttpd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ $HTTP["url"] =~ "^/static/" {
} else $HTTP["url"] =~ "^/chat" { # websocket for the chat, add upgrade header
proxy.server = ("/" =>
( "cousinsmatter socket" => (
"socket" => "/var/run/cousinsmatter.socket"
# sockets are created in APP_DIR for running as non root
"socket" => "{%APP_DIR%}/cousinsmatter.socket"
))
)
proxy.header = ( "upgrade" => "enable" )
} else $HTTP["url"] =~ "" {
proxy.server = ("/" =>
( "cousinsmatter socket" => (
"socket" => "/var/run/cousinsmatter.socket"
"socket" => "{%APP_DIR%}/cousinsmatter.socket"
))
)
}
8 changes: 4 additions & 4 deletions template.supervisord.conf
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ stderr_logfile_maxbytes=0
directory={%APP_DIR%}
autostart=true
autorestart=true
command=daphne -u /var/run/cousinsmatter.socket --proxy-headers cousinsmatter.asgi:application
user=root
command=daphne -u {%APP_DIR%}/cousinsmatter.socket --proxy-headers cousinsmatter.asgi:application
user={%USER%}

[program:lighttpd]
stdout_logfile=/dev/stdout
Expand All @@ -28,7 +28,7 @@ directory={%APP_DIR%}
autostart=true
autorestart=true
command=lighttpd -D -f {%APP_DIR%}/lighttpd.conf
user=www-data
user={%USER%}

[program:redis]
stdout_logfile=/dev/stdout
Expand All @@ -39,4 +39,4 @@ directory={%APP_DIR%}
autostart=true
autorestart=true
command=redis-server
user=www-data
user={%USER%}
Loading