From b229cecb53340633ec7b11de9b3171234abb80bd Mon Sep 17 00:00:00 2001 From: Ari Rubinstein Date: Mon, 27 Jul 2026 10:44:51 -0700 Subject: [PATCH] Allow custom kernel boot args via --kernel-arg (#1744) Signed-off-by: Ari Rubinstein <22369+arirubinstein@users.noreply.github.com> Co-authored-by: Ari Rubinstein <22369+arirubinstein@users.noreply.github.com> --- .../ContainerAPIService/Client/Flags.swift | 11 +++++++++++ .../ContainerAPIService/Client/Utility.swift | 10 ++++++++-- .../RuntimeLinux/Server/RuntimeService.swift | 14 ++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Sources/Services/ContainerAPIService/Client/Flags.swift b/Sources/Services/ContainerAPIService/Client/Flags.swift index d26eddd7..980c7c1b 100644 --- a/Sources/Services/ContainerAPIService/Client/Flags.swift +++ b/Sources/Services/ContainerAPIService/Client/Flags.swift @@ -176,6 +176,7 @@ public struct Flags { entrypoint: String?, initImage: String?, kernel: String?, + kernelArgs: [String], labels: [String], mounts: [String], name: String?, @@ -205,6 +206,7 @@ public struct Flags { self.entrypoint = entrypoint self.initImage = initImage self.kernel = kernel + self.kernelArgs = kernelArgs self.labels = labels self.mounts = mounts self.name = name @@ -277,6 +279,15 @@ public struct Flags { ) public var kernel: String? + @Option( + name: .customLong("kernel-arg"), + help: .init( + "Append a raw boot argument to the kernel command line (repeatable).", + valueName: "arg" + ) + ) + public var kernelArgs: [String] = [] + @Option(name: [.short, .customLong("label")], help: "Add a key=value label to the container") public var labels: [String] = [] diff --git a/Sources/Services/ContainerAPIService/Client/Utility.swift b/Sources/Services/ContainerAPIService/Client/Utility.swift index 4d0ba700..163a1db1 100644 --- a/Sources/Services/ContainerAPIService/Client/Utility.swift +++ b/Sources/Services/ContainerAPIService/Client/Utility.swift @@ -330,14 +330,20 @@ public struct Utility { // For the image itself we'll take the user input and try with it as we can do userspace // emulation for x86, but for the kernel we need it to match the hosts architecture. let s: SystemPlatform = .current + var kernel: Kernel if let userKernel = management.kernel { guard FileManager.default.fileExists(atPath: userKernel) else { throw ContainerizationError(.notFound, message: "kernel file not found at path \(userKernel)") } let p = URL(filePath: userKernel) - return .init(path: p, platform: s) + kernel = .init(path: p, platform: s) + } else { + kernel = try await ClientKernel.getDefaultKernel(for: s) } - return try await ClientKernel.getDefaultKernel(for: s) + // Persist any user-supplied boot args onto the kernel command line. A key supplied + // here overrides the runtime's matching built-in default (see RuntimeService.bootstrap). + kernel.commandLine.kernelArgs.append(contentsOf: management.kernelArgs) + return kernel } /// Parses key-value pairs from command line arguments. diff --git a/Sources/Services/RuntimeLinux/Server/RuntimeService.swift b/Sources/Services/RuntimeLinux/Server/RuntimeService.swift index 2714625c..062cef93 100644 --- a/Sources/Services/RuntimeLinux/Server/RuntimeService.swift +++ b/Sources/Services/RuntimeLinux/Server/RuntimeService.swift @@ -160,8 +160,18 @@ public actor RuntimeService { var config = try bundle.configuration var kernel = try bundle.kernel - kernel.commandLine.kernelArgs.append("oops=panic") - kernel.commandLine.kernelArgs.append("lsm=lockdown,capability,landlock,yama,apparmor") + // Built-in defaults keyed by arg name. Each is applied only if the user did not already + // supply the same key via --kernel-arg, letting custom kernels override them (e.g. lsm=...,bpf). + let defaultKernelArgs: KeyValuePairs = [ + "oops": "panic", + "lsm": "lockdown,capability,landlock,yama,apparmor", + ] + for (key, value) in defaultKernelArgs { + guard !kernel.commandLine.kernelArgs.contains(where: { $0.hasPrefix("\(key)=") }) else { + continue + } + kernel.commandLine.kernelArgs.append("\(key)=\(value)") + } let vmm = VZVirtualMachineManager( kernel: kernel, initialFilesystem: bundle.initialFilesystem.asMount,