# CL-0006: No Capability Restrictions

**Severity:** MEDIUM

**References:**
- [OWASP Docker Security Rule #3](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-3-limit-capabilities-grant-only-specific-capabilities-needed-by-a-container)
- CIS Docker Benchmark 5.4 — Ensure that Linux kernel capabilities are restricted within containers

## What it detects

Any service that does not include `cap_drop: [ALL]` in its configuration.

Dropping only specific capabilities (e.g., `cap_drop: [NET_RAW]`) is not sufficient — the rule requires `ALL` to ensure a least-privilege baseline.

## Why it matters

Docker's default capability set ([`moby/oci/caps/defaults.go`](https://github.com/moby/moby/blob/master/oci/caps/defaults.go)) historically grants 14 capabilities to every container: `AUDIT_WRITE`, `CHOWN`, `DAC_OVERRIDE`, `FOWNER`, `FSETID`, `KILL`, `MKNOD`, `NET_BIND_SERVICE`, `NET_RAW`, `SETFCAP`, `SETGID`, `SETPCAP`, `SETUID`, `SYS_CHROOT`. The set has shifted over Docker versions (e.g., `NET_RAW` is dropped from the default by some distros / engine builds), and the upstream definition is the source of truth.

Highlights of what the defaults grant:

- **NET_RAW** — open raw/`AF_PACKET` sockets; enables ARP spoofing and packet injection on attached networks
- **NET_BIND_SERVICE** — bind to privileged ports (< 1024)
- **SETUID / SETGID** — change process UID/GID; required for `gosu`/`su-exec` user-switch entrypoints
- **CHOWN / DAC_OVERRIDE / FOWNER / FSETID** — bypass file ownership and permission checks
- **MKNOD** — create device nodes via `mknod(2)` (cannot create device nodes the device cgroup denies access to)
- **SETFCAP** — set file capabilities on binaries inside the container

Most containerized applications need none of these. Retaining them expands the attack surface that a compromised container can leverage in chained exploits.

## Fix

Drop all capabilities and add back only the specific ones your application requires:

```yaml
services:
  web:
    image: nginx:1.27-alpine
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE  # only if binding to ports < 1024
```

Common capabilities that specific workloads may need:
- `NET_BIND_SERVICE` — binding to privileged ports (< 1024)
- `CHOWN` / `DAC_OVERRIDE` / `FOWNER` / `SETUID` / `SETGID` — entrypoints that switch users (`gosu`, `su-exec`) or fix volume ownership at startup (postgres, redis)
- `NET_RAW` — `ping` / ICMP from inside the container

## Compatibility

`cap_drop: [ALL]` is incompatible with images whose entrypoints assume default capabilities. Common breakages:

- `gosu` / `su-exec` user-switching entrypoints (postgres, redis, mysql, valkey) — need `SETUID`, `SETGID`, and usually `CHOWN` + `DAC_OVERRIDE` + `FOWNER` for volume initialization.
- `tini` / dumb-init wrappers running as PID 1 with signal forwarding — usually fine, but check.
- Init-style processes that fork helpers (cron, sendmail) — typically need `SETUID` + `SETGID`.

### How to test

Apply `cap_drop: [ALL]`, then `docker compose up`. If the container exits with `Operation not permitted` around `chown`, `setuid`, `setgid`, or `mknod`, add the relevant capability back. Capability profiles for popular images are tracked in [#4](https://github.com/tmatens/compose-lint/issues/4).

## See also

- [CL-0011](CL-0011.md) — dangerous capabilities added back via `cap_add`
- [CL-0002](CL-0002.md) — `privileged: true` (functional superset of all capabilities)
