mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-06-01 18:53:35 +08:00
Add docs and a new TLSConfig init
This commit is contained in:
parent
2fd8dca7b1
commit
ed45e53610
@ -20,7 +20,6 @@ import NIOSSL
|
||||
/// the form of a `NIOSSLContextConfigurationOverride`, which will be used when performing a TLS handshake in NIO.
|
||||
/// Each implementation can choose how to observe for changes, but they all require an ``sslContextConfigurationOverride``
|
||||
/// to be exposed.
|
||||
@available(macOS 11.0, iOS 14, tvOS 14, watchOS 7, *)
|
||||
public protocol CertificateReloader: Sendable {
|
||||
/// A `NIOSSLContextConfigurationOverride` that will be used as part of the NIO application's TLS configuration.
|
||||
/// Its certificate and private key will be kept up-to-date via whatever mechanism the specific ``CertificateReloader``
|
||||
@ -29,10 +28,53 @@ public protocol CertificateReloader: Sendable {
|
||||
}
|
||||
|
||||
extension TLSConfiguration {
|
||||
/// Errors thrown when creating a ``NIOSSL/TLSConfiguration`` with a ``CertificateReloader``.
|
||||
public struct CertificateReloaderError: Error {
|
||||
private enum _Backing {
|
||||
case missingCertificateChain
|
||||
case missingPrivateKey
|
||||
}
|
||||
|
||||
private let backing: _Backing
|
||||
|
||||
private init(backing: _Backing) {
|
||||
self.backing = backing
|
||||
}
|
||||
|
||||
/// The given ``CertificateReloader`` could not provide a certificate chain with which to create this config.
|
||||
public static let missingCertificateChain: Self = .init(backing: .missingCertificateChain)
|
||||
|
||||
/// The given ``CertificateReloader`` could not provide a private key with which to create this config.
|
||||
public static let missingPrivateKey: Self = .init(backing: .missingPrivateKey)
|
||||
}
|
||||
|
||||
/// Create a ``NIOSSL/TLSConfiguration`` for use with server-side contexts, with certificate reloading enabled.
|
||||
/// - Parameter certificateReloader: A ``CertificateReloader`` to watch for certificate and key pair updates.
|
||||
/// - Returns: A ``NIOSSL/TLSConfiguration`` for use with server-side contexts, that reloads the certificate and key
|
||||
/// used in its SSL handshake.
|
||||
public static func makeServerConfiguration(
|
||||
certificateReloader: some CertificateReloader
|
||||
) throws -> Self {
|
||||
let override = certificateReloader.sslContextConfigurationOverride
|
||||
|
||||
guard let certificateChain = override.certificateChain else {
|
||||
throw CertificateReloaderError.missingCertificateChain
|
||||
}
|
||||
|
||||
guard let privateKey = override.privateKey else {
|
||||
throw CertificateReloaderError.missingPrivateKey
|
||||
}
|
||||
|
||||
var configuration = Self.makeServerConfiguration(
|
||||
certificateChain: certificateChain,
|
||||
privateKey: privateKey
|
||||
)
|
||||
return configuration.setCertificateReloader(certificateReloader)
|
||||
}
|
||||
|
||||
/// Configure a ``CertificateReloader`` to observe updates for the certificate and key pair used.
|
||||
/// - Parameter reloader: A ``CertificateReloader`` to watch for certificate and key pair updates.
|
||||
/// - Returns: A `TLSConfiguration` that reloads the certificate and key used in its SSL handshake.
|
||||
@available(macOS 11.0, iOS 14, tvOS 14, watchOS 7, *)
|
||||
/// - Returns: A ``NIOSSL/TLSConfiguration`` that reloads the certificate and key used in its SSL handshake.
|
||||
mutating public func setCertificateReloader(_ reloader: some CertificateReloader) -> Self {
|
||||
self.sslContextCallback = { _, promise in
|
||||
promise.succeed(reloader.sslContextConfigurationOverride)
|
||||
|
@ -31,12 +31,56 @@ import Foundation
|
||||
/// key pair is updated at a fixed interval from the file path or memory location configured.
|
||||
///
|
||||
/// You initialize a ``TimedCertificateReloader`` by providing a refresh interval, and locations for the certificate and the private
|
||||
/// key. You must then call ``run()`` on this reloader for it to start observing changes.
|
||||
/// Once the reloader is running, call ``sslContextConfigurationOverride`` to get a
|
||||
/// `NIOSSLContextConfigurationOverride` which can be set on NIO's `TLSConfiguration`: this will keep the certificate
|
||||
/// and private key pair up to date.
|
||||
/// You may instead call ``NIOSSL/TLSConfiguration/setCertificateReloader(_:)`` to get a
|
||||
/// ``NIOSSL/TLSConfiguration`` with a configured reloader.
|
||||
/// key. You may then set it on your ``NIOSSL/TLSConfiguration`` using
|
||||
/// ``NIOSSL/TLSConfiguration/setCertificateReloader(_:)``:
|
||||
///
|
||||
/// ```
|
||||
/// var configuration = TLSConfiguration.makeServerConfiguration(
|
||||
/// certificateChain: chain,
|
||||
/// privateKey: key
|
||||
/// )
|
||||
/// let reloader = TimedCertificateReloader(
|
||||
/// refreshInterval: .seconds(500),
|
||||
/// certificateDescription: TimedCertificateReloader.CertificateDescription(...),
|
||||
/// privateKeyDescription: TimedCertificateReloader.PrivateKeyDescription(...)
|
||||
/// )
|
||||
/// configuration.setCertificateReloader(reloader)
|
||||
/// ```
|
||||
///
|
||||
/// If you're creating a server configuration, you can instead opt to use
|
||||
/// ``NIOSSL/TLSConfiguration/makeServerConfiguration(certificateReloader:)``, which will set the initial
|
||||
/// certificate chain and private key, as well as set the reloader:
|
||||
///
|
||||
/// ```
|
||||
/// let reloader = TimedCertificateReloader(
|
||||
/// refreshInterval: .seconds(500),
|
||||
/// certificateDescription: TimedCertificateReloader.CertificateDescription(...),
|
||||
/// privateKeyDescription: TimedCertificateReloader.PrivateKeyDescription(...)
|
||||
/// )
|
||||
/// let configuration = TLSConfiguration.makeServerConfiguration(
|
||||
/// certificateReloader: reloader
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// Finally, you must call ``run()`` on the reloader for it to start observing changes.
|
||||
/// Once the reloader is running, you can also manually access its ``sslContextConfigurationOverride`` property to get a
|
||||
/// `NIOSSLContextConfigurationOverride`, although this will typically not be necessary, as it's the NIO channel that will
|
||||
/// handle the override when initiating TLS handshakes.
|
||||
///
|
||||
/// ```
|
||||
/// try await withThrowingTaskGroup(of: Void.self) { group in
|
||||
/// group.addTask {
|
||||
/// reloader.run()
|
||||
/// }
|
||||
///
|
||||
/// // ...
|
||||
/// let override = reloader.sslContextConfigurationOverride
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ``TimedCertificateReloader`` conforms to `ServiceLifecycle`'s `Service` protocol, meaning you can simply create
|
||||
/// the reloader and add it to your `ServiceGroup` without having to manually run it.
|
||||
///
|
||||
/// If any errors occur during a reload attempt (such as: being unable to find the file(s) containing the certificate or the key; the format
|
||||
/// not being recognized or not matching the configured one; not being able to verify a certificate's signature against the given
|
||||
|
@ -315,10 +315,7 @@ final class TimedCertificateReloaderTests: XCTestCase {
|
||||
group.addTask {
|
||||
try await reloader.run()
|
||||
}
|
||||
group.addTask {
|
||||
try await body(reloader)
|
||||
}
|
||||
try await group.next()
|
||||
try await body(reloader)
|
||||
group.cancelAll()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user