diff --git a/Sources/Containerization/UnixSocketRelay.swift b/Sources/Containerization/UnixSocketRelay.swift index d98289976..006f1d5bd 100644 --- a/Sources/Containerization/UnixSocketRelay.swift +++ b/Sources/Containerization/UnixSocketRelay.swift @@ -90,13 +90,7 @@ extension UnixSocketRelay { private func setupHostVsockDial() async throws { let hostConn = configuration.destination - - let socketType = try UnixType( - path: hostConn.path, - unlinkExisting: true - ) - let hostSocket = try Socket(type: socketType) - try hostSocket.listen() + let hostSocket = try Self.makeHostListener(configuration) log?.info( "listening on host UDS", @@ -130,6 +124,26 @@ extension UnixSocketRelay { } } + package static func makeHostListener( + _ configuration: UnixSocketConfiguration + ) throws -> Socket { + let socketType = try UnixType( + path: configuration.destination.path, + perms: configuration.permissions.map { + mode_t($0.rawValue) + }, + unlinkExisting: true + ) + let socket = try Socket(type: socketType) + do { + try socket.listen() + return socket + } catch { + try? socket.close() + throw error + } + } + private func setupHostVsockListener() throws { let hostPath = configuration.source diff --git a/Tests/ContainerizationTests/UnixSocketRelayTests.swift b/Tests/ContainerizationTests/UnixSocketRelayTests.swift index 5ff125ea3..06882defb 100644 --- a/Tests/ContainerizationTests/UnixSocketRelayTests.swift +++ b/Tests/ContainerizationTests/UnixSocketRelayTests.swift @@ -31,6 +31,35 @@ import Musl #endif struct UnixSocketRelayTests { + @Test + func outOfRelayAppliesRequestedHostSocketPermissions() throws { + let root = URL(fileURLWithPath: "/tmp").appendingPathComponent( + "containerization-relay-\(UUID())", + isDirectory: true + ) + try FileManager.default.createDirectory( + at: root, + withIntermediateDirectories: true + ) + defer { try? FileManager.default.removeItem(at: root) } + let socketPath = root.appendingPathComponent("service.sock") + let configuration = UnixSocketConfiguration( + source: URL(fileURLWithPath: "/run/service.sock"), + destination: socketPath, + permissions: .init(rawValue: 0o600), + direction: .outOf + ) + + let listener = try UnixSocketRelay.makeHostListener(configuration) + defer { try? listener.close() } + let permissions = try #require( + FileManager.default.attributesOfItem(atPath: socketPath.path)[ + .posixPermissions + ] as? NSNumber + ) + #expect(permissions.uint16Value == 0o600) + } + private func socketPair() throws -> (connection: FileHandle, peer: FileHandle) { var fds: [Int32] = [-1, -1] #if canImport(Glibc)