mirror of
https://github.com/apple/container.git
synced 2026-10-13 09:39:18 +00:00
fix: default to restricted OCI capability set (#786)
Changes the default Linux capability set for container processes from `.allCapabilities` to `.defaultOCICapabilities`, making the library secure-by-default. Callers that genuinely need elevated capabilities must now opt in explicitly. Signed-off-by: michael_crosby <michael_crosby@apple.com>
This commit is contained in:
1 parent
b438e97b93
commit
bd709ba8cb
3 files changed
+33
-4
No files matched your search
@@ -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.
|
||||
@@ -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,
|
||||
|
||||
@@ -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: ":")
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerizationOCI
|
||||
import ContainerizationOS
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user