From c5c74e2f6107ece9582101722227edefdaa62f8c Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Fri, 30 Aug 2024 15:38:08 +0100 Subject: [PATCH 1/7] Add support for TLS on H2 NIOTS server transport --- ...TP2ServerTransport+TransportServices.swift | 81 +++++++++++++++--- .../TLSConfig.swift | 84 +++++++++++++++++++ ...P2TransportNIOTransportServicesTests.swift | 70 ++++++++++++++-- .../HTTP2TransportTests.swift | 2 +- 4 files changed, 220 insertions(+), 17 deletions(-) create mode 100644 Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift index a3fb426d1..f25ea723a 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift @@ -19,9 +19,10 @@ public import GRPCCore public import NIOTransportServices // has to be public because of default argument value in init public import GRPCHTTP2Core -internal import NIOCore -internal import NIOExtras -internal import NIOHTTP2 +private import NIOCore +private import NIOExtras +private import NIOHTTP2 +private import Network private import Synchronization @@ -171,7 +172,51 @@ extension HTTP2ServerTransport { } } - let serverChannel = try await NIOTSListenerBootstrap(group: self.eventLoopGroup) + let bootstrap: NIOTSListenerBootstrap + + let requireALPN: Bool + let scheme: Scheme + switch self.config.transportSecurity.wrapped { + case .plaintext: + requireALPN = false + scheme = .http + bootstrap = NIOTSListenerBootstrap(group: self.eventLoopGroup) + + case .tls(let tlsConfig): + requireALPN = tlsConfig.requireALPN + scheme = .https + + let nwTLSOptions = NWProtocolTLS.Options() + sec_protocol_options_set_local_identity( + nwTLSOptions.securityProtocolOptions, + sec_identity_create(tlsConfig.identity)! + ) + + sec_protocol_options_set_min_tls_protocol_version( + nwTLSOptions.securityProtocolOptions, + .TLSv12 + ) + + if let hostnameOverride = tlsConfig.hostnameOverride { + sec_protocol_options_set_tls_server_name( + nwTLSOptions.securityProtocolOptions, + hostnameOverride + ) + } + + for `protocol` in ["grpc-exp", "h2"] { + sec_protocol_options_add_tls_application_protocol( + nwTLSOptions.securityProtocolOptions, + `protocol` + ) + } + + bootstrap = NIOTSListenerBootstrap(group: self.eventLoopGroup) + .tlsOptions(nwTLSOptions) + } + + let serverChannel = + try await bootstrap .serverChannelOption( ChannelOptions.socketOption(.so_reuseaddr), value: 1 @@ -190,8 +235,8 @@ extension HTTP2ServerTransport { connectionConfig: self.config.connection, http2Config: self.config.http2, rpcConfig: self.config.rpc, - requireALPN: false, - scheme: .http + requireALPN: requireALPN, + scheme: scheme ) } } @@ -292,41 +337,55 @@ extension HTTP2ServerTransport.TransportServices { public struct Config: Sendable { /// Compression configuration. public var compression: HTTP2ServerTransport.Config.Compression + /// Connection configuration. public var connection: HTTP2ServerTransport.Config.Connection + /// HTTP2 configuration. public var http2: HTTP2ServerTransport.Config.HTTP2 + /// RPC configuration. public var rpc: HTTP2ServerTransport.Config.RPC + /// The transport's security. + public var transportSecurity: TransportSecurity + /// Construct a new `Config`. /// - Parameters: /// - compression: Compression configuration. /// - connection: Connection configuration. /// - http2: HTTP2 configuration. /// - rpc: RPC configuration. + /// - transportSecurity: The transport's security configuration. public init( compression: HTTP2ServerTransport.Config.Compression, connection: HTTP2ServerTransport.Config.Connection, http2: HTTP2ServerTransport.Config.HTTP2, - rpc: HTTP2ServerTransport.Config.RPC + rpc: HTTP2ServerTransport.Config.RPC, + transportSecurity: TransportSecurity ) { self.compression = compression self.connection = connection self.http2 = http2 self.rpc = rpc + self.transportSecurity = transportSecurity } /// Default values for the different configurations. /// - /// - Parameter configure: A closure which allows you to modify the defaults before - /// returning them. - public static func defaults(configure: (_ config: inout Self) -> Void = { _ in }) -> Self { + /// - Parameters: + /// - transportSecurity: The transport's security configuration. + /// - configure: A closure which allows you to modify the defaults before returning them. + public static func defaults( + transportSecurity: TransportSecurity, + configure: (_ config: inout Self) -> Void = { _ in } + ) -> Self { var config = Self( compression: .defaults, connection: .defaults, http2: .defaults, - rpc: .defaults + rpc: .defaults, + transportSecurity: transportSecurity ) configure(&config) return config diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift new file mode 100644 index 000000000..4293b8f1f --- /dev/null +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift @@ -0,0 +1,84 @@ +/* + * Copyright 2024, gRPC Authors All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if canImport(Network) +@preconcurrency public import Network + +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +extension HTTP2ServerTransport.TransportServices.Config { + /// The security configuration for this connection. + public struct TransportSecurity: Sendable { + package enum Wrapped: Sendable { + case plaintext + case tls(TLS) + } + + package let wrapped: Wrapped + + /// This connection is plaintext: no encryption will take place. + public static let plaintext = Self(wrapped: .plaintext) + + /// This connection will use TLS. + public static func tls(_ tls: TLS) -> Self { + Self(wrapped: .tls(tls)) + } + } + + public struct TLS: Sendable { + /// The `SecIdentity` to be used when setting up TLS. + public var identity: SecIdentity + + /// An optional hostname override to use during certificate verification. + public var hostnameOverride: String? + + /// Whether ALPN is required. + /// + /// If this is set to `true` but the client does not support ALPN, then the connection will be rejected. + public var requireALPN: Bool + + /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted: + /// - `requireALPN` equals `false` + /// - `hostnameOverride` equals `nil` + /// + /// - Returns: A new HTTP2 NIO Transport Services transport TLS config. + public static func defaults( + identity: SecIdentity + ) -> Self { + Self.init( + identity: identity, + hostnameOverride: nil, + requireALPN: false + ) + } + + /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted to match + /// the requirements of mTLS: + /// - `requireALPN` equals `false` + /// - `hostnameOverride` equals `""` + /// + /// - Returns: A new HTTP2 NIO Transport Services transport TLS config. + public static func mTLS( + identity: SecIdentity + ) -> Self { + Self.init( + identity: identity, + hostnameOverride: "", + requireALPN: false + ) + } + } +} +#endif diff --git a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift index 8219540ec..6c83a9692 100644 --- a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift +++ b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift @@ -22,10 +22,52 @@ import XCTest @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) final class HTTP2TransportNIOTransportServicesTests: XCTestCase { + private var identity: SecIdentity! + + private static let p12bundleURL = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() // (this file) + .deletingLastPathComponent() // GRPCHTTP2TransportTests + .deletingLastPathComponent() // Tests + .appendingPathComponent("Sources") + .appendingPathComponent("GRPCSampleData") + .appendingPathComponent("bundle") + .appendingPathExtension("p12") + + override func setUp() async throws { + try await super.setUp() + + self.identity = try self.loadIdentity() + XCTAssertNotNil( + self.identity, + "Unable to load identity from '\(Self.p12bundleURL)'" + ) + } + + private func loadIdentity() throws -> SecIdentity? { + let data = try Data(contentsOf: Self.p12bundleURL) + let options = [kSecImportExportPassphrase as String: "password"] + + var rawItems: CFArray? + let status = SecPKCS12Import(data as CFData, options as CFDictionary, &rawItems) + + switch status { + case errSecSuccess: + () + case errSecInteractionNotAllowed: + throw XCTSkip("Unable to import PKCS12 bundle: no interaction allowed") + default: + XCTFail("SecPKCS12Import: failed with status \(status)") + return nil + } + + let items = rawItems! as! [[String: Any]] + return items.first?[kSecImportItemIdentity as String] as! SecIdentity? + } + func testGetListeningAddress_IPv4() async throws { let transport = GRPCHTTP2Core.HTTP2ServerTransport.TransportServices( address: .ipv4(host: "0.0.0.0", port: 0), - config: .defaults() + config: .defaults(transportSecurity: .plaintext) ) try await withThrowingDiscardingTaskGroup { group in @@ -45,7 +87,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_IPv6() async throws { let transport = GRPCHTTP2Core.HTTP2ServerTransport.TransportServices( address: .ipv6(host: "::1", port: 0), - config: .defaults() + config: .defaults(transportSecurity: .plaintext) ) try await withThrowingDiscardingTaskGroup { group in @@ -65,7 +107,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_UnixDomainSocket() async throws { let transport = GRPCHTTP2Core.HTTP2ServerTransport.TransportServices( address: .unixDomainSocket(path: "/tmp/niots-uds-test"), - config: .defaults() + config: .defaults(transportSecurity: .plaintext) ) defer { // NIOTS does not unlink the UDS on close. @@ -91,7 +133,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_InvalidAddress() async { let transport = GRPCHTTP2Core.HTTP2ServerTransport.TransportServices( address: .unixDomainSocket(path: "/this/should/be/an/invalid/path"), - config: .defaults() + config: .defaults(transportSecurity: .plaintext) ) try? await withThrowingDiscardingTaskGroup { group in @@ -120,7 +162,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_StoppedListening() async throws { let transport = GRPCHTTP2Core.HTTP2ServerTransport.TransportServices( address: .ipv4(host: "0.0.0.0", port: 0), - config: .defaults() + config: .defaults(transportSecurity: .plaintext) ) try? await withThrowingDiscardingTaskGroup { group in @@ -149,5 +191,23 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { } } } + + func testTLSConfig_Defaults() throws { + let grpcTLSConfig = HTTP2ServerTransport.TransportServices.Config.TLS.defaults( + identity: self.identity + ) + XCTAssertEqual(grpcTLSConfig.identity, self.identity) + XCTAssertEqual(grpcTLSConfig.hostnameOverride, nil) + XCTAssertEqual(grpcTLSConfig.requireALPN, false) + } + + func testTLSConfig_mTLS() throws { + let grpcTLSConfig = HTTP2ServerTransport.TransportServices.Config.TLS.mTLS( + identity: self.identity + ) + XCTAssertEqual(grpcTLSConfig.identity, self.identity) + XCTAssertEqual(grpcTLSConfig.hostnameOverride, "") + XCTAssertEqual(grpcTLSConfig.requireALPN, false) + } } #endif diff --git a/Tests/GRPCHTTP2TransportTests/HTTP2TransportTests.swift b/Tests/GRPCHTTP2TransportTests/HTTP2TransportTests.swift index a78751874..14a9d69c3 100644 --- a/Tests/GRPCHTTP2TransportTests/HTTP2TransportTests.swift +++ b/Tests/GRPCHTTP2TransportTests/HTTP2TransportTests.swift @@ -166,7 +166,7 @@ final class HTTP2TransportTests: XCTestCase { let server = GRPCServer( transport: .http2NIOTS( address: .ipv4(host: "127.0.0.1", port: 0), - config: .defaults { + config: .defaults(transportSecurity: .plaintext) { $0.compression.enabledAlgorithms = compression } ), From cca2422fe8f7695d0105a7527e3197783a8d57cc Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Mon, 9 Sep 2024 12:03:45 +0100 Subject: [PATCH 2/7] PR changes --- ...TP2ServerTransport+TransportServices.swift | 69 +++++++++++-------- .../TLSConfig.swift | 4 +- ...P2TransportNIOTransportServicesTests.swift | 39 +++++++---- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift index f25ea723a..d7fc0f9e8 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift @@ -185,34 +185,8 @@ extension HTTP2ServerTransport { case .tls(let tlsConfig): requireALPN = tlsConfig.requireALPN scheme = .https - - let nwTLSOptions = NWProtocolTLS.Options() - sec_protocol_options_set_local_identity( - nwTLSOptions.securityProtocolOptions, - sec_identity_create(tlsConfig.identity)! - ) - - sec_protocol_options_set_min_tls_protocol_version( - nwTLSOptions.securityProtocolOptions, - .TLSv12 - ) - - if let hostnameOverride = tlsConfig.hostnameOverride { - sec_protocol_options_set_tls_server_name( - nwTLSOptions.securityProtocolOptions, - hostnameOverride - ) - } - - for `protocol` in ["grpc-exp", "h2"] { - sec_protocol_options_add_tls_application_protocol( - nwTLSOptions.securityProtocolOptions, - `protocol` - ) - } - bootstrap = NIOTSListenerBootstrap(group: self.eventLoopGroup) - .tlsOptions(nwTLSOptions) + .tlsOptions(try NWProtocolTLS.Options(tlsConfig)) } let serverChannel = @@ -455,4 +429,45 @@ extension ServerTransport where Self == HTTP2ServerTransport.TransportServices { ) } } + +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +extension NWProtocolTLS.Options { + convenience init(_ tlsConfig: HTTP2ServerTransport.TransportServices.Config.TLS) throws { + self.init() + + guard let sec_identity = sec_identity_create(tlsConfig.identity) else { + throw RuntimeError( + code: .transportError, + message: """ + There was an issue creating the SecIdentity required to set up TLS. \ + Please check your TLS configuration. + """ + ) + } + + sec_protocol_options_set_local_identity( + self.securityProtocolOptions, + sec_identity + ) + + sec_protocol_options_set_min_tls_protocol_version( + self.securityProtocolOptions, + .TLSv12 + ) + + if let hostnameOverride = tlsConfig.hostnameOverride { + sec_protocol_options_set_tls_server_name( + self.securityProtocolOptions, + hostnameOverride + ) + } + + for `protocol` in ["grpc-exp", "h2"] { + sec_protocol_options_add_tls_application_protocol( + self.securityProtocolOptions, + `protocol` + ) + } + } +} #endif diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift index 4293b8f1f..cf609e9f4 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift @@ -57,7 +57,7 @@ extension HTTP2ServerTransport.TransportServices.Config { public static func defaults( identity: SecIdentity ) -> Self { - Self.init( + Self( identity: identity, hostnameOverride: nil, requireALPN: false @@ -73,7 +73,7 @@ extension HTTP2ServerTransport.TransportServices.Config { public static func mTLS( identity: SecIdentity ) -> Self { - Self.init( + Self( identity: identity, hostnameOverride: "", requireALPN: false diff --git a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift index 6c83a9692..1f4ed9f1f 100644 --- a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift +++ b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift @@ -45,23 +45,34 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { private func loadIdentity() throws -> SecIdentity? { let data = try Data(contentsOf: Self.p12bundleURL) - let options = [kSecImportExportPassphrase as String: "password"] - - var rawItems: CFArray? - let status = SecPKCS12Import(data as CFData, options as CFDictionary, &rawItems) - - switch status { - case errSecSuccess: - () - case errSecInteractionNotAllowed: - throw XCTSkip("Unable to import PKCS12 bundle: no interaction allowed") - default: - XCTFail("SecPKCS12Import: failed with status \(status)") + + var externalFormat = SecExternalFormat.formatUnknown + var externalItemType = SecExternalItemType.itemTypeUnknown + let passphrase = "password" as CFTypeRef + var exportKeyParams = SecItemImportExportKeyParameters() + exportKeyParams.passphrase = Unmanaged.passUnretained(passphrase) + var items: CFArray? + + let status = SecItemImport( + data as CFData, + "bundle.p12" as CFString, + &externalFormat, + &externalItemType, + SecItemImportExportFlags(rawValue: 0), + &exportKeyParams, + nil, + &items + ) + + if status != errSecSuccess { + XCTFail("SecItemImport failed with status \(status)") + return nil + } else if items == nil { + XCTFail("SecItemImport failed.") return nil } - let items = rawItems! as! [[String: Any]] - return items.first?[kSecImportItemIdentity as String] as! SecIdentity? + return ((items! as NSArray)[0] as! SecIdentity) } func testGetListeningAddress_IPv4() async throws { From 644b519fd06ca9ed572d303f9fa726295b79da02 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Mon, 9 Sep 2024 12:06:06 +0100 Subject: [PATCH 3/7] Formatting --- .../HTTP2ServerTransport+TransportServices.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift index d7fc0f9e8..35ba6d4b7 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift @@ -439,9 +439,9 @@ extension NWProtocolTLS.Options { throw RuntimeError( code: .transportError, message: """ - There was an issue creating the SecIdentity required to set up TLS. \ - Please check your TLS configuration. - """ + There was an issue creating the SecIdentity required to set up TLS. \ + Please check your TLS configuration. + """ ) } From d87fa0026b9a21f6f57952cab44a937cd8001a70 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Mon, 9 Sep 2024 15:10:33 +0100 Subject: [PATCH 4/7] Don't provide mTLS config --- ...TP2ServerTransport+TransportServices.swift | 7 ------- .../TLSConfig.swift | 21 ------------------- ...P2TransportNIOTransportServicesTests.swift | 10 --------- 3 files changed, 38 deletions(-) diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift index 35ba6d4b7..74f8bba04 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift @@ -455,13 +455,6 @@ extension NWProtocolTLS.Options { .TLSv12 ) - if let hostnameOverride = tlsConfig.hostnameOverride { - sec_protocol_options_set_tls_server_name( - self.securityProtocolOptions, - hostnameOverride - ) - } - for `protocol` in ["grpc-exp", "h2"] { sec_protocol_options_add_tls_application_protocol( self.securityProtocolOptions, diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift index cf609e9f4..623088b5e 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift @@ -41,9 +41,6 @@ extension HTTP2ServerTransport.TransportServices.Config { /// The `SecIdentity` to be used when setting up TLS. public var identity: SecIdentity - /// An optional hostname override to use during certificate verification. - public var hostnameOverride: String? - /// Whether ALPN is required. /// /// If this is set to `true` but the client does not support ALPN, then the connection will be rejected. @@ -51,7 +48,6 @@ extension HTTP2ServerTransport.TransportServices.Config { /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted: /// - `requireALPN` equals `false` - /// - `hostnameOverride` equals `nil` /// /// - Returns: A new HTTP2 NIO Transport Services transport TLS config. public static func defaults( @@ -59,23 +55,6 @@ extension HTTP2ServerTransport.TransportServices.Config { ) -> Self { Self( identity: identity, - hostnameOverride: nil, - requireALPN: false - ) - } - - /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted to match - /// the requirements of mTLS: - /// - `requireALPN` equals `false` - /// - `hostnameOverride` equals `""` - /// - /// - Returns: A new HTTP2 NIO Transport Services transport TLS config. - public static func mTLS( - identity: SecIdentity - ) -> Self { - Self( - identity: identity, - hostnameOverride: "", requireALPN: false ) } diff --git a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift index 1f4ed9f1f..3dac42e05 100644 --- a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift +++ b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift @@ -208,16 +208,6 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { identity: self.identity ) XCTAssertEqual(grpcTLSConfig.identity, self.identity) - XCTAssertEqual(grpcTLSConfig.hostnameOverride, nil) - XCTAssertEqual(grpcTLSConfig.requireALPN, false) - } - - func testTLSConfig_mTLS() throws { - let grpcTLSConfig = HTTP2ServerTransport.TransportServices.Config.TLS.mTLS( - identity: self.identity - ) - XCTAssertEqual(grpcTLSConfig.identity, self.identity) - XCTAssertEqual(grpcTLSConfig.hostnameOverride, "") XCTAssertEqual(grpcTLSConfig.requireALPN, false) } } From cfe028dc1fee0f8581644cfb78883f82a319a43b Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Mon, 9 Sep 2024 16:13:58 +0100 Subject: [PATCH 5/7] Remove @preconcurrency import for Network.framework --- ...TP2ServerTransport+TransportServices.swift | 2 +- .../TLSConfig.swift | 8 ++-- ...P2TransportNIOTransportServicesTests.swift | 37 +++++++++---------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift index 74f8bba04..8bf2e5b0f 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift @@ -435,7 +435,7 @@ extension NWProtocolTLS.Options { convenience init(_ tlsConfig: HTTP2ServerTransport.TransportServices.Config.TLS) throws { self.init() - guard let sec_identity = sec_identity_create(tlsConfig.identity) else { + guard let sec_identity = sec_identity_create(tlsConfig.identityProvider()) else { throw RuntimeError( code: .transportError, message: """ diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift index 623088b5e..222a945a4 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift @@ -15,7 +15,7 @@ */ #if canImport(Network) -@preconcurrency public import Network +public import Network @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension HTTP2ServerTransport.TransportServices.Config { @@ -39,7 +39,7 @@ extension HTTP2ServerTransport.TransportServices.Config { public struct TLS: Sendable { /// The `SecIdentity` to be used when setting up TLS. - public var identity: SecIdentity + public var identityProvider: @Sendable () -> SecIdentity /// Whether ALPN is required. /// @@ -51,10 +51,10 @@ extension HTTP2ServerTransport.TransportServices.Config { /// /// - Returns: A new HTTP2 NIO Transport Services transport TLS config. public static func defaults( - identity: SecIdentity + identityProvider: @Sendable @escaping () -> SecIdentity ) -> Self { Self( - identity: identity, + identityProvider: identityProvider, requireALPN: false ) } diff --git a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift index 3dac42e05..c02f1e5d7 100644 --- a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift +++ b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift @@ -22,8 +22,6 @@ import XCTest @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) final class HTTP2TransportNIOTransportServicesTests: XCTestCase { - private var identity: SecIdentity! - private static let p12bundleURL = URL(fileURLWithPath: #filePath) .deletingLastPathComponent() // (this file) .deletingLastPathComponent() // GRPCHTTP2TransportTests @@ -33,18 +31,8 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { .appendingPathComponent("bundle") .appendingPathExtension("p12") - override func setUp() async throws { - try await super.setUp() - - self.identity = try self.loadIdentity() - XCTAssertNotNil( - self.identity, - "Unable to load identity from '\(Self.p12bundleURL)'" - ) - } - - private func loadIdentity() throws -> SecIdentity? { - let data = try Data(contentsOf: Self.p12bundleURL) + @Sendable private static func loadIdentity() -> SecIdentity { + let data = try! Data(contentsOf: Self.p12bundleURL) var externalFormat = SecExternalFormat.formatUnknown var externalItemType = SecExternalItemType.itemTypeUnknown @@ -65,11 +53,19 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { ) if status != errSecSuccess { - XCTFail("SecItemImport failed with status \(status)") - return nil + XCTFail( + """ + Unable to load identity from '\(Self.p12bundleURL)'. \ + SecItemImport failed with status \(status) + """ + ) } else if items == nil { - XCTFail("SecItemImport failed.") - return nil + XCTFail( + """ + Unable to load identity from '\(Self.p12bundleURL)'. \ + SecItemImport failed. + """ + ) } return ((items! as NSArray)[0] as! SecIdentity) @@ -204,10 +200,11 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { } func testTLSConfig_Defaults() throws { + let identityProvider = Self.loadIdentity let grpcTLSConfig = HTTP2ServerTransport.TransportServices.Config.TLS.defaults( - identity: self.identity + identityProvider: identityProvider ) - XCTAssertEqual(grpcTLSConfig.identity, self.identity) + XCTAssertEqual(grpcTLSConfig.identityProvider(), identityProvider()) XCTAssertEqual(grpcTLSConfig.requireALPN, false) } } From 78846bf018cb43a93c0d8bbcea6078e6d37deb16 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Mon, 9 Sep 2024 16:52:29 +0100 Subject: [PATCH 6/7] Make identityProvider throwing --- .../HTTP2ServerTransport+TransportServices.swift | 2 +- .../GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift | 4 ++-- .../HTTP2TransportNIOTransportServicesTests.swift | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift index 8bf2e5b0f..7b37bcc4d 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/HTTP2ServerTransport+TransportServices.swift @@ -435,7 +435,7 @@ extension NWProtocolTLS.Options { convenience init(_ tlsConfig: HTTP2ServerTransport.TransportServices.Config.TLS) throws { self.init() - guard let sec_identity = sec_identity_create(tlsConfig.identityProvider()) else { + guard let sec_identity = sec_identity_create(try tlsConfig.identityProvider()) else { throw RuntimeError( code: .transportError, message: """ diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift index 222a945a4..87198ae74 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift @@ -39,7 +39,7 @@ extension HTTP2ServerTransport.TransportServices.Config { public struct TLS: Sendable { /// The `SecIdentity` to be used when setting up TLS. - public var identityProvider: @Sendable () -> SecIdentity + public var identityProvider: @Sendable () throws -> SecIdentity /// Whether ALPN is required. /// @@ -51,7 +51,7 @@ extension HTTP2ServerTransport.TransportServices.Config { /// /// - Returns: A new HTTP2 NIO Transport Services transport TLS config. public static func defaults( - identityProvider: @Sendable @escaping () -> SecIdentity + identityProvider: @Sendable @escaping () throws -> SecIdentity ) -> Self { Self( identityProvider: identityProvider, diff --git a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift index c02f1e5d7..8611bdb16 100644 --- a/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift +++ b/Tests/GRPCHTTP2TransportTests/HTTP2TransportNIOTransportServicesTests.swift @@ -31,8 +31,8 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { .appendingPathComponent("bundle") .appendingPathExtension("p12") - @Sendable private static func loadIdentity() -> SecIdentity { - let data = try! Data(contentsOf: Self.p12bundleURL) + @Sendable private static func loadIdentity() throws -> SecIdentity { + let data = try Data(contentsOf: Self.p12bundleURL) var externalFormat = SecExternalFormat.formatUnknown var externalItemType = SecExternalItemType.itemTypeUnknown @@ -204,7 +204,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { let grpcTLSConfig = HTTP2ServerTransport.TransportServices.Config.TLS.defaults( identityProvider: identityProvider ) - XCTAssertEqual(grpcTLSConfig.identityProvider(), identityProvider()) + XCTAssertEqual(try grpcTLSConfig.identityProvider(), try identityProvider()) XCTAssertEqual(grpcTLSConfig.requireALPN, false) } } From 04be347127e52d07a7fdd3b5f819f76beb05b6c4 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Mon, 9 Sep 2024 16:53:22 +0100 Subject: [PATCH 7/7] Fix docs --- Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift index 87198ae74..05840a42d 100644 --- a/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift +++ b/Sources/GRPCHTTP2TransportNIOTransportServices/TLSConfig.swift @@ -38,7 +38,7 @@ extension HTTP2ServerTransport.TransportServices.Config { } public struct TLS: Sendable { - /// The `SecIdentity` to be used when setting up TLS. + /// A provider for the `SecIdentity` to be used when setting up TLS. public var identityProvider: @Sendable () throws -> SecIdentity /// Whether ALPN is required.