Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Sources/Containerization/LinuxProcessConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,12 @@ public struct LinuxProcessConfiguration: Sendable {
/// process and its children cannot gain additional privileges via setuid/setgid binaries
/// or file capabilities.
public var noNewPrivileges: Bool = false
/// The Linux capabilities for the container process.
public var capabilities: LinuxCapabilities = .allCapabilities
/// The Linux capabilities for the container process. Defaults to
/// ``LinuxCapabilities/defaultOCICapabilities`` — the restricted baseline used by
/// runc/containerd, which excludes privileged capabilities such as `CAP_SYS_ADMIN`.
/// Callers that require additional capabilities (for example, privileged containers)
/// must opt in explicitly, e.g. by setting ``LinuxCapabilities/allCapabilities``.
public var capabilities: LinuxCapabilities = .defaultOCICapabilities
/// Whether to allocate a pseudo terminal for the process. If you'd like interactive
/// behavior and are planning to use a terminal for stdin/out/err on the client side,
/// this should likely be set to true.
Expand All @@ -398,7 +402,7 @@ public struct LinuxProcessConfiguration: Sendable {
user: ContainerizationOCI.User = .init(),
rlimits: [LinuxRLimit] = [],
noNewPrivileges: Bool = false,
capabilities: LinuxCapabilities = .allCapabilities,
capabilities: LinuxCapabilities = .defaultOCICapabilities,
terminal: Bool = false,
stdin: ReaderStream? = nil,
stdout: Writer? = nil,
Expand Down
1 change: 0 additions & 1 deletion Sources/cctl/RunCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ extension Application {
config.process.setTerminalIO(terminal: current)
config.process.arguments = arguments
config.process.workingDirectory = cwd
config.process.capabilities = .allCapabilities

for mount in self.mounts {
let paths = mount.split(separator: ":")
Expand Down
26 changes: 26 additions & 0 deletions Tests/ContainerizationTests/LinuxContainerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//

import ContainerizationOCI
import ContainerizationOS
import Foundation
import Testing

Expand Down Expand Up @@ -66,4 +67,29 @@ struct LinuxContainerTests {

#expect(process.arguments == ["/bin/sh", "-c", "echo 'hello'", "&&", "sleep 10"])
}

@Test func defaultCapabilitiesAreRestrictedOCISet() {
// Regression guard against shipping `.allCapabilities` as the default.
// A default container must not receive CAP_SYS_ADMIN, which would let it
// write /proc/sys/kernel/core_pattern and escape to guest-root. Cover both
// construction paths: the no-argument init (property default) and the full
// memberwise init (parameter default).
let viaProperty = LinuxProcessConfiguration()
let viaInit = LinuxProcessConfiguration(arguments: ["/bin/sh"])

for caps in [viaProperty.capabilities, viaInit.capabilities] {
for set in [caps.bounding, caps.effective, caps.permitted, caps.inheritable, caps.ambient] {
#expect(!set.contains(.sysAdmin), "default capabilities must not include CAP_SYS_ADMIN")
}
}

// The default must be exactly the documented OCI baseline.
let expected = LinuxCapabilities.defaultOCICapabilities
#expect(viaProperty.capabilities.bounding == expected.bounding)
#expect(viaProperty.capabilities.effective == expected.effective)
#expect(viaProperty.capabilities.permitted == expected.permitted)
#expect(viaProperty.capabilities.inheritable == expected.inheritable)
#expect(viaProperty.capabilities.ambient == expected.ambient)
#expect(viaInit.capabilities.bounding == expected.bounding)
}
}
Loading