# Using Linux Capabilities (--cap-add and --cap-drop) for Container Security

> Secure your containers with Linux capabilities. Learn to use --cap-add and --cap-drop for fine-grained privilege management and enforce least-privilege workloads with Apple Container.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-07-04

---

**Apple Container implements Linux capabilities as a fine-grained security mechanism that lets you add or drop specific privileges using `--cap-add` and `--cap-drop` flags to enforce least-privilege container workloads.**

The `apple/container` repository provides a Swift-based container runtime that uses Linux capabilities to restrict which privileged operations a container process can perform. Instead of running containers with full root privileges or completely unprivileged, you can tailor the exact kernel capabilities available to your workload. This approach minimizes the attack surface by ensuring containers possess only the specific privileges they require to function.

## How Linux Capabilities Work in Apple Container

Apple Container processes capability configurations through a three-stage pipeline that transforms user input into kernel-enforced restrictions. When you specify `--cap-add` or `--cap-drop` on the command line, the system validates, normalizes, and applies these settings before the container process starts.

### Parsing CLI Input in Flags.swift

The command-line parser extracts capability arguments from the user input and stores them in the `Flags.Management` structure. In [`Sources/Services/ContainerAPIService/Client/Flags.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Flags.swift), lines 31-44 define the `capAdd` and `capDrop` properties that capture the string arrays passed via CLI flags.

```swift
let flags = Flags.Management(
    arch: "x86_64",
    capAdd: ["NET_ADMIN", "SYS_TIME"],
    capDrop: ["MKNOD"]
)

```

### Validation and Normalization in Parser.swift

Before reaching the Linux kernel, capability strings undergo strict validation in [`Sources/Services/ContainerAPIService/Client/Parser.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Parser.swift). The `capabilities()` function (lines 21-55) performs three critical operations:

- Converts all input to uppercase
- Validates against the known `CapabilityName` set from the containerization library
- Normalizes strings to the canonical `CAP_` prefix format (accepting both `NET_ADMIN` and `CAP_NET_ADMIN`)

The parser also handles the special `ALL` keyword, which represents the complete capability set.

### Runtime Enforcement

The normalized capability lists are passed to `container-runtime-linux`, which invokes the Linux kernel APIs (`prctl` and `capset`) to configure the process capability sets. Any attempt by the container to exercise a dropped capability results in an `EPERM` (Operation not permitted) error from the kernel.

## Default Capability Sets and Security Best Practices

Apple Container ships with a default whitelist of capabilities that include the minimal privileges needed for most workloads (such as `CAP_NET_RAW` and `CAP_SETUID`). You can view these defaults in the user documentation or by inspecting `Flags.Management` in the source code.

Key security considerations include:

- **Least-privilege principle**: Start with the default set and only add capabilities that a specific workload truly needs, such as `CAP_NET_ADMIN` for low-level networking operations.
- **Complete capability dropping**: Use `--cap-drop ALL` to remove every capability, then selectively re-add only required privileges using `--cap-add`.
- **Processing order**: Drops are processed before adds, meaning `--cap-drop ALL --cap-add ALL` results in a container with all capabilities granted.
- **Input flexibility**: The parser accepts both `CAP_` prefixed and bare capability names, handling case insensitivity automatically.

## Practical Examples for Container Security

### Granting Specific Capabilities

To grant a single additional capability for network administration tasks:

```bash
container run --cap-add NET_ADMIN alpine ip link set lo down

```

To grant all capabilities (use with extreme caution):

```bash
container run --cap-add ALL alpine sh -c "ip link set lo down && echo ok"

```

### Dropping All Capabilities

For maximum security, drop all capabilities and restore only those required for setuid programs:

```bash
container run \
  --cap-drop ALL \
  --cap-add SETUID \
  --cap-add SETGID \
  alpine id

```

### Combining Add and Drop Operations

When you need to grant all capabilities except specific dangerous ones:

```bash
container run --cap-add ALL --cap-drop NET_ADMIN alpine sh

```

