# CL-0009: Security Profile Disabled

**Severity:** HIGH

**References:**
- [OWASP Docker Security Rule #6](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-6-use-linux-security-module-seccomp-apparmor-or-selinux-for-runtime-security)
- CIS Docker Benchmark 5.22 — Ensure that the default seccomp profile is not Disabled
- CIS Docker Benchmark 5.2 — Ensure that, if applicable, an AppArmor Profile is enabled
- CIS Docker Benchmark 5.3 — Ensure that, if applicable, SELinux security options are set

## What it detects

Any service with `seccomp:unconfined`, `apparmor:unconfined`, or `label:disable` in `security_opt`.

This rule only flags *explicitly disabled* profiles. Services that omit `security_opt` entirely are not flagged — the host applies default profiles automatically. Custom profiles (e.g., `seccomp:custom.json`) and SELinux label overrides that don't disable confinement (e.g., `label:user:system_u`) are also not flagged. SELinux `label:type:spc_t` (super-privileged container) is functionally similar to `label:disable` but is currently out of scope for this rule — it requires SELinux-specific knowledge of type transitions to evaluate.

## Why it matters

Linux distributions apply security profiles by default:

- **seccomp**: Docker's default profile ([`profiles/seccomp/default.json`](https://github.com/moby/moby/blob/master/profiles/seccomp/default.json)) blocks dozens of syscalls outright (including `kexec_load`, `bpf`, `clone` with risky flags, `init_module`) and gates many more behind specific capabilities. Disabling it gives the container the kernel's full unrestricted syscall surface — a massively expanded attack surface for kernel exploits.
- **AppArmor**: Docker's default profile (`docker-default`) restricts file paths in `/proc` and `/sys`, blocks ptrace of host processes, and deny-lists `/proc/kcore` and similar. Disabling it removes those mandatory access controls beyond what capabilities alone enforce.
- **SELinux**: On RHEL-family hosts, applies type enforcement via labels (`container_t`) that confine what files and processes a container can access. `label:disable` turns off type-enforcement confinement for the container; the container then runs in the calling user's domain, which on a typical container host is `unconfined_t`.

Explicitly disabling any of these is an intentional weakening of container isolation that should only be done with strong justification.

## Fix

Remove the unconfined profile — Docker's defaults apply automatically:

```yaml
# Before
services:
  web:
    image: nginx:1.27-alpine
    security_opt:
      - seccomp:unconfined

# After
services:
  web:
    image: nginx:1.27-alpine
    security_opt:
      - no-new-privileges:true
```

If your application requires specific syscalls blocked by the default profile, create a custom seccomp profile rather than disabling it entirely:

```yaml
security_opt:
  - seccomp:custom-seccomp.json
```

## When to suppress

- A debugger or tracer that genuinely needs `ptrace` of arbitrary syscalls — narrow to a custom profile if at all possible, and never run it on a production network.
- Sandboxes that need to run their own seccomp profile (browsers, language runtimes) — `seccomp:unconfined` is sometimes the documented way to let the inner sandbox install its filter. Suppress only when verified.

## See also

- [CL-0002](CL-0002.md) — `privileged: true` (drops the default seccomp and AppArmor profiles)
- [CL-0003](CL-0003.md) — `no-new-privileges:true` (keeps the LSM/seccomp gate closed across `execve`)
