Skip to content
Merged
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
8 changes: 6 additions & 2 deletions Sources/ContainerClient/Core/ContainerConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@ public struct ContainerConfiguration: Sendable, Codable {
public var rosetta: Bool = false
/// Initial or main process of the container.
public var initProcess: ProcessConfiguration
/// Platform for the container
/// Platform for the container.
public var platform: ContainerizationOCI.Platform = .current
/// Resource values for the container.
public var resources: Resources = .init()
/// Name of the runtime that supports the container
/// Name of the runtime that supports the container.
public var runtimeHandler: String = "container-runtime-linux"
/// Configure exposing virtualization support in the container.
public var virtualization: Bool = false
/// Enable SSH agent socket forwarding from host to container.
public var ssh: Bool = false

enum CodingKeys: String, CodingKey {
case id
Expand All @@ -65,6 +67,7 @@ public struct ContainerConfiguration: Sendable, Codable {
case resources
case runtimeHandler
case virtualization
case ssh
}

/// Create a configuration from the supplied Decoder, initializing missing
Expand Down Expand Up @@ -99,6 +102,7 @@ public struct ContainerConfiguration: Sendable, Codable {
resources = try container.decodeIfPresent(Resources.self, forKey: .resources) ?? .init()
runtimeHandler = try container.decodeIfPresent(String.self, forKey: .runtimeHandler) ?? "container-runtime-linux"
virtualization = try container.decodeIfPresent(Bool.self, forKey: .virtualization) ?? false
ssh = try container.decodeIfPresent(Bool.self, forKey: .ssh) ?? false
}

public struct DNSConfiguration: Sendable, Codable {
Expand Down
3 changes: 3 additions & 0 deletions Sources/ContainerClient/Flags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ public struct Flags {
"Expose virtualization capabilities to the container. (Host must have nested virtualization support, and guest kernel must have virtualization capabilities enabled)"
)
public var virtualization: Bool = false

@Flag(name: .customLong("ssh"), help: "Forward SSH agent socket to container")
public var ssh = false
}

public struct Progress: ParsableArguments {
Expand Down
2 changes: 2 additions & 0 deletions Sources/ContainerClient/Utility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ public struct Utility {
// to enable socket forwarding from container to host.
config.publishedSockets = try Parser.publishSockets(management.publishSockets)

config.ssh = management.ssh

return (config, kernel)
}

Expand Down
32 changes: 30 additions & 2 deletions Sources/Services/ContainerSandboxService/SandboxService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public actor SandboxService {
private var processes: [String: ProcessInfo] = [:]
private var socketForwarders: [SocketForwarderResult] = []

private static let sshAuthSocketGuestPath = "/run/host-services/ssh-auth.sock"
private static let sshAuthSocketEnvVar = "SSH_AUTH_SOCK"

/// Create an instance with a bundle that describes the container.
///
/// - Parameters:
Expand Down Expand Up @@ -718,6 +721,17 @@ public actor SandboxService {
czConfig.sockets.append(socketConfig)
}

if config.ssh {
if let sshSocket = Foundation.ProcessInfo.processInfo.environment[Self.sshAuthSocketEnvVar] {
let socketConfig = UnixSocketConfiguration(
source: URL(fileURLWithPath: sshSocket),
destination: URL(fileURLWithPath: Self.sshAuthSocketGuestPath),
direction: .into
)
czConfig.sockets.append(socketConfig)
}
}

czConfig.hostname = config.id

if let dns = config.dns {
Expand All @@ -726,7 +740,7 @@ public actor SandboxService {
searchDomains: dns.searchDomains, options: dns.options)
}

Self.configureInitialProcess(czConfig: &czConfig, process: config.initProcess)
Self.configureInitialProcess(czConfig: &czConfig, config: config)
}

private func getDefaultNameserver(attachmentConfigurations: [AttachmentConfiguration]) async throws -> String? {
Expand All @@ -744,10 +758,24 @@ public actor SandboxService {

private static func configureInitialProcess(
czConfig: inout LinuxContainer.Configuration,
process: ProcessConfiguration
config: ContainerConfiguration
) {
let process = config.initProcess

czConfig.process.arguments = [process.executable] + process.arguments
czConfig.process.environmentVariables = process.environment

// Add SSH_AUTH_SOCK if ssh forwarding is enabled
if config.ssh {
if czConfig.sockets.contains(where: {
$0.destination == URL(fileURLWithPath: Self.sshAuthSocketGuestPath)
}) {
if !czConfig.process.environmentVariables.contains(where: { $0.starts(with: "\(Self.sshAuthSocketEnvVar)=") }) {
czConfig.process.environmentVariables.append("\(Self.sshAuthSocketEnvVar)=\(Self.sshAuthSocketGuestPath)")
}
}
}

czConfig.process.terminal = process.terminal
czConfig.process.workingDirectory = process.workingDirectory
czConfig.process.rlimits = process.rlimits.map {
Expand Down