To drop a default capability and add a different one:

```bash
container run --cap-drop MKNOD --cap-add SYS_ADMIN alpine sh

```

Note that adds are applied after drops, so the final capability set depends on the order of operations.

## Implementation Details for Developers

When integrating capability management into Swift applications using the Apple Container client library, explicitly validate flags before execution:

```swift
let flags = Flags.Management(
    arch: "x86_64",
    capAdd: ["NET_ADMIN", "SYS_TIME"],
    capDrop: ["MKNOD"]
)

try flags.validate()
let (add, drop) = try Parser.capabilities(capAdd: flags.capAdd, capDrop: flags.capDrop)

// add and drop now contain normalised strings like ["CAP_NET_ADMIN", "CAP_SYS_TIME"]
// These are sent to the container-runtime via the API.

```

### Key Source Files

| File | Role |
|------|------|
| [`Sources/Services/ContainerAPIService/Client/Flags.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Flags.swift) | Defines `--cap-add` and `--cap-drop` CLI options in `Flags.Management` |
| [`Sources/Services/ContainerAPIService/Client/Parser.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Parser.swift) | Validates and normalizes capability strings via the `capabilities()` function |
| [`Tests/IntegrationTests/Run/TestCLIRunCapabilities.swift`](https://github.com/apple/container/blob/main/Tests/IntegrationTests/Run/TestCLIRunCapabilities.swift) | Integration tests verifying add/drop combinations and parser behavior |
| [`docs/how-to.md`](https://github.com/apple/container/blob/main/docs/how-to.md) | User-facing documentation of default capability sets (lines 70-101) |
| [`docs/command-reference.md`](https://github.com/apple/container/blob/main/docs/command-reference.md) | Reference documentation for capability flags |

## Summary

- Apple Container uses `--cap-add` and `--cap-drop` to implement fine-grained privilege control through Linux capabilities.
- The system normalizes capability names in [`Parser.swift`](https://github.com/apple/container/blob/main/Parser.swift) and validates them against known capability sets before passing them to the Linux kernel.
- Security best practices include starting with `--cap-drop ALL` and selectively adding only required capabilities, remembering that add operations are processed after drops.
- Implementation spans [`Flags.swift`](https://github.com/apple/container/blob/main/Flags.swift) for CLI parsing, [`Parser.swift`](https://github.com/apple/container/blob/main/Parser.swift) for validation, and the container runtime for kernel enforcement via `prctl` and `capset`.

## Frequently Asked Questions

### What is the difference between --cap-add and --cap-drop?

**`--cap-add` grants additional Linux capabilities to a container beyond the default set, while `--cap-drop` removes capabilities from the default set.** When both flags are used together, drops are processed first, followed by adds. For example, `--cap-drop ALL --cap-add NET_ADMIN` results in a container with only the `CAP_NET_ADMIN` capability.

### Can I use capability names without the CAP_ prefix?

**Yes, Apple Container accepts capability names with or without the `CAP_` prefix, and the parser is case-insensitive.** The `Parser.capabilities()` function in [`Parser.swift`](https://github.com/apple/container/blob/main/Parser.swift) automatically normalizes inputs like `net_admin` or `NET_RAW` to the canonical `CAP_NET_ADMIN` and `CAP_NET_RAW` formats before kernel submission.

### How do I run a completely unprivileged container?

**Use `--cap-drop ALL` to remove every capability from the container process.** If your application requires specific privileges (such as changing user IDs), you can then selectively re-add only those capabilities: `--cap-drop ALL --cap-add SETUID --cap-add SETGID`. This follows the principle of least privilege by explicitly whitelisting only necessary operations.

### Where does Apple Container validate capability strings?

**Validation occurs in [`Sources/Services/ContainerAPIService/Client/Parser.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Parser.swift) within the `capabilities()` function.** This method checks each capability string against the known `CapabilityName` enumeration, converts inputs to uppercase, and ensures only valid kernel capabilities or the special `ALL` keyword are passed to the runtime.