Merge branch 'fix/security-hardening' into 'main'
fix(security): CSO findings #1-3 — non-root containers + .env gitignore + actuator hardening See merge request 2-6/2-6-4/terravault/ordinis!279
This commit is contained in:
+10
@@ -23,3 +23,13 @@ HELP.md
|
|||||||
*.key
|
*.key
|
||||||
.gstack/
|
.gstack/
|
||||||
.claude/
|
.claude/
|
||||||
|
# Local env files — CSO finding #2. dotenv pattern предполагает не-committed
|
||||||
|
# .env с реальными creds для local dev. Без этого правила `git add .` засосёт
|
||||||
|
# secrets. Whitelist .env.example / .env.template / .env.sample — шаблоны.
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
*.env
|
||||||
|
!.env.example
|
||||||
|
!.env.template
|
||||||
|
!.env.sample
|
||||||
|
|||||||
@@ -30,7 +30,14 @@ RUN mvn -B -pl ordinis-app -am package -DskipTests \
|
|||||||
-Dgit.commit.time="$GIT_TIME"
|
-Dgit.commit.time="$GIT_TIME"
|
||||||
|
|
||||||
FROM repo.nstart.cloud/library/eclipse-temurin:21-jre
|
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
|
WORKDIR /app
|
||||||
COPY --from=build /build/ordinis-app/target/*.jar /app/app.jar
|
COPY --from=build --chown=app:app /build/ordinis-app/target/*.jar /app/app.jar
|
||||||
|
USER app
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["java", "-XX:+UseZGC", "-XX:MaxRAMPercentage=75", "-jar", "/app/app.jar"]
|
ENTRYPOINT ["java", "-XX:+UseZGC", "-XX:MaxRAMPercentage=75", "-jar", "/app/app.jar"]
|
||||||
|
|||||||
@@ -12,14 +12,21 @@ management:
|
|||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
exposure:
|
exposure:
|
||||||
# `conditions`, `beans`, `configprops`, `loggers` — diagnostic четвёрка
|
# CSO finding #3 — defense-in-depth. Default exposure narrowed к трём
|
||||||
# для autoconfig debug (например: почему MailSenderAutoConfiguration
|
# безопасным: health (k8s probes), info (UI banner), prometheus
|
||||||
# не fired при наличии SPRING_MAIL_HOST env var и starter-mail на
|
# (ServiceMonitor scrape). Sensitive endpoints (env, configprops,
|
||||||
# classpath). `loggers` runtime-toggle позволяет крутить
|
# beans, mappings, conditions, loggers) выключены by default —
|
||||||
# `org.springframework.boot.autoconfigure` в DEBUG без redeploy.
|
# утечка ORDINIS_KEYCLOAK_ADMIN_CLIENT_SECRET через /actuator/env
|
||||||
# Endpoints sensitive — exposed только на staging behind cluster-internal
|
# потенциальна на любой pod-to-pod атаке.
|
||||||
# Ingress (auth.required=true в prod).
|
#
|
||||||
include: health,info,prometheus,refresh,metrics,env,mappings,conditions,beans,configprops,loggers
|
# Diagnostic четвёрка (conditions/beans/configprops/loggers) +
|
||||||
|
# debug-set (env/mappings/refresh/metrics) включаются через env
|
||||||
|
# override `MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE` на staging
|
||||||
|
# — для autoconfig debug (например: почему
|
||||||
|
# MailSenderAutoConfiguration не fired при наличии SPRING_MAIL_HOST
|
||||||
|
# env var). На prod env override не ставим → defense вместе с
|
||||||
|
# SecurityConfig denyAll на /actuator/{env,beans,...}.
|
||||||
|
include: ${MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE:health,info,prometheus}
|
||||||
base-path: /actuator
|
base-path: /actuator
|
||||||
endpoint:
|
endpoint:
|
||||||
health:
|
health:
|
||||||
|
|||||||
+29
-1
@@ -40,7 +40,35 @@ public class SecurityConfig {
|
|||||||
.csrf(csrf -> csrf.disable())
|
.csrf(csrf -> csrf.disable())
|
||||||
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authorizeHttpRequests(auth -> {
|
.authorizeHttpRequests(auth -> {
|
||||||
auth.requestMatchers("/actuator/**").permitAll();
|
// Actuator split — CSO finding #3.
|
||||||
|
// Раньше /actuator/** = permitAll → /actuator/env публично отдавал
|
||||||
|
// env vars (включая ORDINIS_KEYCLOAK_ADMIN_CLIENT_SECRET) любому
|
||||||
|
// pod'у в кластере. Сейчас защищены только три безопасных:
|
||||||
|
//
|
||||||
|
// - /actuator/health/** — k8s liveness/readiness probes (бессрочно
|
||||||
|
// open, иначе pod не Ready).
|
||||||
|
// - /actuator/info — build info (commit, tag) для UI banner.
|
||||||
|
// - /actuator/prometheus — Prometheus scrape (cluster-internal
|
||||||
|
// ServiceMonitor; в принципе можно гейтить по network policy,
|
||||||
|
// но scrape token convention'а не было).
|
||||||
|
//
|
||||||
|
// Остальные (env, configprops, beans, mappings, conditions,
|
||||||
|
// loggers, refresh, metrics) — denyAll. Если оператор хочет
|
||||||
|
// diagnose в проде — port-forward к pod и `curl localhost:8080`
|
||||||
|
// (внутри pod нет network policy), либо temporarily exposed
|
||||||
|
// через kubectl exec.
|
||||||
|
//
|
||||||
|
// Defense-in-depth: дополнительно values-prod.yaml ограничивает
|
||||||
|
// management.endpoints.web.exposure.include = health,info,prometheus
|
||||||
|
// на уровне Spring → даже если SecurityFilterChain поменяется,
|
||||||
|
// endpoint'ов просто не существует.
|
||||||
|
auth.requestMatchers(
|
||||||
|
"/actuator/health",
|
||||||
|
"/actuator/health/**",
|
||||||
|
"/actuator/info",
|
||||||
|
"/actuator/prometheus"
|
||||||
|
).permitAll();
|
||||||
|
auth.requestMatchers("/actuator/**").denyAll();
|
||||||
auth.requestMatchers("/error").permitAll();
|
auth.requestMatchers("/error").permitAll();
|
||||||
// Build-info endpoint — открыт без auth: UI polling с update-banner
|
// Build-info endpoint — открыт без auth: UI polling с update-banner
|
||||||
// должен работать ДО login (anonymous landing → "новая версия"
|
// должен работать ДО login (anonymous landing → "новая версия"
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ COPY . ./
|
|||||||
RUN mvn -B -pl ordinis-projection-writer -am package -DskipTests
|
RUN mvn -B -pl ordinis-projection-writer -am package -DskipTests
|
||||||
|
|
||||||
FROM repo.nstart.cloud/library/eclipse-temurin:21-jre
|
FROM repo.nstart.cloud/library/eclipse-temurin:21-jre
|
||||||
|
# Non-root user — security hardening (CSO report 2026-05-27 finding #1).
|
||||||
|
# См. ordinis-app/Dockerfile для rationale.
|
||||||
|
RUN groupadd -r -g 10001 app && useradd -r -u 10001 -g app -s /sbin/nologin app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /build/ordinis-projection-writer/target/*.jar /app/app.jar
|
COPY --from=build --chown=app:app /build/ordinis-projection-writer/target/*.jar /app/app.jar
|
||||||
|
USER app
|
||||||
EXPOSE 8082
|
EXPOSE 8082
|
||||||
ENTRYPOINT ["java", "-XX:+UseZGC", "-XX:MaxRAMPercentage=75", "-jar", "/app/app.jar"]
|
ENTRYPOINT ["java", "-XX:+UseZGC", "-XX:MaxRAMPercentage=75", "-jar", "/app/app.jar"]
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ management:
|
|||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
exposure:
|
exposure:
|
||||||
include: health,info,prometheus,refresh,metrics
|
# CSO finding #3 — defense-in-depth. См. ordinis-app/application.yml.
|
||||||
|
include: ${MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE:health,info,prometheus}
|
||||||
endpoint:
|
endpoint:
|
||||||
health:
|
health:
|
||||||
probes:
|
probes:
|
||||||
|
|||||||
@@ -7,7 +7,12 @@ COPY . ./
|
|||||||
RUN mvn -B -pl ordinis-read-api -am package -DskipTests
|
RUN mvn -B -pl ordinis-read-api -am package -DskipTests
|
||||||
|
|
||||||
FROM repo.nstart.cloud/library/eclipse-temurin:21-jre
|
FROM repo.nstart.cloud/library/eclipse-temurin:21-jre
|
||||||
|
# Non-root user — security hardening (CSO report 2026-05-27 finding #1).
|
||||||
|
# См. ordinis-app/Dockerfile для rationale (compromise → root в контейнере
|
||||||
|
# = escalation surface на ноду). UID 10001 outside system+distro reserved.
|
||||||
|
RUN groupadd -r -g 10001 app && useradd -r -u 10001 -g app -s /sbin/nologin app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /build/ordinis-read-api/target/*.jar /app/app.jar
|
COPY --from=build --chown=app:app /build/ordinis-read-api/target/*.jar /app/app.jar
|
||||||
|
USER app
|
||||||
EXPOSE 8081
|
EXPOSE 8081
|
||||||
ENTRYPOINT ["java", "-XX:+UseZGC", "-XX:MaxRAMPercentage=75", "-jar", "/app/app.jar"]
|
ENTRYPOINT ["java", "-XX:+UseZGC", "-XX:MaxRAMPercentage=75", "-jar", "/app/app.jar"]
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ management:
|
|||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
exposure:
|
exposure:
|
||||||
include: health,info,prometheus,refresh,metrics,env,mappings
|
# CSO finding #3 — defense-in-depth. См. ordinis-app/application.yml
|
||||||
|
# для rationale (narrow default, env-override для staging diagnostic).
|
||||||
|
include: ${MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE:health,info,prometheus}
|
||||||
endpoint:
|
endpoint:
|
||||||
health:
|
health:
|
||||||
probes:
|
probes:
|
||||||
|
|||||||
Reference in New Issue
Block a user