mirror of
https://github.com/apple/container.git
synced 2026-09-22 23:55:34 +00:00
Add SSH auth socket forwarding (#502)
Add `--ssh` flag to forward the host's SSH agent socket into the container, so we can use SSH authentication for things like cloning private repos, and also updates the socket path every time the container starts to handle socket path changes like reboot/re-login. Closes #498
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -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 {
|
||||
@@ -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? {
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user