Files
mdm-ordinis/ordinis-app/Dockerfile
T
Zimin A.N. 7abff8f73f fix(security): CSO findings — non-root containers + .env gitignore + actuator hardening
Три прямых фикса по /cso security audit на v2.45.2:

## Finding #1 (HIGH): Backend containers run as root

ordinis-app / ordinis-read-api / ordinis-projection-writer Dockerfile'ы
запускали JVM от root (eclipse-temurin:21-jre default'но не задаёт USER).
Compromise через Spring CVE / Jackson deserialization → root-в-контейнере
= escalation surface на ноду через любой pod misconfig.

Добавлено: `RUN groupadd -r -g 10001 app && useradd -r -u 10001 -g app
-s /sbin/nologin app` + `USER app`. UID 10001 — outside system (0-999)
и distribution reserved (1000-9999). `--chown=app:app` на COPY чтобы
JVM могла прочитать jar.

ordinis-migrations (USER liquibase) и docs (nginx default) уже non-root.

## Finding #2 (HIGH): .env / .env.local not in .gitignore

`.gitignore` блокировал *.pem, *.key, .gstack/, .claude/ — но не .env.
Развработчик с realный dotenv для local dev мог случайно `git add .` и
закоммитить creds. Сейчас нет committed .env (verified) но защита нулевая.

Добавлено: `.env`, `.env.local`, `.env.*.local`, `*.env` + whitelist
`!.env.example`, `!.env.template`, `!.env.sample` для шаблонов.

Существующий tracked `ordinis-admin-ui/.env.example` остался tracked
(verified `git ls-files | grep .env.example`).

## Finding #3 (MEDIUM): Spring Actuator permitAll() — defense-in-depth gap

SecurityConfig had `requestMatchers("/actuator/**").permitAll()` →
любой pod в кластере мог `curl ordinis-app:8080/actuator/env` и
получить ORDINIS_KEYCLOAK_ADMIN_CLIENT_SECRET, DB creds и пр.

VERIFIED: nginx ingress на проде НЕ проксирует /actuator/* (SPA fallback
отдаёт index.html), так что не exposed на интернет. Но pod-to-pod в
кластере = открыто.

Defense in two layers:

1. SecurityConfig: split actuator routes —
     /actuator/health/**, /actuator/info, /actuator/prometheus → permitAll
     /actuator/**                                              → denyAll
   Probes/Prometheus scrape работают, остальное блок.

2. application.yml для всех трёх Spring apps: narrowed default exposure
   to "health,info,prometheus". Поддерживается env override
   MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE для staging diagnostic
   (включить env/configprops/loggers только когда нужно дебажить
   autoconfig).

## Тесты

- mvn package — все 15 модулей SUCCESS
- ordinis-rest-api + ordinis-auth tests — SUCCESS

## Manual verify post-deploy

После пророллится v2.45.3:
- staging+prod liveness/readiness probes должны остаться зелёными
  (/actuator/health/{liveness,readiness} в permitAll list).
- Prometheus scrape /actuator/prometheus → 200.
- ssh pod && curl localhost:8080/actuator/env → должен вернуть
  401/403 (или 404 если endpoint вообще выключен через exposure).

## Откат

Если probes сломаются (маловероятно — health endpoints whitelisted) —
revert этого MR. Image rollback к v2.45.2 через helm.
2026-05-27 19:02:34 +03:00

44 lines
2.4 KiB
Docker
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Multi-stage build для ordinis-app.
#
# COPY . ./ + root .dockerignore: один rule вместо explicit per-module
# list. Когда добавляется новый Maven module в parent pom.xml — Docker
# build автоматически его подхватит, без правки этого файла. Lesson из
# Phase A (notifications module): explicit COPY list пропускал новый
# module → `mvn -am package` падал на parent pom validation.
#
# .dockerignore исключает frontend (admin-ui), bench, docs, target/, .git/,
# IDE metadata — backend image остаётся компактным.
FROM repo.nstart.cloud/library/maven:3.9.9-eclipse-temurin-21 AS build
# Git info из CI environment (`.gitlab-ci.yml` `--build-arg`) — нужен для
# Spring Boot build-info goal через ordinis-app/pom.xml. Внутри Docker
# context .git/ исключён (.dockerignore), поэтому git-commit-id-maven-plugin
# не может прочитать локально. Defaults в pom.xml gracefully fallback'ят
# если ARG'и не переданы (local docker build без ENV).
ARG GIT_COMMIT=unknown
ARG GIT_BRANCH=unknown
# GIT_TAG defaults to "none" (не пусто): Maven property substitution трактует
# empty value как null → Spring Boot build-info goal валится с
# "Additional property 'tag' is illegal as its value is null".
ARG GIT_TAG=none
ARG GIT_TIME=unknown
WORKDIR /build
COPY . ./
RUN mvn -B -pl ordinis-app -am package -DskipTests \
-Dgit.commit.id.abbrev="$GIT_COMMIT" \
-Dgit.branch="$GIT_BRANCH" \
-Dgit.tags="$GIT_TAG" \
-Dgit.commit.time="$GIT_TIME"
FROM repo.nstart.cloud/library/eclipse-temurin:21-jre
# Non-root user — security hardening (CSO report 2026-05-27 finding #1).
# Eclipse-temurin runtime image не задаёт USER by default → JVM от root.
# Compromise через Spring Boot CVE / Jackson deserialization без USER
# становится root-в-контейнере → escalation surface на ноду. UID 10001
# не конфликтует с system users (0-999) и distribution-reserved (1000-9999).
RUN groupadd -r -g 10001 app && useradd -r -u 10001 -g app -s /sbin/nologin app
WORKDIR /app
COPY --from=build --chown=app:app /build/ordinis-app/target/*.jar /app/app.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseZGC", "-XX:MaxRAMPercentage=75", "-jar", "/app/app.jar"]