diff --git a/Package.swift b/Package.swift index 9a2eb69..d98b5fc 100644 --- a/Package.swift +++ b/Package.swift @@ -28,7 +28,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.0.0"), - .package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.30.0"), .package(url: "https://github.com/apple/swift-nio-http2.git", from: "1.0.0"), ], targets: [ diff --git a/Sources/HTTPServer/HTTPServer.swift b/Sources/HTTPServer/HTTPServer.swift index 1670f68..53b9d68 100644 --- a/Sources/HTTPServer/HTTPServer.swift +++ b/Sources/HTTPServer/HTTPServer.swift @@ -174,6 +174,41 @@ public final class Server { logger: logger ) + case .tls(let certificateChain, let privateKey): + let http2Config = NIOHTTP2Handler.Configuration( + httpServerHTTP2Configuration: configuration.http2 + ) + + let certificateChain = try certificateChain + .map { + try NIOSSLCertificate( + bytes: $0.serializeAsPEM().derBytes, + format: .der + ) + } + .map { NIOSSLCertificateSource.certificate($0) } + let privateKey = NIOSSLPrivateKeySource.privateKey( + try NIOSSLPrivateKey( + bytes: privateKey.serializeAsPEM().derBytes, + format: .der + ) + ) + + var tlsConfiguration: TLSConfiguration = .makeServerConfiguration( + certificateChain: certificateChain, + privateKey: privateKey + ) + tlsConfiguration.applicationProtocols = ["h2", "http/1.1"] + + try await Self.serveSecureUpgrade( + bindTarget: configuration.bindTarget, + tlsConfiguration: tlsConfiguration, + handler: handler, + asyncChannelConfiguration: asyncChannelConfiguration, + http2Configuration: http2Config, + logger: logger + ) + case .reloadingTLS(let certificateReloader): let http2Config = NIOHTTP2Handler.Configuration( httpServerHTTP2Configuration: configuration.http2 @@ -193,7 +228,7 @@ public final class Server { logger: logger ) - case .staticTLS(let certificateChain, let privateKey): + case .mTLS(let certificateChain, let privateKey, let trustRoots): let http2Config = NIOHTTP2Handler.Configuration( httpServerHTTP2Configuration: configuration.http2 ) @@ -213,9 +248,58 @@ public final class Server { ) ) - var tlsConfiguration: TLSConfiguration = .makeServerConfiguration( + let nioTrustRoots: NIOSSLTrustRoots + if let trustRoots { + nioTrustRoots = .certificates( + try trustRoots.map { + try NIOSSLCertificate( + bytes: $0.serializeAsPEM().derBytes, + format: .der + ) + } + ) + } else { + nioTrustRoots = .default + } + + var tlsConfiguration: TLSConfiguration = .makeServerConfigurationWithMTLS( certificateChain: certificateChain, - privateKey: privateKey + privateKey: privateKey, + trustRoots: nioTrustRoots + ) + tlsConfiguration.applicationProtocols = ["h2", "http/1.1"] + + try await Self.serveSecureUpgrade( + bindTarget: configuration.bindTarget, + tlsConfiguration: tlsConfiguration, + handler: handler, + asyncChannelConfiguration: asyncChannelConfiguration, + http2Configuration: http2Config, + logger: logger + ) + + case .reloadingMTLS(let certificateReloader, let trustRoots): + let http2Config = NIOHTTP2Handler.Configuration( + httpServerHTTP2Configuration: configuration.http2 + ) + + let nioTrustRoots: NIOSSLTrustRoots + if let trustRoots { + nioTrustRoots = .certificates( + try trustRoots.map { + try NIOSSLCertificate( + bytes: $0.serializeAsPEM().derBytes, + format: .der + ) + } + ) + } else { + nioTrustRoots = .default + } + + var tlsConfiguration: TLSConfiguration = try .makeServerConfigurationWithMTLS( + certificateReloader: certificateReloader, + trustRoots: nioTrustRoots ) tlsConfiguration.applicationProtocols = ["h2", "http/1.1"] diff --git a/Sources/HTTPServer/HTTPServerConfiguration.swift b/Sources/HTTPServer/HTTPServerConfiguration.swift index b2c79c9..7b56003 100644 --- a/Sources/HTTPServer/HTTPServerConfiguration.swift +++ b/Sources/HTTPServer/HTTPServerConfiguration.swift @@ -42,12 +42,20 @@ public struct HTTPServerConfiguration: Sendable { public struct TransportSecurity: Sendable { enum Backing { case plaintext - case staticTLS( + case tls( certificateChain: [Certificate], privateKey: Certificate.PrivateKey ) case reloadingTLS(certificateReloader: any CertificateReloader) - + case mTLS( + certificateChain: [Certificate], + privateKey: Certificate.PrivateKey, + trustRoots: [Certificate]? + ) + case reloadingMTLS( + certificateReloader: any CertificateReloader, + trustRoots: [Certificate]? + ) } let backing: Backing @@ -59,7 +67,7 @@ public struct HTTPServerConfiguration: Sendable { privateKey: Certificate.PrivateKey ) -> Self { Self( - backing: .staticTLS( + backing: .tls( certificateChain: certificateChain, privateKey: privateKey ) @@ -69,6 +77,30 @@ public struct HTTPServerConfiguration: Sendable { public static func tls(certificateReloader: any CertificateReloader) throws -> Self { Self(backing: .reloadingTLS(certificateReloader: certificateReloader)) } + + public static func mTLS( + certificateChain: [Certificate], + privateKey: Certificate.PrivateKey, + trustRoots: [Certificate]? + ) -> Self { + Self( + backing: .mTLS( + certificateChain: certificateChain, + privateKey: privateKey, + trustRoots: trustRoots + ) + ) + } + + public static func mTLS( + certificateReloader: any CertificateReloader, + trustRoots: [Certificate]? + ) throws -> Self { + Self(backing: .reloadingMTLS( + certificateReloader: certificateReloader, + trustRoots: trustRoots + )) + } } /// HTTP/2 specific configuration.