diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfdb8b7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/Dockerfile b/Dockerfile index 2d4a52e..36a3af1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,17 +30,18 @@ RUN dotnet publish backend/src/PnvPanel.Api/PnvPanel.Api.csproj -c Release -o /a # ── Stage 3: runtime ────────────────────────────────────────────────────── FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime WORKDIR /app -# curl — только для HEALTHCHECK в docker-compose (GET /health), в образе его нет по умолчанию. -RUN apt-get update && apt-get install -y --no-install-recommends curl \ +# curl — HEALTHCHECK (GET /health). gosu — безопасный drop-privileges из entrypoint: контейнер +# стартует под root, чтобы почистить владельца смонтированных volume'ов, и тут же передаёт +# управление непривилегированному пользователю (см. docker-entrypoint.sh). +RUN apt-get update && apt-get install -y --no-install-recommends curl gosu \ && rm -rf /var/lib/apt/lists/* ENV ASPNETCORE_ENVIRONMENT=Production \ ASPNETCORE_HTTP_PORTS=8080 EXPOSE 8080 COPY --from=backend /app/publish ./ -# Data Protection key-ring и загрузки тикетов монтируются сюда volume'ами (см. docker-compose.yml). -# Готовим права заранее — Docker скопирует владельца/содержимое в volume при первом маунте (copy-up). -RUN mkdir -p /app/keys /app/uploads && chown -R app:app /app/keys /app/uploads -USER app +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +RUN chmod +x /usr/local/bin/docker-entrypoint.sh && mkdir -p /app/keys /app/uploads HEALTHCHECK --interval=15s --timeout=5s --start-period=20s --retries=5 \ CMD curl -f http://localhost:8080/health || exit 1 -ENTRYPOINT ["dotnet", "PnvPanel.Api.dll"] +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["dotnet", "PnvPanel.Api.dll"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..b523fd4 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +# Именованные volume'ы (/app/keys, /app/uploads) на уже существующих деплоях могли остаться +# root-owned (Docker copy-up переносит владельца из образа только для НОВОЙ пустой volume). +# Контейнер поэтому стартует под root и сам чинит владельца при каждом запуске, только +# после этого передаёт управление непривилегированному app-пользователю через gosu. +chown -R app:app /app/keys /app/uploads + +exec gosu app "$@"