← [Раздел 07](README.md) · [Главная](../README.md)

# Container hardening

## Цель

Научиться **усиливать контейнеры**: минимальные образы, non-root, сканирование, безопасный Dockerfile и runtime constraints — чтобы compromise контейнера не давал root на хосте.

## Предварительно

- [seti-segmentaciya.md](seti-segmentaciya.md).
- Docker basics: image, container, layer.

## Время

~30 минут чтения + audit одного Dockerfile

---

## Threat model контейнера

Контейнер **не VM** — общий kernel хоста. Escape или privileged container → риск для node.

| Угроза | Mitigation |
|--------|------------|
| Vulnerable base image | Minimal base + scan |
| Root in container | USER non-root |
| Writable root FS | readOnlyRootFilesystem |
| Secrets in image layers | Runtime inject only |
| Capability abuse | Drop ALL, add minimal |

## Minimal base images

| Base | Use case |
|------|----------|
| `distroless` (Google) | Runtime only, no shell |
| `alpine` | Small, но musl quirks |
| `scratch` | Static binaries only |
| Full `ubuntu` | Avoid for prod microservices |

Меньше пакетов → меньше CVE surface.

## Dockerfile checklist

```dockerfile
# ✅ Паттерны (концепт)
FROM gcr.io/distroless/nodejs20-debian12

WORKDIR /app
COPY --chown=nonroot:nonroot package*.json ./
# install deps in build stage, copy artifact only — multi-stage

USER nonroot
EXPOSE 8080
ENTRYPOINT ["node", "server.js"]
```

| Rule | Why |
|------|-----|
| Multi-stage build | No build tools in final image |
| Pin base digest | `@sha256:...` not `:latest` |
| No secrets in ARG/ENV | Layers are forever |
| USER non-root | Limit container breakout impact |
| HEALTHCHECK | Orchestrator replaces bad pods |

## Image scanning

Scan на build и registry:

| Tool | When |
|------|------|
| **Trivy** | CI + admission |
| **Grype** | CI |
| **ECR/GCR scanning** | Registry native |

Gate: block deploy on Critical OS/package CVE ([quality gates](../06-bezopasnost-koda/quality-gates-v-ci.md)).

## Runtime security (Kubernetes)

| Setting | Effect |
|---------|--------|
| `runAsNonRoot: true` | Enforce non-zero UID |
| `readOnlyRootFilesystem: true` | tmp via emptyDir |
| `allowPrivilegeEscalation: false` | No setuid escalation |
| `capabilities.drop: ["ALL"]` | Minimal caps |
| `seccompProfile: RuntimeDefault` | Syscall filter |

Pod Security Standards (PSS):

| Level | Profile |
|-------|---------|
| **Privileged** | Unrestricted (avoid) |
| **Baseline** | Minimal restrictions |
| **Restricted** | Hardened default target |

## Privileged containers — almost never

`privileged: true` ≈ root on host capabilities.

Exceptions: CNI plugins, node debug (break-glass only).

## Rootless Docker / Podman

Build and run without daemon root — extra isolation on dev laptops.

## Supply chain for images

| Practice | Benefit |
|----------|---------|
| Sign images (cosign) | Verify before deploy |
| Private registry | Control provenance |
| Only allow approved bases | Golden images |
| SBOM per image | Incident response |

## Secrets in containers

| ❌ | ✅ |
|----|-----|
| `ENV API_KEY=...` in Dockerfile | K8s Secret / external secrets operator |
| Bake `.env` in image | Mount at runtime, tmpfs |
| Same image dev/prod secrets | Config per env at deploy |

## Monitoring runtime

| Signal | Tool class |
|--------|------------|
| Syscall anomalies | Falco, Tetragon |
| Drift from baseline | Audit policy |
| Unexpected outbound | NetworkPolicy + flow logs |

→ [SIEM и алерты](../08-runtime-monitoring/siem-i-alerty.md)

## Hardening checklist

- [ ] Multi-stage Dockerfile, non-root USER
- [ ] Base image pinned by digest
- [ ] Trivy/Grype in CI, critical = fail
- [ ] No `:latest` in prod manifests
- [ ] PSS Restricted or Baseline enforced at namespace
- [ ] readOnlyRootFilesystem where possible
- [ ] Registry scanning enabled
- [ ] Signed images in admission (optional maturity)

## Lab: найдите проблемы

```dockerfile
FROM node:latest
COPY . .
ENV DATABASE_PASSWORD=YOUR_EXAMPLE_PASSWORD
RUN npm install
USER root
CMD ["node", "index.js"]
```

Список issues: latest tag, secrets in ENV, root, full context copy, no multi-stage.

---

## Самопроверка

1. Почему контейнер с root опаснее non-root при escape?
2. Чем distroless лучше ubuntu для prod API?
3. Зачем pin digest вместо tag?
4. Где должны жить secrets — в image или at deploy?

## Дальше

[Kubernetes security](kubernetes-security.md)
