Skip to content
Merged
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
48 changes: 37 additions & 11 deletions {{ cookiecutter.name }}/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,27 @@ LABEL maintainer="{{ cookiecutter.email }}"
ENV DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1
ENV STATIC_ROOT=/var/lib/django-static
# Define user ids to ensure consistent permissions, e.g., for mounted volumes
ENV UID=999 GID=999

RUN apt-get update \
&& apt-get --no-install-recommends install -y gettext locales-all tzdata git wait-for-it wget \
&& rm -rf /var/lib/apt/lists/*

RUN groupadd --system --gid=${GID} "web" \
&& useradd --system --uid=${UID} --gid=${GID} --create-home --home-dir "/code" "web"

COPY --from=uwsgi-compile /uwsgi /usr/local/bin/
COPY --from=deps-compile /code/.venv /code/.venv
COPY src /code/src
COPY --from=deps-compile --chown=web:web /code/.venv /code/.venv
COPY --chown=web:web src /code/src
ENV PATH="/code/.venv/bin:$PATH"

WORKDIR /code/src

RUN chmod +x ./manage.py
RUN ./manage.py compilemessages
RUN ./manage.py collectstatic --noinput
RUN python manage.py compilemessages
RUN python manage.py collectstatic --noinput

USER web
Comment on lines 56 to +61
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nkiryanov а может еще USER web перед compilemessages и collectstatic поставить?

тогда созданные файлы будут принадлежать web-у. Так же, чисто теоретически, не получится сделать атаку, что какая-то хрень в них из под рута запустится.

Copy link
Contributor Author

@nkiryanov nkiryanov May 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так же, чисто теоретически, не получится сделать атаку, что какая-то хрень в них из под рута запустится.

Если правильно понимаю это безопасность не повысит: важно ведь не кто owner у файла, а каким юзером они запускаются.

Тут даже наоборот: если поставить USER web перед collectstatic, то не получится статику собрать, т.к. пишем её в /var/lib/django-static и не хватит прав на запись. То есть можно права на /var/lib/django-static на web поменять после выполнения, но кажется смысла в этом нет — я проверял что есть права на чтение у web и раздаётся ок.

Но! Это удивило, но что-то не сообразил сразу сделать issue — а сфига статика в /var/lib/django-static собирается. Походу у нас древняя ошибка в settings.STATIC_ROOT — там релатив путь, хотя обычно ожидаешь абсолютный и возможно ноги из этого растут.

Сделаю новый issue разобраться с этим — прежде всего интересует откуда берётся /var/lib/django-static и куда по уму собирать. Глядишь и в Makefile не будем папку с статикой создавать, т.к. создадим в процессе bootstrap.

update:
Новый issue сделал разобраться с relative статикой. Откуда /var/lib/django-static — всё понятно (в dockerfile).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Согласен со Славой насчет

тогда созданные файлы будут принадлежать web-у.

Мы создаем юзера, который запускает процесс в контейнере и может работать только со своими файлами. В этом смысле, chown`нить файлы имеет смысл, чтобы образ был целостным.

Навсякий, идея всей задачи уберечься от какого-нибудь zero-day в котором рут пользователь изнутри контейнера может получить доступ к руту хоста, а для этого нужно максимально сузить доступы основного юзера в образе.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не получится сделать атаку, что какая-то хрень в них из под рута запустится.
важно ведь не кто owner у файла, а каким юзером они запускаются.

Тут в голову лезет сценарий, что кто-то проник в репозиторий и деплоит код, в котором вместо джанговской collectstatic выполняется своя рутовая команда в кастомном collectstatic. Как раз, чтобы прав не хватило на это.

Новый issue сделал разобраться с relative статикой.
круть! Про это не думал.


# Also to mark that when CMD is used in shell form, it is a conscious decision
SHELL ["/bin/bash", "-c"]
Expand All @@ -62,26 +68,46 @@ FROM base AS web
HEALTHCHECK --interval=15s --timeout=15s --start-period=15s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:8000/api/v1/healthchecks/

CMD ./manage.py migrate && uwsgi --master --http :8000 --venv /code/.venv/ --wsgi app.wsgi --workers 2 --threads 2 --harakiri 25 --max-requests 1000 --log-x-forwarded-for

CMD python manage.py migrate \
&& uwsgi \
--master \
--http=:8000 \
--venv=/code/.venv/ \
--wsgi=app.wsgi \
--workers=2 \
--threads=2 \
--harakiri=25 \
--max-requests=1000 \
--log-x-forwarded-for

FROM base AS worker

ENV _CELERY_APP=app.celery
HEALTHCHECK --interval=15s --timeout=15s --start-period=5s --retries=3 \
CMD celery --app=${_CELERY_APP} inspect ping --destination=celery@$HOSTNAME

CMD celery --app=${_CELERY_APP} worker --concurrency=${CONCURENCY:-2} --hostname="celery@%h" --max-tasks-per-child=${MAX_REQUESTS_PER_CHILD:-50} --time-limit=${TIME_LIMIT:-900} --soft-time-limit=${SOFT_TIME_LIMIT:-45}
CMD celery \
--app=${_CELERY_APP} \
worker \
--concurrency=${CONCURENCY:-2} \
--hostname="celery@%h" \
--max-tasks-per-child=${MAX_REQUESTS_PER_CHILD:-50} \
--time-limit=${TIME_LIMIT:-900} \
--soft-time-limit=${SOFT_TIME_LIMIT:-45}


FROM base AS scheduler

ENV _SCHEDULER_DB_PATH=/var/db/scheduler
USER root
RUN mkdir -p ${_SCHEDULER_DB_PATH} && chown nobody ${_SCHEDULER_DB_PATH}
RUN mkdir --parent ${_SCHEDULER_DB_PATH} && chown web:web ${_SCHEDULER_DB_PATH}
USER web
VOLUME ${_SCHEDULER_DB_PATH}
USER nobody

ENV _CELERY_APP=app.celery
HEALTHCHECK NONE
CMD celery --app=${_CELERY_APP} beat --pidfile=/tmp/celerybeat.pid --schedule=${_SCHEDULER_DB_PATH}/celerybeat-schedule.db
CMD celery \
--app=${_CELERY_APP} \
beat \
--pidfile=/tmp/celerybeat.pid \
--schedule=${_SCHEDULER_DB_PATH}/celerybeat-schedule.db