diff --git a/Dockerfile b/Dockerfile index c57e95d3..7ef95ef1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 @@ -15,16 +18,17 @@ 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 @@ -32,16 +36,19 @@ 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 diff --git a/requirements.txt b/requirements.txt index f94bf2fe..64ab143b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh index 8bb110a3..592651d9 100755 --- a/scripts/entrypoint.sh +++ b/scripts/entrypoint.sh @@ -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 "$@" diff --git a/scripts/prepare-envt.sh b/scripts/prepare-envt.sh index 8fb96350..c036d8e1 100644 --- a/scripts/prepare-envt.sh +++ b/scripts/prepare-envt.sh @@ -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..." diff --git a/template.lighttpd.conf b/template.lighttpd.conf index 46f786d6..2088625e 100644 --- a/template.lighttpd.conf +++ b/template.lighttpd.conf @@ -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" )) ) } diff --git a/template.supervisord.conf b/template.supervisord.conf index 6e9ed510..420e6e5a 100644 --- a/template.supervisord.conf +++ b/template.supervisord.conf @@ -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 @@ -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 @@ -39,4 +39,4 @@ directory={%APP_DIR%} autostart=true autorestart=true command=redis-server -user=www-data +user={%USER%}