Skip to content

Commit b87fdbf

Browse files
authored
Add makeServerConfigurationWithMTLS (#286)
To mirror apple/swift-nio-ssl#545 This allows users to easily create a tls configuration for servers which want to do mtls, with reloading
1 parent 382b5fc commit b87fdbf

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ let package = Package(
300300
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.2.0"),
301301
.package(url: "https://github.com/apple/swift-algorithms.git", from: "1.2.0"),
302302
.package(url: "https://github.com/apple/swift-certificates.git", from: "1.10.0"),
303-
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.29.3"),
303+
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.34.0"),
304304
.package(url: "https://github.com/apple/swift-asn1.git", from: "1.3.1"),
305305
.package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.8.0"),
306306
.package(url: "https://github.com/apple/swift-async-algorithms.git", from: "1.0.0"),

Sources/NIOCertificateReloading/CertificateReloader.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,43 @@ extension TLSConfiguration {
8989
return configuration
9090
}
9191

92+
/// Create a ``NIOSSL/TLSConfiguration`` for use with server-side contexts that expect to validate a client, with
93+
/// certificate reloading enabled. For servers that don't need mTLS, try ``TLSConfiguration/makeServerConfiguration(certificateReloader:)``.
94+
/// This configuration is very similar to ``TLSConfiguration/makeServerConfiguration(certificateReloader:)`` but
95+
/// adds a `trustRoots` requirement. These roots will be used to validate the certificate presented by the peer. It
96+
/// also sets the `certificateVerification` field to `noHostnameVerification`, which enables verification but
97+
/// disables any hostname checking, which cannot succeed in a server context.
98+
///
99+
/// - Parameters:
100+
/// - certificateReloader: A ``CertificateReloader`` to watch for certificate and key pair updates.
101+
/// - trustRoots: The roots used to validate the client certificate.
102+
/// - Returns: A ``NIOSSL/TLSConfiguration`` for use with server-side contexts, that reloads the certificate and key
103+
/// used in its SSL handshake.
104+
/// - Throws: This method will throw if an override isn't present. This may happen if a certificate or private key
105+
/// could not be loaded from the given paths.
106+
public static func makeServerConfigurationWithMTLS(
107+
certificateReloader: some CertificateReloader,
108+
trustRoots: NIOSSLTrustRoots
109+
) throws -> Self {
110+
let override = certificateReloader.sslContextConfigurationOverride
111+
112+
guard let certificateChain = override.certificateChain else {
113+
throw CertificateReloaderError.missingCertificateChain
114+
}
115+
116+
guard let privateKey = override.privateKey else {
117+
throw CertificateReloaderError.missingPrivateKey
118+
}
119+
120+
var configuration = Self.makeServerConfigurationWithMTLS(
121+
certificateChain: certificateChain,
122+
privateKey: privateKey,
123+
trustRoots: trustRoots
124+
)
125+
configuration.setCertificateReloader(certificateReloader)
126+
return configuration
127+
}
128+
92129
/// Create a ``NIOSSL/TLSConfiguration`` for use with client-side contexts, with certificate reloading enabled.
93130
/// - Parameter certificateReloader: A ``CertificateReloader`` to watch for certificate and key pair updates.
94131
/// - Returns: A ``NIOSSL/TLSConfiguration`` for use with client-side contexts, that reloads the certificate and key

Tests/NIOCertificateReloadingTests/TimedCertificateReloaderTests.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,56 @@ final class TimedCertificateReloaderTests: XCTestCase {
730730
XCTAssertThrowsError(try TimedCertificateReloader.makeReloaderValidatingSources(configuration: config))
731731
}
732732

733+
/// This tests that `makeServerConfigurationWithMTLS(certificateReloader:trustRoots:)` correctly extracts the
734+
/// certificate chain and private key from `certificateReloader` and sets those in the returned `TLSConfiguration`
735+
/// (along with `trustRoots` and setting `.certificateVerification` to `.noHostnameVerification`)
736+
func testCreateServerConfigWithMTLS() async throws {
737+
let certificateReloader = try TimedCertificateReloader.makeReloaderValidatingSources(
738+
refreshInterval: .seconds(10),
739+
certificateSource: .init(
740+
location: .memory(provider: {
741+
.init(
742+
try Self.sampleCertChain.map { try $0.serializeAsPEM().pemString }.joined(separator: "\n").utf8
743+
)
744+
}),
745+
format: .pem
746+
),
747+
privateKeySource: .init(
748+
location: .memory(provider: { .init(Self.samplePrivateKey1.derRepresentation) }),
749+
format: .der
750+
)
751+
)
752+
753+
let trustRoots = NIOSSLTrustRoots.certificates(
754+
try Self.sampleCertChain.map {
755+
try NIOSSLCertificate(bytes: .init($0.serializeAsPEM().pemString.utf8), format: .pem)
756+
}
757+
)
758+
759+
let tlsConfiguration = try TLSConfiguration.makeServerConfigurationWithMTLS(
760+
certificateReloader: certificateReloader,
761+
trustRoots: trustRoots
762+
)
763+
764+
// Check whether the configuration is set up with the same certificate chain, private key, and trust roots
765+
// that were used to initialize the reloader
766+
XCTAssertEqual(
767+
tlsConfiguration.certificateChain,
768+
try Self.sampleCertChain.map {
769+
.certificate(try NIOSSLCertificate(bytes: $0.serializeAsPEM().derBytes, format: .der))
770+
}
771+
)
772+
XCTAssertEqual(
773+
tlsConfiguration.privateKey,
774+
NIOSSLPrivateKeySource.privateKey(
775+
try NIOSSLPrivateKey(bytes: .init(Self.samplePrivateKey1.derRepresentation), format: .der)
776+
)
777+
)
778+
XCTAssertEqual(tlsConfiguration.trustRoots, trustRoots)
779+
780+
XCTAssertEqual(tlsConfiguration.certificateVerification, .noHostnameVerification)
781+
}
782+
733783
static let startDate = Date()
734784
static let samplePrivateKey1 = P384.Signing.PrivateKey()
735785
static let samplePrivateKey2 = P384.Signing.PrivateKey()

0 commit comments

Comments
 (0)