mirror of
https://github.com/apple/container.git
synced 2026-09-27 01:21:04 -04:00
@@ -14,6 +14,8 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ContainerizationExtras
|
||||
|
||||
/// The network protocols available for port forwarding.
|
||||
public enum PublishProtocol: String, Sendable, Codable {
|
||||
case tcp = "tcp"
|
||||
@@ -37,7 +39,7 @@ public enum PublishProtocol: String, Sendable, Codable {
|
||||
/// Specifies internet port forwarding from host to container.
|
||||
public struct PublishPort: Sendable, Codable {
|
||||
/// The IP address of the proxy listener on the host
|
||||
public let hostAddress: String
|
||||
public let hostAddress: IPAddress
|
||||
|
||||
/// The port number of the proxy listener on the host
|
||||
public let hostPort: UInt16
|
||||
@@ -52,7 +54,7 @@ public struct PublishPort: Sendable, Codable {
|
||||
public let count: UInt16
|
||||
|
||||
/// Creates a new port forwarding specification.
|
||||
public init(hostAddress: String, hostPort: UInt16, containerPort: UInt16, proto: PublishProtocol, count: UInt16) {
|
||||
public init(hostAddress: IPAddress, hostPort: UInt16, containerPort: UInt16, proto: PublishProtocol, count: UInt16) {
|
||||
self.hostAddress = hostAddress
|
||||
self.hostPort = hostPort
|
||||
self.containerPort = containerPort
|
||||
@@ -65,7 +67,7 @@ public struct PublishPort: Sendable, Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
hostAddress = try container.decode(String.self, forKey: .hostAddress)
|
||||
hostAddress = try container.decode(IPAddress.self, forKey: .hostAddress)
|
||||
hostPort = try container.decode(UInt16.self, forKey: .hostPort)
|
||||
containerPort = try container.decode(UInt16.self, forKey: .containerPort)
|
||||
proto = try container.decode(PublishProtocol.self, forKey: .proto)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import ContainerResource
|
||||
import Containerization
|
||||
import ContainerizationError
|
||||
import ContainerizationExtras
|
||||
import ContainerizationOCI
|
||||
import ContainerizationOS
|
||||
import Foundation
|
||||
@@ -576,41 +577,39 @@ public struct Parser {
|
||||
|
||||
// Parse a single `--publish-port` argument into a `PublishPort`.
|
||||
public static func publishPort(_ portText: String) throws -> PublishPort {
|
||||
let protoSplit = portText.split(separator: "/")
|
||||
let proto: PublishProtocol
|
||||
let addressAndPortText: String
|
||||
switch protoSplit.count {
|
||||
case 1:
|
||||
addressAndPortText = String(protoSplit[0])
|
||||
proto = .tcp
|
||||
case 2:
|
||||
addressAndPortText = String(protoSplit[0])
|
||||
let protoText = String(protoSplit[1])
|
||||
guard let parsedProto = PublishProtocol(protoText) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid publish protocol: \(protoText)")
|
||||
}
|
||||
proto = parsedProto
|
||||
default:
|
||||
let publishPortRegex = #/((\[(?<ipv6>[^\]]*)\]|(?<ipv4>[^:].*)):)?(?<hostPort>[^:].*):(?<containerPort>[^:/]*)(/(?<proto>.*))?/#
|
||||
guard let match = try publishPortRegex.wholeMatch(in: portText) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid publish value: \(portText)")
|
||||
}
|
||||
|
||||
let hostAddress: String
|
||||
let hostPortText: String
|
||||
let containerPortText: String
|
||||
let parts = addressAndPortText.split(separator: ":")
|
||||
switch parts.count {
|
||||
case 2:
|
||||
hostAddress = "0.0.0.0"
|
||||
hostPortText = String(parts[0])
|
||||
containerPortText = String(parts[1])
|
||||
case 3:
|
||||
hostAddress = String(parts[0])
|
||||
hostPortText = String(parts[1])
|
||||
containerPortText = String(parts[2])
|
||||
let proto: PublishProtocol
|
||||
let protoText = match.proto?.lowercased() ?? "tcp"
|
||||
switch protoText {
|
||||
case "tcp":
|
||||
proto = .tcp
|
||||
case "udp":
|
||||
proto = .udp
|
||||
default:
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid publish address: \(portText)")
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid publish protocol: \(protoText)")
|
||||
}
|
||||
|
||||
let hostAddress: IPAddress
|
||||
if let ipv6 = match.ipv6, !ipv6.isEmpty {
|
||||
guard let address = try? IPAddress(String(ipv6)), case .v6 = address else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid publish IPv6 address: \(portText)")
|
||||
}
|
||||
hostAddress = address
|
||||
} else if let ipv4 = match.ipv4, !ipv4.isEmpty {
|
||||
guard let address = try? IPAddress(String(ipv4)), case .v4 = address else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid publish IPv4 address: \(portText)")
|
||||
}
|
||||
hostAddress = address
|
||||
} else {
|
||||
hostAddress = try IPAddress("0.0.0.0")
|
||||
}
|
||||
|
||||
let hostPortText = match.hostPort
|
||||
let containerPortText = match.containerPort
|
||||
let hostPortRangeStart: UInt16
|
||||
let hostPortRangeEnd: UInt16
|
||||
let containerPortRangeStart: UInt16
|
||||
@@ -679,7 +678,7 @@ public struct Parser {
|
||||
let containerCount = containerPortRangeEnd - containerPortRangeStart + 1
|
||||
|
||||
guard hostCount == containerCount else {
|
||||
throw ContainerizationError(.invalidArgument, message: "publish host and container port counts are not equal: \(addressAndPortText)")
|
||||
throw ContainerizationError(.invalidArgument, message: "publish host and container port counts are not equal: \(hostPortText):\(containerPortText)")
|
||||
}
|
||||
|
||||
return PublishPort(
|
||||
|
||||
@@ -218,9 +218,7 @@ public actor SandboxService {
|
||||
try await container.create()
|
||||
try await self.monitor.registerProcess(id: config.id, onExit: self.onContainerExit)
|
||||
if !container.interfaces.isEmpty {
|
||||
let firstCidr = container.interfaces[0].ipv4Address
|
||||
let ipAddress = firstCidr.address.description
|
||||
try await self.startSocketForwarders(containerIpAddress: ipAddress, publishedPorts: config.publishedPorts)
|
||||
try await self.startSocketForwarders(attachment: attachments[0], publishedPorts: config.publishedPorts)
|
||||
}
|
||||
await self.setState(.booted)
|
||||
} catch {
|
||||
@@ -704,7 +702,7 @@ public actor SandboxService {
|
||||
try await self.monitor.track(id: id, waitingOn: waitFunc)
|
||||
}
|
||||
|
||||
private func startSocketForwarders(containerIpAddress: String, publishedPorts: [PublishPort]) async throws {
|
||||
private func startSocketForwarders(attachment: Attachment, publishedPorts: [PublishPort]) async throws {
|
||||
var forwarders: [SocketForwarderResult] = []
|
||||
guard !publishedPorts.hasOverlaps() else {
|
||||
throw ContainerizationError(.invalidArgument, message: "host ports for different publish port specs may not overlap")
|
||||
@@ -713,8 +711,18 @@ public actor SandboxService {
|
||||
try await withThrowingTaskGroup(of: SocketForwarderResult.self) { group in
|
||||
for publishedPort in publishedPorts {
|
||||
for index in 0..<publishedPort.count {
|
||||
let proxyAddress = try SocketAddress(ipAddress: publishedPort.hostAddress, port: Int(publishedPort.hostPort + index))
|
||||
let serverAddress = try SocketAddress(ipAddress: containerIpAddress, port: Int(publishedPort.containerPort + index))
|
||||
let proxyAddress = try SocketAddress(ipAddress: publishedPort.hostAddress.description, port: Int(publishedPort.hostPort + index))
|
||||
let containerIPAddress: String
|
||||
switch publishedPort.hostAddress {
|
||||
case .v4(_):
|
||||
containerIPAddress = attachment.ipv4Address.address.description
|
||||
case .v6(_):
|
||||
guard let ipv6Address = attachment.ipv6Address else {
|
||||
throw ContainerizationError(.invalidState, message: "cannot configure IPv6 port forwarding for container with unknown IPv6 address")
|
||||
}
|
||||
containerIPAddress = ipv6Address.address.description
|
||||
}
|
||||
let serverAddress = try SocketAddress(ipAddress: containerIPAddress, port: Int(publishedPort.containerPort + index))
|
||||
log.info(
|
||||
"creating forwarder for",
|
||||
metadata: [
|
||||
|
||||
Reference in New Issue
Block a user