# Managing Linux Capabilities and Capability Dropping in Containers

> Master Linux capabilities in containers with apple/container's --cap-add and --cap-drop flags. Fine-tune security with a precise three-step algorithm and optimize your container environment.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: deep-dive
- Published: 2026-06-29

---

**The `container` CLI provides `--cap-add` and `--cap-drop` flags that let you fine-tune Linux capabilities by modifying the default OCI capability set through a three-step algorithm implemented in [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift).**

The `apple/container` repository implements a secure-by-default approach to **managing Linux capabilities and capability dropping in containers** through a deterministic flag-processing system. When you launch a container, the runtime calculates an effective capability set by combining default OCI capabilities with user-specified additions and removals. This calculation happens before the container starts, ensuring the kernel enforces only the permissions you explicitly grant.

## How Capability Flags Work in `container`

The capability management system translates CLI arguments into kernel-enforced permission sets through two main phases: parsing and calculation.

### CLI Parsing in Flags.swift

In [`Sources/Services/ContainerAPIService/Client/Flags.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Flags.swift), the `--cap-add` and `--cap-drop` options are defined as string arrays that accept individual capability names or the special `ALL` sentinel:

```swift
@Option(
    name: .customLong("cap-add"),
    help: .init("Add a Linux capability (e.g. CAP_NET_RAW, or ALL)", valueName: "cap")
)
public var capAdd: [String] = []

@Option(
    name: .customLong("cap-drop"),
    help: .init("Drop a Linux capability (e.g. CAP_NET_RAW, or ALL)", valueName: "cap")
)
public var capDrop: [String] = []

```

These arrays are passed to the runtime service where the effective capability set is computed.

### The Effective Capability Algorithm

The core logic resides in [`Sources/Services/RuntimeLinux/Server/RuntimeService.swift`](https://github.com/apple/container/blob/main/Sources/Services/RuntimeLinux/Server/RuntimeService.swift) within the `effectiveCapabilities` function. This implementation follows Docker-compatible semantics using a three-step ordered process.

## Capability Calculation Logic

The `effectiveCapabilities` function processes capabilities in strict sequence to determine the final set:

1. **Base set initialization** – If `capDrop` contains `"ALL"`, start with an empty set; otherwise inherit the default OCI capabilities (`Containerization.LinuxCapabilities.defaultOCICapabilities`).
2. **Process additions** – If `capAdd` contains `"ALL"`, replace the set with all possible capabilities (`CapabilityName.allCases`). Otherwise validate and insert each specified capability.
3. **Process drops** – Remove every capability listed in `capDrop` (except the `"ALL"` sentinel) from the current set.

```swift
private static func effectiveCapabilities(capAdd: [String],
                                         capDrop: [String]) throws -> Containerization.LinuxCapabilities {
    // 1. Base set
    var caps: Set<CapabilityName>
    if capDrop.contains("ALL") {
        caps = []
    } else {
        caps = Set(Containerization.LinuxCapabilities.defaultOCICapabilities.effective)
    }

    // 2. Process adds
    if capAdd.contains("ALL") {
        caps = Set(CapabilityName.allCases)
    } else {
        for name in capAdd {
            caps.insert(try CapabilityName(rawValue: name))
        }
    }

    // 3. Process individual drops
    for name in capDrop where name != "ALL" {
        caps.remove(try CapabilityName(rawValue: name))
    }

    return Containerization.LinuxCapabilities(capabilities: Array(caps))
}

```

Because steps are processed sequentially, later operations override earlier ones. For example, `--cap-drop ALL --cap-add SETGID` results in a container possessing only `CAP_SETGID`.

## Validation and Enforcement

Capability names undergo strict validation before reaching the kernel.

### Name Validation

The parser validates every capability name against the `CapabilityName` enum from the `Containerization` Swift package. Invalid names like `CHWOWZERS` trigger immediate user-visible errors, which the test suite in [`Tests/CLITests/Subcommands/Run/TestCLIRunCapabilities.swift`](https://github.com/apple/container/blob/main/Tests/CLITests/Subcommands/Run/TestCLIRunCapabilities.swift) verifies.

### Kernel Enforcement

After validation, the runtime passes the computed capability bitmask to the Linux VM. The kernel enforces these restrictions natively inside the container. The test suite confirms this by attempting to execute `mknod` after dropping `CAP_MKNOD`; the operation fails with a permission error, demonstrating that the capability drop was effective at the kernel level.

## Practical Examples

Use these patterns to configure container capabilities for common scenarios:

Add a single capability to enable network raw sockets:

```bash
container run --cap-add NET_RAW alpine sh -c 'ping -c 1 8.8.8.8'

```

Drop a specific capability to prevent device node creation:

```bash
container run --cap-drop MKNOD alpine sh -c 'mknod /tmp/sda b 8 0'

```

Start from a clean slate and add only required capabilities:

```bash
container run \
    --cap-drop ALL \
    --cap-add SETGID \
    --cap-add NET_RAW \
    alpine sh

```

Grant all capabilities for debugging:

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

```

Combine drops and adds to create a minimal permission set:

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

```

## Summary

- The `apple/container` repository implements **Linux capability management** through `--cap-add` and `--cap-drop` CLI flags defined in [`Flags.swift`](https://github.com/apple/container/blob/main/Flags.swift).
- The `effectiveCapabilities` function in [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift) calculates the final set using a three-step algorithm: initialize base, process adds, process drops.
- Using `--cap-drop ALL` followed by specific `--cap-add` entries creates a secure **whitelist approach** to container permissions.
- All capability names are validated against the `CapabilityName` enum before enforcement, with invalid names producing immediate errors.
- The kernel enforces the final capability set inside the container, as verified by the test suite in [`TestCLIRunCapabilities.swift`](https://github.com/apple/container/blob/main/TestCLIRunCapabilities.swift).

## Frequently Asked Questions

### What is the default capability set when running a container?

If you do not specify `--cap-drop ALL`, the container starts with the **default OCI capability list** (`Containerization.LinuxCapabilities.defaultOCICapabilities`). This provides a standard set of safe capabilities while removing dangerous privileges like `CAP_SYS_ADMIN` or `CAP_NET_ADMIN`.

### How does the ordering of flags affect the final capability set?

The algorithm processes flags in a fixed sequence regardless of their CLI order: first it establishes the base set, then applies all additions, then applies all drops. This means `--cap-drop ALL --cap-add NET_RAW` results in only `CAP_NET_RAW` being present, because the drop clears the set before the add occurs.

### Can I add all capabilities at once?

Yes. Pass `--cap-add ALL` to grant every possible Linux capability to the container. This is useful for debugging but should be avoided in production due to security implications. The implementation uses `CapabilityName.allCases` to expand the `ALL` sentinel into the complete capability set.

### How do I verify that capabilities are actually dropped inside the container?

You can test enforcement by attempting an operation that requires the dropped capability. For example, after dropping `CAP_MKNOD`, running `mknod /tmp/device b 8 0` will fail with "Operation not permitted". The repository's test suite uses this exact method in [`TestCLIRunCapabilities.swift`](https://github.com/apple/container/blob/main/TestCLIRunCapabilities.swift) to verify that the kernel correctly enforces the computed capability restrictions.