Skip to content

Commit 0b92b6c

Browse files
authored
Run Geti Tune container as non-root user (#5045)
1 parent 803682c commit 0b92b6c

File tree

4 files changed

+144
-116
lines changed

4 files changed

+144
-116
lines changed

.github/workflows/build.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
echo "Testing container with tag: $FIRST_TAG for device: $DEVICE"
116116
117117
# Start the container in detached mode
118-
CONTAINER_ID=$(docker run -d --rm -v .:/app/data -p 80:80 geti-tune-${DEVICE}:${FIRST_TAG})
118+
CONTAINER_ID=$(docker run -d --rm -v .:/app/data -p 80:8080 geti-tune-${DEVICE}:${FIRST_TAG})
119119
echo "Started container: $CONTAINER_ID"
120120
121121
# Function to cleanup container on exit

application/docker/Dockerfile

Lines changed: 86 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
# Backend base
55
##############
66
FROM python:3.13-slim@sha256:85dfbf1b566b7addfe645faea9938e81a0a01a83580b0ea05fb23706357d77fb AS backend-base
7+
8+
# uv / uvx
79
COPY --from=docker.io/astral/uv:0.9.7@sha256:ba4857bf2a068e9bc0e64eed8563b065908a4cd6bfb66b531a9c424c8e25e142 /uv /uvx /bin/
810

9-
# Set working directory
1011
WORKDIR /application
1112

12-
# Install required build dependencies
13-
# TODO remove 'git' once all dependencies are available on PyPI
13+
# Build deps (for compiling / Rust-based crates)
14+
# TODO: remove 'git' once all dependencies are on PyPI
1415
RUN apt-get update \
1516
&& apt-get install -y --no-install-recommends \
1617
build-essential=12.12 \
@@ -19,18 +20,40 @@ RUN apt-get update \
1920
&& rm -rf /var/lib/apt/lists/* \
2021
&& apt-get clean
2122

22-
# Install Rust compiler
23+
# Install Rust toolchain
2324
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
2425
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
2526
ENV PATH="/root/.cargo/bin:${PATH}"
2627

27-
# Install dependencies
28+
##########
29+
# CPU Base
30+
##########
31+
FROM backend-base AS cpu-base
32+
33+
RUN --mount=type=cache,target=/root/.cache/uv \
34+
--mount=type=bind,source=backend/uv.lock,target=uv.lock \
35+
--mount=type=bind,source=backend/pyproject.toml,target=pyproject.toml \
36+
uv sync --frozen --no-dev --no-editable --extra mqtt --extra cpu
37+
38+
##########
39+
# GPU Base
40+
##########
41+
FROM backend-base AS gpu-base
42+
2843
RUN --mount=type=cache,target=/root/.cache/uv \
2944
--mount=type=bind,source=backend/uv.lock,target=uv.lock \
3045
--mount=type=bind,source=backend/pyproject.toml,target=pyproject.toml \
31-
uv sync --frozen --no-dev --no-editable --extra mqtt
46+
uv sync --frozen --no-dev --no-editable --extra mqtt --extra cuda
47+
48+
##########
49+
# XPU Base
50+
##########
51+
FROM backend-base AS xpu-base
3252

33-
COPY backend/app ./app
53+
RUN --mount=type=cache,target=/root/.cache/uv \
54+
--mount=type=bind,source=backend/uv.lock,target=uv.lock \
55+
--mount=type=bind,source=backend/pyproject.toml,target=pyproject.toml \
56+
uv sync --frozen --no-dev --no-editable --extra mqtt --extra xpu
3457

3558
##################
3659
# Geti UI packages
@@ -59,24 +82,27 @@ COPY --link ui/rsbuild.config.ts ./
5982
COPY --link ui/src/ src/
6083

6184
ARG PUBLIC_API_BASE_URL=""
62-
ENV PUBLIC_API_BASE_URL ${PUBLIC_API_BASE_URL}
85+
ENV PUBLIC_API_BASE_URL=${PUBLIC_API_BASE_URL}
6386

6487
########
6588
# Web UI
6689
########
67-
FROM web-ui-base as web-ui
90+
FROM web-ui-base AS web-ui
6891
RUN npm run build
6992

70-
######################
71-
# Server (CPU version)
72-
######################
73-
FROM python:3.13-slim@sha256:58c30f5bfaa718b5803a53393190b9c68bd517c44c6c94c1b6c8c172bcfad040 AS geti-tune-cpu
93+
#########################
94+
# Base for CPU, GPU and XPU runtime images
95+
#########################
96+
FROM python:3.13-slim@sha256:85dfbf1b566b7addfe645faea9938e81a0a01a83580b0ea05fb23706357d77fb AS runtime-base
7497

75-
WORKDIR /application
76-
ENV PYTHONPATH=/application
98+
ARG UID=10001
99+
ARG USER_NAME="non-root"
77100

78-
# Install nginx & runtime system dependencies
79-
# TODO remove 'git' once all dependencies are available on PyPI
101+
# Create secure non-root user
102+
RUN useradd -l -u ${UID} ${USER_NAME}
103+
104+
# Common system deps (CPU/GPU/XPU all need these)
105+
# TODO: remove 'git' once all dependencies are available on PyPI
80106
RUN apt-get update \
81107
&& apt-get install -y --no-install-recommends \
82108
git=1:2.47.3-0* \
@@ -87,28 +113,44 @@ RUN apt-get update \
87113
&& rm -rf /var/lib/apt/lists/* \
88114
&& apt-get clean
89115

90-
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
116+
# Nginx config + static UI
117+
COPY docker/nginx.conf /etc/nginx/nginx.conf
91118
COPY --from=web-ui /home/app/web_ui/dist/ /usr/share/nginx/html
92-
COPY --from=backend-base /application /application
119+
120+
# Backend code + uv from backend-base
121+
COPY --link backend/app /application/app
93122
COPY --from=backend-base /bin/uv /bin/uv
94123

95-
# Install Python dependencies for CPU version
96-
RUN --mount=type=cache,target=/root/.cache/uv \
97-
--mount=type=bind,source=backend/uv.lock,target=uv.lock \
98-
--mount=type=bind,source=backend/pyproject.toml,target=pyproject.toml \
99-
uv sync --frozen --no-dev --no-editable --inexact --extra cpu
124+
# Make runtime directories writable for non-root user
125+
RUN mkdir -p /home/${USER_NAME}/.cache/uv /tmp/nginx /tmp/nginx/logs && \
126+
chown -R ${UID}:${UID} \
127+
/application \
128+
/usr/share/nginx/html \
129+
/home/${USER_NAME} \
130+
/tmp/nginx
131+
132+
EXPOSE 8080
133+
134+
WORKDIR /application
135+
ENV PYTHONPATH=/application
136+
137+
USER ${USER_NAME}
100138

101-
EXPOSE 80
139+
CMD ["sh", "-c", "nginx && exec uv run app/main.py;"]
140+
141+
######################
142+
# Server (CPU version)
143+
######################
144+
FROM runtime-base AS geti-tune-cpu
102145

103-
CMD ["sh", "-c", "nginx && exec uv run ./app/main.py;"]
146+
COPY --from=cpu-base --chown=${UID}:${UID} /application/.venv /application/.venv
104147

105148
######################
106149
# Server (GPU version)
107150
######################
108-
FROM python:3.13-slim@sha256:58c30f5bfaa718b5803a53393190b9c68bd517c44c6c94c1b6c8c172bcfad040 AS geti-tune-gpu
151+
FROM runtime-base AS geti-tune-gpu
109152

110-
WORKDIR /application
111-
ENV PYTHONPATH=/application
153+
USER root
112154

113155
RUN apt-get update \
114156
&& apt-get install -y --no-install-recommends wget=1.25.0-2 \
@@ -117,73 +159,38 @@ RUN apt-get update \
117159
&& rm -rf cuda-keyring_1.1-1_all.deb \
118160
&& apt-get remove -y wget
119161

120-
# Install nginx & runtime system dependencies, including the Nvidia drivers.
121-
# Note that CUDA runtime dependencies are shipped through PyTorch, so there's no need to install the CUDA toolkit here.
122-
# TODO remove 'git' once all dependencies are available on PyPI
123-
# hadolint ignore=DL3008
124162
RUN apt-get update \
125163
&& apt-get install -y --no-install-recommends \
126-
git=1:2.47.3-0* \
127-
libgl1=1.7.0-1+b2 \
128-
libglib2.0-0=2.84.4-3~deb13u1 \
129-
libglx-mesa0=25.0.7-2 \
130-
nvidia-open \
131-
nginx=1.26.* \
164+
nvidia-open=580.105.08-1 \
132165
&& rm -rf /var/lib/apt/lists/* \
133166
&& apt-get clean
134167

135-
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
136-
COPY --from=web-ui /home/app/web_ui/dist/ /usr/share/nginx/html
137-
COPY --from=backend-base /application /application
138-
COPY --from=backend-base /bin/uv /bin/uv
139-
140-
# Install Python dependencies for GPU version
141-
RUN --mount=type=cache,target=/root/.cache/uv \
142-
--mount=type=bind,source=backend/uv.lock,target=uv.lock \
143-
--mount=type=bind,source=backend/pyproject.toml,target=pyproject.toml \
144-
uv sync --frozen --no-dev --no-editable --inexact --extra cuda
168+
USER ${USER_NAME}
145169

146-
EXPOSE 80
170+
COPY --from=gpu-base --chown=${UID}:${UID} /application/.venv /application/.venv
147171

148-
CMD ["sh", "-c", "nginx && exec uv run ./app/main.py;"]
172+
#######################
173+
## Server (XPU version)
174+
#######################
175+
FROM runtime-base AS geti-tune-xpu
149176

150-
######################
151-
# Server (XPU version)
152-
######################
153-
FROM python:3.13-slim@sha256:58c30f5bfaa718b5803a53393190b9c68bd517c44c6c94c1b6c8c172bcfad040 AS geti-tune-xpu
177+
USER root
154178

155-
WORKDIR /application
156-
ENV PYTHONPATH=/application
157-
158-
# Install nginx and runtime system dependencies.
179+
# XPU / Intel stack
159180
# Includes Intel graphics drivers (steps from https://dgpu-docs.intel.com/driver/client/overview.html#ubuntu-latest)
160-
# TODO remove 'git' once all dependencies are available on PyPI
161181
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
162-
RUN echo "deb [arch=amd64 trusted=yes] https://ppa.launchpadcontent.net/kobuk-team/intel-graphics/ubuntu noble main" | tee /etc/apt/sources.list.d/kobuk-intel.list
182+
183+
RUN echo "deb [arch=amd64 trusted=yes] https://ppa.launchpadcontent.net/kobuk-team/intel-graphics/ubuntu noble main" \
184+
| tee /etc/apt/sources.list.d/kobuk-intel.list
185+
163186
# hadolint ignore=DL3008
164187
RUN apt-get update \
165188
&& apt-get install -y --no-install-recommends \
166-
git=1:2.47.3-0* \
167-
libgl1=1.7.0-1+b2 \
168-
libglib2.0-0=2.84.4-3~deb13u1 \
169-
libglx-mesa0=25.0.7-2 \
170189
libze-intel-gpu1 libze1 libze-dev intel-metrics-discovery intel-opencl-icd clinfo intel-gsc intel-ocloc \
171190
intel-media-va-driver-non-free libmfx-gen1 libvpl2 libvpl-tools libva-glx2 va-driver-all vainfo \
172-
nginx=1.26.* \
173191
&& rm -rf /var/lib/apt/lists/* \
174192
&& apt-get clean
175193

176-
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
177-
COPY --from=web-ui /home/app/web_ui/dist/ /usr/share/nginx/html
178-
COPY --from=backend-base /application /application
179-
COPY --from=backend-base /bin/uv /bin/uv
180-
181-
# Install Python dependencies for XPU version
182-
RUN --mount=type=cache,target=/root/.cache/uv \
183-
--mount=type=bind,source=backend/uv.lock,target=uv.lock \
184-
--mount=type=bind,source=backend/pyproject.toml,target=pyproject.toml \
185-
uv sync --frozen --no-dev --no-editable --inexact --extra xpu
186-
187-
EXPOSE 80
194+
USER ${USER_NAME}
188195

189-
CMD ["sh", "-c", "nginx && exec uv run ./app/main.py;"]
196+
COPY --from=xpu-base --chown=${UID}:${UID} /application/.venv /application/.venv

application/docker/docker-compose.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ services:
1616
dockerfile: docker/Dockerfile
1717
target: geti-tune-${AI_DEVICE:-cpu}
1818
<<: *proxies
19-
working_dir: /app
19+
working_dir: /application
2020
environment:
2121
- AI_DEVICE=${AI_DEVICE} # choose between 'cpu' (default), 'xpu', 'gpu'
2222
volumes:
23-
- ../backend/data/:/app/data/
24-
- ../backend/logs/:/app/logs/
23+
- ../backend/data/:/application/data/
24+
- ../backend/logs/:/application/logs/
2525
ports:
26-
- "80:80"
26+
- "80:8080"
2727
# Map all host devices to provide access to webcams and other attached devices
2828
privileged: true
2929
devices:

application/docker/nginx.conf

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,61 @@
1-
server {
2-
listen 80;
3-
server_name geti-tune.localhost;
1+
pid /tmp/nginx.pid;
42

5-
root /usr/share/nginx/html;
6-
index index.html;
3+
events {
4+
worker_connections 1024;
5+
}
76

8-
# Enable gzip compression for faster loading
9-
gzip on;
10-
gzip_types text/plain text/css application/json application/javascript;
7+
http {
8+
include /etc/nginx/mime.types;
9+
default_type application/octet-stream;
1110

12-
location / {
13-
# Redirect to index.html if file not found
14-
try_files $uri $uri/ /index.html;
15-
}
11+
# Logging
12+
error_log /tmp/nginx/logs/error.log warn;
13+
access_log /tmp/nginx/logs/access.log;
1614

17-
# Cache static assets
18-
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
19-
expires 1y;
20-
add_header Cache-Control "public, max-age=31536000";
21-
try_files $uri =404;
22-
}
15+
# Temp storage
16+
client_body_temp_path /tmp/nginx/client_temp;
17+
proxy_temp_path /tmp/nginx/proxy_temp;
18+
fastcgi_temp_path /tmp/nginx/fastcgi_temp;
19+
uwsgi_temp_path /tmp/nginx/uwsgi_temp;
20+
scgi_temp_path /tmp/nginx/scgi_temp;
2321

24-
# Don't cache HTML files
25-
location ~* \.html$ {
26-
expires -1;
27-
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
28-
}
22+
server {
23+
listen 8080;
24+
server_name geti-tune.localhost;
25+
root /usr/share/nginx/html;
26+
index index.html;
2927

30-
location /health {
31-
proxy_pass http://localhost:7860/health;
32-
}
33-
location /stream {
34-
proxy_pass http://localhost:7860/stream;
35-
}
36-
location ~ ^/api/ {
37-
proxy_pass http://localhost:7860;
38-
}
28+
gzip on;
29+
gzip_types text/plain text/css application/json application/javascript;
3930

31+
location / {
32+
# Redirect to index.html if file not found
33+
try_files $uri $uri/ /index.html;
34+
}
35+
36+
# Cache static assets
37+
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
38+
expires 1y;
39+
add_header Cache-Control "public, max-age=31536000";
40+
try_files $uri =404;
41+
}
42+
43+
# Don't cache HTML files
44+
location ~* \.html$ {
45+
expires -1;
46+
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
47+
}
48+
49+
location /health {
50+
proxy_pass http://127.0.0.1:7860/health;
51+
}
52+
53+
location /stream {
54+
proxy_pass http://127.0.0.1:7860/stream;
55+
}
56+
57+
location ~ ^/api/ {
58+
proxy_pass http://127.0.0.1:7860;
59+
}
60+
}
4061
}

0 commit comments

Comments
 (0)