mirror of
https://github.com/rommapp/romm.git
synced 2025-12-23 19:00:23 +00:00
Merge pull request #2454 from rommapp/secure-docker-image
[ROMM-2432] Secure docker image
This commit is contained in:
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@@ -42,22 +42,22 @@ jobs:
|
||||
run: echo "Triggered by ${{ github.event_name }}"
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v4.3.0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v3.6.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v3.5.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v3.5.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
|
||||
- name: Generate Docker metadata (slim)
|
||||
id: meta-slim
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
with:
|
||||
images: |
|
||||
name=rommapp/romm
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
|
||||
- name: Generate Docker metadata (full)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
with:
|
||||
images: |
|
||||
name=rommapp/romm
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
|
||||
- name: Build slim image
|
||||
id: build-slim
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
file: docker/Dockerfile
|
||||
context: .
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
|
||||
- name: Build full image
|
||||
id: build-full
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
file: docker/Dockerfile
|
||||
context: .
|
||||
|
||||
4
.github/workflows/i18n.yml
vendored
4
.github/workflows/i18n.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v4.3.0
|
||||
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6.0.0
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
|
||||
6
.github/workflows/pytest.yml
vendored
6
.github/workflows/pytest.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v4.3.0
|
||||
|
||||
- name: Install mariadb connectors
|
||||
run: |
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
sudo apt-get install -y libmariadb3 libmariadb-dev
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6.7.0
|
||||
|
||||
- name: Install python
|
||||
run: |
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
uv run pytest -vv --maxfail=10 --junitxml=pytest-report.xml --cov --cov-report xml:coverage.xml --cov-config=.coveragerc .
|
||||
|
||||
- name: Publish test results
|
||||
uses: EnricoMi/publish-unit-test-result-action/linux@v2
|
||||
uses: EnricoMi/publish-unit-test-result-action/linux@sha-3a74b29
|
||||
if: (!cancelled())
|
||||
with:
|
||||
files: |
|
||||
|
||||
12
.github/workflows/test-build.yml
vendored
12
.github/workflows/test-build.yml
vendored
@@ -26,26 +26,26 @@ jobs:
|
||||
run: echo "Triggered by ${{ github.event_name }}"
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v4.3.0
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v3.6.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v3.5.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Generate Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
with:
|
||||
images: |
|
||||
name=rommapp/romm-testing
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
- name: Build full image
|
||||
id: build-full
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
file: docker/Dockerfile
|
||||
context: .
|
||||
|
||||
4
.github/workflows/trunk-check.yml
vendored
4
.github/workflows/trunk-check.yml
vendored
@@ -17,6 +17,6 @@ jobs:
|
||||
contents: read # For repo checkout
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v4.3.0
|
||||
- name: Trunk Check
|
||||
uses: trunk-io/trunk-action@v1
|
||||
uses: trunk-io/trunk-action@v1.2.4
|
||||
|
||||
4
.github/workflows/typecheck.yml
vendored
4
.github/workflows/typecheck.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v4.3.0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5.0.0
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5.0.0
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
|
||||
@@ -11,26 +11,39 @@
|
||||
# - dev-slim: Slim image with development dependencies
|
||||
# - dev-full: Full image with emulator stage and development dependencies
|
||||
|
||||
# Versions:
|
||||
|
||||
# ARGUMENT DECLARATIONS
|
||||
ARG ALPINE_VERSION=3.22
|
||||
ARG NGINX_VERSION=1.29.0
|
||||
ARG NODE_VERSION=20.19
|
||||
ARG ALPINE_SHA256=4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1
|
||||
ARG PYTHON_VERSION=3.13
|
||||
|
||||
# Alias stages:
|
||||
FROM python:${PYTHON_VERSION}-alpine${ALPINE_VERSION} AS python-alias
|
||||
ARG PYTHON_ALPINE_SHA256=9ba6d8cbebf0fb6546ae71f2a1c14f6ffd2fdab83af7fa5669734ef30ad48844
|
||||
ARG NODE_VERSION=20.19
|
||||
ARG NODE_ALPINE_SHA256=eabac870db94f7342d6c33560d6613f188bbcf4bbe1f4eb47d5e2a08e1a37722
|
||||
ARG NGINX_VERSION=1.29.1
|
||||
ARG NGINX_SHA256=42a516af16b852e33b7682d5ef8acbd5d13fe08fecadc7ed98605ba5e3b26ab8
|
||||
ARG UV_VERSION=0.7.19
|
||||
ARG UV_SHA256=9ce16aa2fe33496c439996865dc121371bb33fd5fb37500007d48e2078686b0d
|
||||
|
||||
|
||||
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS frontend-build
|
||||
FROM python:${PYTHON_VERSION}-alpine${ALPINE_VERSION}@sha256:${PYTHON_ALPINE_SHA256} AS python-alias
|
||||
|
||||
|
||||
# FRONTEND BUILD
|
||||
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION}@sha256:${NODE_ALPINE_SHA256} AS frontend-build
|
||||
WORKDIR /front
|
||||
|
||||
COPY ./frontend/package*.json ./
|
||||
RUN npm ci
|
||||
RUN npm ci --ignore-scripts --no-audit --no-fund
|
||||
|
||||
COPY ./frontend ./
|
||||
RUN npm run build
|
||||
|
||||
|
||||
# https://github.com/astral-sh/uv/pkgs/container/uv/452595714
|
||||
FROM ghcr.io/astral-sh/uv:${UV_VERSION}-python${PYTHON_VERSION}-alpine@sha256:${UV_SHA256} AS uv-stage
|
||||
|
||||
|
||||
# BACKEND PYTHON BUILD
|
||||
FROM python-alias AS backend-build
|
||||
|
||||
# git is needed to install streaming-form-data fork
|
||||
@@ -43,14 +56,13 @@ RUN apk add --no-cache \
|
||||
mariadb-connector-c-dev \
|
||||
musl-dev
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.7.19 /uv /uvx /bin/
|
||||
COPY --from=uv-stage /usr/local/bin/uv /usr/local/bin/uvx /bin/
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY ./pyproject.toml ./uv.lock /src/
|
||||
RUN uv sync --locked --no-cache
|
||||
|
||||
|
||||
FROM backend-build AS backend-dev-build
|
||||
|
||||
# linux-headers is needed to install psutil
|
||||
@@ -60,8 +72,8 @@ RUN apk add --no-cache \
|
||||
RUN uv sync --locked --no-cache --all-extras
|
||||
|
||||
|
||||
FROM alpine:${ALPINE_VERSION} AS rahasher-build
|
||||
|
||||
# CUSTOM RAHASHER FOR RETROACHIEVEMENTS
|
||||
FROM alpine:${ALPINE_VERSION}@sha256:${ALPINE_SHA256} AS rahasher-build
|
||||
RUN apk add --no-cache \
|
||||
g++ \
|
||||
git \
|
||||
@@ -84,24 +96,34 @@ RUN git clone --recursive --branch "${RALIBRETRO_VERSION}" --depth 1 https://git
|
||||
make HAVE_CHD=1 -f ./Makefile.RAHasher
|
||||
|
||||
|
||||
FROM alpine:${ALPINE_VERSION} AS emulator-stage
|
||||
# FETCH EMULATORJS AND RUFFLE
|
||||
FROM alpine:${ALPINE_VERSION}@sha256:${ALPINE_SHA256} AS emulator-stage
|
||||
|
||||
RUN apk add --no-cache \
|
||||
7zip \
|
||||
wget
|
||||
wget \
|
||||
ca-certificates
|
||||
|
||||
ARG EMULATORJS_VERSION=4.2.3
|
||||
ARG EMULATORJS_SHA256=07d451bc06fa3ad04ab30d9b94eb63ac34ad0babee52d60357b002bde8f3850b
|
||||
|
||||
RUN wget "https://github.com/EmulatorJS/EmulatorJS/releases/download/v${EMULATORJS_VERSION}/${EMULATORJS_VERSION}.7z" && \
|
||||
echo "${EMULATORJS_SHA256} ${EMULATORJS_VERSION}.7z" | sha256sum -c - && \
|
||||
7z x -y "${EMULATORJS_VERSION}.7z" -o/emulatorjs && \
|
||||
rm -rf "${EMULATORJS_VERSION}.7z";
|
||||
rm -f "${EMULATORJS_VERSION}.7z"
|
||||
|
||||
ARG RUFFLE_VERSION=nightly-2025-08-14
|
||||
ARG RUFFLE_FILE=ruffle-nightly-2025_08_14-web-selfhosted.zip
|
||||
RUN wget "https://github.com/ruffle-rs/ruffle/releases/download/${RUFFLE_VERSION}/${RUFFLE_FILE}" && \
|
||||
unzip -o "${RUFFLE_FILE}" -d /ruffle && \
|
||||
rm -f "${RUFFLE_FILE}";
|
||||
ARG RUFFLE_SHA256=178870c5e7dd825a8df35920dfc5328d83e53f3c4d5d95f70b1ea9cd13494151
|
||||
|
||||
FROM alpine:${ALPINE_VERSION} AS nginx-build
|
||||
RUN wget "https://github.com/ruffle-rs/ruffle/releases/download/${RUFFLE_VERSION}/${RUFFLE_FILE}" && \
|
||||
echo "${RUFFLE_SHA256} ${RUFFLE_FILE}" | sha256sum -c - && \
|
||||
unzip -o "${RUFFLE_FILE}" -d /ruffle && \
|
||||
rm -f "${RUFFLE_FILE}"
|
||||
|
||||
|
||||
# BUILD NGINX MODULE WITH MOD_ZIP
|
||||
FROM alpine:${ALPINE_VERSION}@sha256:${ALPINE_SHA256} AS nginx-build
|
||||
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
@@ -115,14 +137,14 @@ ARG NGINX_VERSION
|
||||
# The specified commit SHA is the latest commit on the `master` branch at the time of writing.
|
||||
# It includes a fix to correctly calculate CRC-32 checksums when using upstream subrequests.
|
||||
# TODO: Move to a tagged release of `mod_zip`, once a version newer than 1.3.0 is released.
|
||||
ARG NGINX_MOD_ZIP_SHA=a9f9afa441117831cc712a832c98408b3f0416f6
|
||||
ARG NGINX_MOD_ZIP_COMMIT=a9f9afa441117831cc712a832c98408b3f0416f6
|
||||
|
||||
# Clone both nginx and `ngx_http_zip_module` repositories, needed to compile the module from source.
|
||||
# Clone both `nginx` and `ngx_http_zip_module` repositories, needed to compile the module from source.
|
||||
# This is needed to be able to dinamically load it as a module in the final image. `nginx` Docker
|
||||
# images do not have a simple way to include third-party modules.
|
||||
RUN git clone https://github.com/evanmiller/mod_zip.git && \
|
||||
cd ./mod_zip && \
|
||||
git checkout "${NGINX_MOD_ZIP_SHA}" && \
|
||||
git checkout "${NGINX_MOD_ZIP_COMMIT}" && \
|
||||
cd ../ && \
|
||||
git clone --branch "release-${NGINX_VERSION}" --depth 1 https://github.com/nginx/nginx.git && \
|
||||
cd ./nginx && \
|
||||
@@ -131,10 +153,10 @@ RUN git clone https://github.com/evanmiller/mod_zip.git && \
|
||||
chmod 644 ./objs/ngx_http_zip_module.so
|
||||
|
||||
|
||||
FROM nginx:${NGINX_VERSION}-alpine${ALPINE_VERSION} AS production-stage
|
||||
# PRODUCTION STAGE
|
||||
FROM nginx:${NGINX_VERSION}-alpine${ALPINE_VERSION}@sha256:${NGINX_SHA256} AS production-stage
|
||||
ARG WEBSERVER_FOLDER=/var/www/html
|
||||
|
||||
# Install required packages and dependencies
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
libmagic \
|
||||
@@ -175,13 +197,15 @@ COPY ./docker/gunicorn/logging.conf /etc/gunicorn/logging.conf
|
||||
# User permissions
|
||||
# - Create default user `romm` (1000) and group `romm` (1000).
|
||||
# - Create base directories and make default user/group the owner.
|
||||
# - Make nginx configuration files writable by everyone, for `envsubst` to work
|
||||
# when a custom UID/GID is used.
|
||||
# - Make nginx configuration files writable by everyone for `envsubst` to work
|
||||
RUN addgroup -g 1000 -S romm && adduser -u 1000 -D -S -G romm romm && \
|
||||
mkdir /romm /redis-data && chown romm:romm /romm /redis-data && \
|
||||
mkdir /romm /redis-data && \
|
||||
chown romm:romm /romm /redis-data && \
|
||||
chmod 755 /romm /redis-data && \
|
||||
chmod -R a+w /etc/nginx/conf.d
|
||||
|
||||
|
||||
# SLIM IMAGE
|
||||
FROM scratch AS slim-image
|
||||
|
||||
COPY --from=production-stage / /
|
||||
@@ -190,10 +214,15 @@ COPY --from=backend-build /src/.venv /src/.venv
|
||||
|
||||
ENV PATH="/src/.venv/bin:${PATH}"
|
||||
|
||||
# Security: Set security-focused environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONPATH=/backend
|
||||
|
||||
# Declare the supported volumes
|
||||
VOLUME ["/romm/resources", "/romm/library", "/romm/assets", "/romm/config", "/redis-data"]
|
||||
|
||||
# Expose ports and start
|
||||
# Expose non-privileged ports
|
||||
EXPOSE 8080 6379/tcp
|
||||
WORKDIR /romm
|
||||
|
||||
@@ -201,6 +230,7 @@ ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["/init"]
|
||||
|
||||
|
||||
# FULL IMAGE
|
||||
FROM slim-image AS full-image
|
||||
ARG WEBSERVER_FOLDER=/var/www/html
|
||||
COPY --from=emulator-stage /emulatorjs ${WEBSERVER_FOLDER}/assets/emulatorjs
|
||||
|
||||
3
frontend/package-lock.json
generated
3
frontend/package-lock.json
generated
@@ -23,6 +23,7 @@
|
||||
"qrcode": "^1.5.4",
|
||||
"semver": "^7.6.2",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"vanilla-tilt": "^1.8.1",
|
||||
"vue": "^3.4.27",
|
||||
"vue-i18n": "^11.1.10",
|
||||
@@ -44,7 +45,6 @@
|
||||
"eslint-plugin-vue": "^9.33.0",
|
||||
"globals": "^16.0.0",
|
||||
"openapi-typescript-codegen": "^0.29.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.42.0",
|
||||
"vite": "^6.3.6",
|
||||
@@ -8374,7 +8374,6 @@
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz",
|
||||
"integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "npm run typecheck && vite build",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"generate": "openapi --input http://127.0.0.1:3000/openapi.json --output ./src/__generated__ --client axios --useOptions --useUnionTypes --exportServices false --exportSchemas false --exportCore false",
|
||||
@@ -42,6 +42,7 @@
|
||||
"qrcode": "^1.5.4",
|
||||
"semver": "^7.6.2",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"vanilla-tilt": "^1.8.1",
|
||||
"vue": "^3.4.27",
|
||||
"vue-i18n": "^11.1.10",
|
||||
@@ -63,7 +64,6 @@
|
||||
"eslint-plugin-vue": "^9.33.0",
|
||||
"globals": "^16.0.0",
|
||||
"openapi-typescript-codegen": "^0.29.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.42.0",
|
||||
"vite": "^6.3.6",
|
||||
|
||||
Reference in New Issue
Block a user