From 9cb0037b67c849327b76325cb62c66b050a06921 Mon Sep 17 00:00:00 2001 From: bbingz Date: Sun, 31 May 2026 06:57:51 +0800 Subject: [PATCH] fix: harden docker deployment defaults --- Dockerfile | 10 ++++++---- docker/Dockerfile.core | 11 +++++++---- docker/Dockerfile.nginx | 10 ++++++++-- docker/README.md | 20 ++++++++++++-------- docker/cluster/docker-compose.yml | 20 ++++++++------------ docker/mysql/docker-compose.yml | 24 +++++++++++------------- docker/nginx.conf | 12 +++++++++--- docker/postgres/docker-compose.yml | 23 ++++++++++------------- 8 files changed, 71 insertions(+), 59 deletions(-) diff --git a/Dockerfile b/Dockerfile index 48c0200ef..ced8f8e9b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -FROM mcr.microsoft.com/dotnet/core/aspnet:8.0-buster-slim AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble-chiseled-extra@sha256:a6df1767e8363f13963cc5da5c24d0403b630f66acbf5fd6bc414269e446c8fb AS base WORKDIR /app EXPOSE 8080 EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/core/sdk:8.0-buster AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0-noble@sha256:6b0b7f73dc7cce85fe9eaf7cfcfd1dc109accc5b3782c8cba006fbe036da424e AS build WORKDIR /src COPY ["src/SSCMS.Web/SSCMS.Web.csproj", "src/SSCMS.Web/"] COPY ["src/SSCMS.Core/SSCMS.Core.csproj", "src/SSCMS.Core/"] @@ -23,7 +23,9 @@ RUN echo `date +%Y-%m-%d-%H-%M-%S` > /app/sscms/_wwwroot/sitefiles/version.txt FROM base AS final WORKDIR /app -COPY --from=publish /app/sscms . +COPY --from=publish --chown=1654:1654 /app/sscms . +USER 1654 +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 CMD ["dotnet", "--list-runtimes"] ENTRYPOINT ["dotnet", "SSCMS.Web.dll"] -# docker build -t sscms/core:dev . \ No newline at end of file +# docker build -t sscms/core:dev . diff --git a/docker/Dockerfile.core b/docker/Dockerfile.core index 053e9c9fc..91b6ac991 100644 --- a/docker/Dockerfile.core +++ b/docker/Dockerfile.core @@ -1,9 +1,10 @@ -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble-chiseled-extra@sha256:a6df1767e8363f13963cc5da5c24d0403b630f66acbf5fd6bc414269e446c8fb AS base WORKDIR /app +EXPOSE 8080 EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0-noble@sha256:6b0b7f73dc7cce85fe9eaf7cfcfd1dc109accc5b3782c8cba006fbe036da424e AS build WORKDIR /sscms RUN wget https://dl.sscms.com/cms/7.4.0/sscms-7.4.0-linux-x64.tar.gz RUN tar -xzf sscms-7.4.0-linux-x64.tar.gz @@ -13,7 +14,9 @@ RUN echo `date +%Y-%m-%d-%H-%M-%S` > /sscms/_wwwroot/sitefiles/version.txt FROM base AS final WORKDIR /app -COPY --from=build /sscms . +COPY --from=build --chown=1654:1654 /sscms . +USER 1654 +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 CMD ["dotnet", "--list-runtimes"] ENTRYPOINT ["dotnet", "SSCMS.Web.dll"] -# docker build -f Dockerfile.core --no-cache -t sscms/core:latest -t sscms/core:7.4.0 . \ No newline at end of file +# docker build -f Dockerfile.core --no-cache -t sscms/core:7.4.0 . diff --git a/docker/Dockerfile.nginx b/docker/Dockerfile.nginx index e9f01b2b3..0a2f80cc1 100644 --- a/docker/Dockerfile.nginx +++ b/docker/Dockerfile.nginx @@ -1,5 +1,11 @@ -FROM nginx:latest +FROM nginx:stable-alpine-slim@sha256:2c605dbeab79a6b2a63340474fe58119d0ef95bdc4b1f41df0aa689659b3d13b COPY nginx.conf /etc/nginx/nginx.conf -# docker build -f Dockerfile.nginx -t sscms/nginx . \ No newline at end of file +RUN chown -R nginx:nginx /var/cache/nginx /var/run + +USER nginx +EXPOSE 8080 +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD ["nginx", "-t"] + +# docker build -f docker/Dockerfile.nginx -t sscms/nginx:7.4.0 docker diff --git a/docker/README.md b/docker/README.md index ec7973c0c..f0fcf2276 100644 --- a/docker/README.md +++ b/docker/README.md @@ -7,7 +7,7 @@ SSCMS 官方镜像,跟随 SSCMS 版本同步更新。 拉取最新版本的 [SSCMS 镜像](https://hub.docker.com/r/sscms/core),运行命令: ``` bash -docker pull sscms/core:latest +docker pull sscms/core:7.4.0 ``` 如果需要获取指定版本的 [SSCMS 镜像](https://hub.docker.com/r/sscms/core),可以运行命令: @@ -27,19 +27,21 @@ mkdir wwwroot 接下来,我们使用 SQLite 本地数据库运行 SSCMS: ```bash +export SSCMS_SECURITY_KEY="$(uuidgen)" + docker run -d \ --name my-sscms \ - -p 80:80 \ + -p 80:8080 \ --restart=always \ -v "$(pwd)"/wwwroot:/app/wwwroot \ - -e SSCMS_SECURITY_KEY=e2a3d303-ac9b-41ff-9154-930710af0845 \ + -e SSCMS_SECURITY_KEY="$SSCMS_SECURITY_KEY" \ -e SSCMS_DATABASE_TYPE=SQLite \ - sscms/core:latest + sscms/core:7.4.0 ``` - `-d` 参数让容器以后台任务形式运行 - `-name` 参数将容器实例命名为 my-sscms,可以更换为其他名称 -- `-p` 参数映射容器的80端口到宿主机的80端口,如果希望使用8080端口访问可以设置 `-p 8080:80` +- `-p` 参数映射容器的8080端口到宿主机的80端口,如果希望使用8080端口访问可以设置 `-p 8080:8080` - `-restart` 参数使得容器能够自动重启,必须使用 `always` 选项,否则容器将无法安装及升级插件 - `-v` 参数将当前文件夹下的 `wwwroot` 目录作为网站跟目录,从而保存 SSCMS 站点数据,其中 `$(pwd)` 代表当前文件夹 - `-e` 参数设置容器运行环境变量,SSCMS 系统将读取环境变量,作为容器运行的参数,在此我们设置 `SecurityKey` 为随机的 GUID 值,数据库类型为 SQLite @@ -50,14 +52,16 @@ docker run -d \ 除了将当前文件夹下的 `wwwroot` 目录作为站点根目录存储数据,我们也可以将镜像数据持久化存储在 Volume 中: ```bash +export SSCMS_SECURITY_KEY="$(uuidgen)" + docker run -d \ --name my-sscms \ - -p 80:80 \ + -p 80:8080 \ --restart=always \ -v volume-sscms:/app/wwwroot \ - -e SSCMS_SECURITY_KEY=e2a3d303-ac9b-41ff-9154-930710af0845 \ + -e SSCMS_SECURITY_KEY="$SSCMS_SECURITY_KEY" \ -e SSCMS_DATABASE_TYPE=SQLite \ - sscms/core:latest + sscms/core:7.4.0 ``` 此命令将自动创建名称为 `volume-sscms` 的 Docker Volume。 diff --git a/docker/cluster/docker-compose.yml b/docker/cluster/docker-compose.yml index 6152af192..311cb51e8 100644 --- a/docker/cluster/docker-compose.yml +++ b/docker/cluster/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.7" - volumes: volume-sscms: @@ -8,29 +6,27 @@ volumes: services: reverse-proxy: - image: "sscms/nginx" + image: "sscms/nginx:7.4.0" ports: - - "80:80" - - "443:443" + - "80:8080" restart: always cache: - image: "redis" - ports: - - "6379:6379" + image: "redis:8-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1" + command: ["redis-server", "--requirepass", "${SSCMS_REDIS_PASSWORD:?set SSCMS_REDIS_PASSWORD}"] restart: always sscms: depends_on: - reverse-proxy - cache - image: "sscms/core" + image: "sscms/core:7.4.0" expose: - - "80" + - "8080" environment: - SSCMS_SECURITY_KEY: e2a3d303-ac9b-41ff-9154-930710af0845 + SSCMS_SECURITY_KEY: ${SSCMS_SECURITY_KEY:?set SSCMS_SECURITY_KEY} SSCMS_DATABASE_TYPE: SQLite - SSCMS_REDIS_HOST: redis://cache + SSCMS_REDIS_HOST: redis://:${SSCMS_REDIS_PASSWORD:?set SSCMS_REDIS_PASSWORD}@cache:6379 volumes: - volume-sscms:/app/wwwroot restart: always diff --git a/docker/mysql/docker-compose.yml b/docker/mysql/docker-compose.yml index 09c9a0895..7d7f2b2f5 100644 --- a/docker/mysql/docker-compose.yml +++ b/docker/mysql/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.7" - volumes: volume-mysql: @@ -11,31 +9,31 @@ volumes: services: sscms-mysql: - image: mysql + image: mysql:8.4@sha256:c36050afdca850f23cef85703f84c7531a5ae155a11b5ee1c60acb09937c4084 command: --default-authentication-plugin=mysql_native_password restart: always - ports: - - "3306:3306" environment: - MYSQL_ROOT_PASSWORD: mysql-password - MYSQL_DATABASE: mysql-db + MYSQL_ROOT_PASSWORD: ${SSCMS_MYSQL_ROOT_PASSWORD:?set SSCMS_MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: sscms + MYSQL_USER: sscms + MYSQL_PASSWORD: ${SSCMS_DATABASE_PASSWORD:?set SSCMS_DATABASE_PASSWORD} volumes: - volume-mysql:/var/lib/mysql sscms-core: depends_on: - sscms-mysql - image: "sscms/core" + image: "sscms/core:7.4.0" restart: always ports: - - "80:80" + - "80:8080" environment: - SSCMS_SECURITY_KEY: e2a3d303-ac9b-41ff-9154-930710af0845 + SSCMS_SECURITY_KEY: ${SSCMS_SECURITY_KEY:?set SSCMS_SECURITY_KEY} SSCMS_DATABASE_TYPE: MySQL SSCMS_DATABASE_HOST: sscms-mysql - SSCMS_DATABASE_USER: root - SSCMS_DATABASE_PASSWORD: mysql-password - SSCMS_DATABASE_NAME: mysql-db + SSCMS_DATABASE_USER: sscms + SSCMS_DATABASE_PASSWORD: ${SSCMS_DATABASE_PASSWORD:?set SSCMS_DATABASE_PASSWORD} + SSCMS_DATABASE_NAME: sscms volumes: - volume-sscms:/app/wwwroot diff --git a/docker/nginx.conf b/docker/nginx.conf index 5ef4eb24f..e7dc6fe64 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -1,4 +1,5 @@ worker_processes 1; +pid /tmp/nginx.pid; events { worker_connections 1024; } @@ -6,13 +7,18 @@ http { sendfile on; client_max_body_size 500m; + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; upstream sscms { - server sscms; + server sscms:8080; } server { - listen 80; + listen 8080; server_name _; location / { @@ -29,4 +35,4 @@ http { proxy_set_header X-Forwarded-Host $server_name; } } -} \ No newline at end of file +} diff --git a/docker/postgres/docker-compose.yml b/docker/postgres/docker-compose.yml index b3e5b2e1c..9e5e7576d 100644 --- a/docker/postgres/docker-compose.yml +++ b/docker/postgres/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.7" - volumes: volume-postgres: @@ -11,30 +9,29 @@ volumes: services: sscms-postgres: - image: postgres + image: postgres:17-alpine@sha256:979c4379dd698aba0b890599a6104e082035f98ef31d9b9291ec22f2b13059ca restart: always - ports: - - "5432:5432" environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres-password + POSTGRES_USER: sscms + POSTGRES_PASSWORD: ${SSCMS_DATABASE_PASSWORD:?set SSCMS_DATABASE_PASSWORD} + POSTGRES_DB: sscms volumes: - volume-postgres:/var/lib/postgresql/data sscms-core: depends_on: - sscms-postgres - image: "sscms/core" + image: "sscms/core:7.4.0" restart: always ports: - - "80:80" + - "80:8080" environment: - SSCMS_SECURITY_KEY: e2a3d303-ac9b-41ff-9154-930710af0845 + SSCMS_SECURITY_KEY: ${SSCMS_SECURITY_KEY:?set SSCMS_SECURITY_KEY} SSCMS_DATABASE_TYPE: PostgreSQL SSCMS_DATABASE_HOST: sscms-postgres - SSCMS_DATABASE_USER: postgres - SSCMS_DATABASE_PASSWORD: postgres-password - SSCMS_DATABASE_NAME: postgres + SSCMS_DATABASE_USER: sscms + SSCMS_DATABASE_PASSWORD: ${SSCMS_DATABASE_PASSWORD:?set SSCMS_DATABASE_PASSWORD} + SSCMS_DATABASE_NAME: sscms volumes: - volume-sscms:/app/wwwroot