4aa0be3df5
Currently, all source files are located in `/home/django`, and owned by `django`. This means that if there's any vulnerability that lets an attacker overwrite files in the server, they can replace source files with their own code, and potentially get that code executed. That's pretty bad, so I want to harden against that. Make all source files owned by root, and move them to `/opt/cvat`. Add a `manage.py` symlink in `/home/django` for backwards compatibility. It happens that if a script is a symlink, Python does not add the symlink's directory to `sys.path`, which is great for us, since that lets us avoid a writable directory on there. Still, even though `/home/django/manage.py` is owned by root, an attacker could potentially be able to delete it and replace it with their own malicious file. To be a bit more safe, replace `~/manage.py` calls in backend scripts with `django-admin`. To make sure CVAT can still find the data directory, add a new environment variable, `CVAT_BASE_DIR` and set it in the Docker image. This also fixes a minor bug: we no longer override the `HOME` environment variable in the `Dockerfile`, so now it's automatically set by `Docker` depending on the current user.
221 lines
7.1 KiB
Docker
221 lines
7.1 KiB
Docker
# syntax=docker/dockerfile:1
|
|
|
|
ARG BASE_IMAGE=ubuntu:24.04
|
|
|
|
FROM ${BASE_IMAGE} AS build-image-base
|
|
|
|
RUN apt-get update && \
|
|
DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \
|
|
cargo-1.85 \
|
|
curl \
|
|
g++ \
|
|
gcc \
|
|
git \
|
|
libhdf5-dev \
|
|
libldap2-dev \
|
|
libmp3lame-dev \
|
|
libsasl2-dev \
|
|
libxml2-dev \
|
|
libxmlsec1-dev \
|
|
libxmlsec1-openssl \
|
|
make \
|
|
nasm \
|
|
pkg-config \
|
|
python3-dev \
|
|
python3-pip \
|
|
&& update-alternatives \
|
|
--install /usr/bin/rustc rustc /usr/bin/rustc-1.85 185 \
|
|
--slave /usr/bin/cargo cargo /usr/bin/cargo-1.85 \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
|
|
|
|
# We build OpenH264, FFmpeg and PyAV in a separate build stage,
|
|
# because this way Docker can do it in parallel to all the other packages.
|
|
FROM build-image-base AS build-image-av
|
|
|
|
# Compile Openh264 and FFmpeg
|
|
ARG PREFIX=/opt/ffmpeg
|
|
ARG PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig
|
|
|
|
ENV FFMPEG_VERSION=8.0 \
|
|
OPENH264_VERSION=2.6.0
|
|
|
|
WORKDIR /tmp/openh264
|
|
RUN curl -sL https://github.com/cisco/openh264/archive/v${OPENH264_VERSION}.tar.gz --output - | \
|
|
tar -zx --strip-components=1 && \
|
|
make -j5 && make install-shared PREFIX=${PREFIX} && make clean
|
|
|
|
WORKDIR /tmp/ffmpeg
|
|
RUN curl -sL https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz --output - | \
|
|
tar -zx --strip-components=1 && \
|
|
./configure --disable-nonfree --disable-gpl --enable-libopenh264 --enable-libmp3lame \
|
|
--enable-shared --disable-static --disable-doc --disable-programs --prefix="${PREFIX}" && \
|
|
make -j5 && make install && make clean
|
|
|
|
COPY utils/dataset_manifest/requirements.txt /tmp/utils/dataset_manifest/requirements.txt
|
|
|
|
# Since we're using pip-compile-multi, each dependency can only be listed in
|
|
# one requirements file. In the case of PyAV, that should be
|
|
# `dataset_manifest/requirements.txt`. Make sure it's actually there,
|
|
# and then remove everything else.
|
|
RUN grep -q '^av==' /tmp/utils/dataset_manifest/requirements.txt
|
|
RUN sed -i '/^av==/!d' /tmp/utils/dataset_manifest/requirements.txt
|
|
|
|
RUN --mount=type=cache,target=/root/.cache/pip/http-v2 \
|
|
python3 -m pip wheel --no-binary=av \
|
|
-r /tmp/utils/dataset_manifest/requirements.txt \
|
|
-w /tmp/wheelhouse
|
|
|
|
# This stage builds wheels for all dependencies (except PyAV)
|
|
FROM build-image-base AS build-image
|
|
|
|
COPY cvat/requirements/ /tmp/cvat/requirements/
|
|
COPY utils/dataset_manifest/requirements.txt /tmp/utils/dataset_manifest/requirements.txt
|
|
|
|
# Exclude av from the requirements file
|
|
RUN sed -i '/^av==/d' /tmp/utils/dataset_manifest/requirements.txt
|
|
|
|
ARG CVAT_CONFIGURATION="production"
|
|
|
|
# https://github.com/SAML-Toolkits/python3-saml#note
|
|
# Building from source is recommended for lxml and xmlsec to avoid libxml2 version conflicts
|
|
RUN --mount=type=cache,target=/root/.cache/pip/http-v2 \
|
|
DATUMARO_HEADLESS=1 python3 -m pip wheel --no-deps --no-binary lxml,xmlsec \
|
|
-r /tmp/cvat/requirements/${CVAT_CONFIGURATION}.txt \
|
|
-w /tmp/wheelhouse
|
|
|
|
FROM golang:1.26.5 AS build-smokescreen
|
|
|
|
RUN git clone --filter=blob:none --no-checkout https://github.com/stripe/smokescreen.git
|
|
RUN cd smokescreen && git checkout eb1ac09 && go build -o /tmp/smokescreen
|
|
|
|
FROM ${BASE_IMAGE}
|
|
|
|
ARG http_proxy
|
|
ARG https_proxy
|
|
ARG no_proxy
|
|
ARG socks_proxy
|
|
ARG TZ="Etc/UTC"
|
|
|
|
ENV TERM=xterm \
|
|
http_proxy=${http_proxy} \
|
|
https_proxy=${https_proxy} \
|
|
no_proxy=${no_proxy} \
|
|
socks_proxy=${socks_proxy} \
|
|
LANG='C.UTF-8' \
|
|
LC_ALL='C.UTF-8' \
|
|
TZ=${TZ}
|
|
|
|
ARG USER="django"
|
|
ARG CVAT_CONFIGURATION="production"
|
|
ENV DJANGO_SETTINGS_MODULE="cvat.settings.${CVAT_CONFIGURATION}"
|
|
|
|
# Install necessary apt packages
|
|
RUN apt-get update && \
|
|
DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \
|
|
adduser \
|
|
bzip2 \
|
|
ca-certificates \
|
|
curl \
|
|
git \
|
|
libgl1 \
|
|
libgomp1 \
|
|
libldap2 \
|
|
libmp3lame0 \
|
|
libpython3.12t64 \
|
|
libsasl2-2 \
|
|
libxml2 \
|
|
libxmlsec1 \
|
|
libxmlsec1-openssl \
|
|
nginx \
|
|
p7zip-full \
|
|
poppler-utils \
|
|
python3 \
|
|
python3-venv \
|
|
supervisor \
|
|
tzdata \
|
|
unrar \
|
|
wait-for-it \
|
|
&& ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime && \
|
|
dpkg-reconfigure -f noninteractive tzdata && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install smokescreen
|
|
COPY --from=build-smokescreen /tmp/smokescreen /usr/local/bin/smokescreen
|
|
|
|
# Add a non-root user
|
|
ENV USER=${USER}
|
|
RUN deluser --remove-home ubuntu && \
|
|
adduser --uid=1000 --shell /bin/bash --disabled-password --gecos "" ${USER}
|
|
ENV CVAT_BASE_DIR=/home/${USER}
|
|
|
|
ARG CLAM_AV="no"
|
|
RUN if [ "$CLAM_AV" = "yes" ]; then \
|
|
apt-get update && \
|
|
apt-get --no-install-recommends install -yq \
|
|
clamav \
|
|
libclamunrar && \
|
|
sed -i 's/ReceiveTimeout 30/ReceiveTimeout 300/g' /etc/clamav/freshclam.conf && \
|
|
freshclam && \
|
|
chown -R ${USER}:${USER} /var/lib/clamav && \
|
|
rm -rf /var/lib/apt/lists/*; \
|
|
fi
|
|
|
|
# Install wheels from the build image
|
|
RUN python3 -m venv /opt/venv
|
|
ENV PATH="/opt/venv/bin:${PATH}"
|
|
ARG PIP_DISABLE_PIP_VERSION_CHECK=1
|
|
|
|
RUN --mount=type=bind,from=build-image,source=/tmp/wheelhouse,target=/mnt/wheelhouse \
|
|
--mount=type=bind,from=build-image-av,source=/tmp/wheelhouse,target=/mnt/wheelhouse-av \
|
|
python -m pip install --no-index /mnt/wheelhouse/*.whl /mnt/wheelhouse-av/*.whl
|
|
|
|
ENV NUMPROCS=1
|
|
COPY --from=build-image-av /opt/ffmpeg/lib /usr/lib
|
|
|
|
# These variables are required for supervisord substitutions in files
|
|
# This library allows remote python debugging with VS Code
|
|
ARG CVAT_DEBUG_ENABLED
|
|
RUN if [ "${CVAT_DEBUG_ENABLED}" = 'yes' ]; then \
|
|
python3 -m pip install --no-cache-dir debugpy; \
|
|
fi
|
|
|
|
# Removing pip due to security reasons. See: https://scout.docker.com/vulnerabilities/id/CVE-2018-20225
|
|
# The vulnerability is dubious and we don't use pip at runtime, but some vulnerability scanners mark it as a high vulnerability,
|
|
# and it was decided to remove pip from the final image
|
|
RUN python -m pip uninstall -y pip
|
|
|
|
# Install and initialize CVAT, copy all necessary files
|
|
COPY cvat/nginx.conf /etc/nginx/nginx.conf
|
|
COPY --parents \
|
|
backend_entrypoint.d cvat supervisord utils \
|
|
backend_entrypoint.sh \
|
|
components/analytics/clickhouse/init.py \
|
|
manage.py \
|
|
rqscheduler.py \
|
|
wait_for_deps.sh \
|
|
/opt/cvat/
|
|
|
|
RUN python -m compileall -q /opt/cvat
|
|
|
|
# Link manage.py to the home directory for backwards compatibility.
|
|
RUN ln -s /opt/cvat/manage.py ${CVAT_BASE_DIR}/manage.py
|
|
|
|
RUN echo "/opt/cvat" > /opt/venv/lib/python3.12/site-packages/cvat.pth
|
|
|
|
ARG COVERAGE_PROCESS_START
|
|
RUN if [ "${COVERAGE_PROCESS_START}" ]; then \
|
|
echo "import coverage; coverage.process_startup()" > /opt/venv/lib/python3.12/site-packages/coverage_subprocess.pth; \
|
|
fi
|
|
|
|
# RUN all commands below as 'django' user.
|
|
# Use numeric UID/GID so that the image is compatible with the Kubernetes runAsNonRoot setting.
|
|
USER 1000:1000
|
|
WORKDIR ${CVAT_BASE_DIR}
|
|
|
|
RUN mkdir -p data share keys logs /tmp/supervisord /tmp/cvat static
|
|
|
|
EXPOSE 8080
|
|
ENTRYPOINT ["/opt/cvat/backend_entrypoint.sh"]
|