mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-05-15 01:18:58 +08:00
98 lines
4.1 KiB
Swift
98 lines
4.1 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2025 Apple Inc. and the SwiftNIO project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import NIOCore
|
|
import NIOSSL
|
|
|
|
/// A protocol that defines a certificate reloader.
|
|
///
|
|
/// A certificate reloader is a service that can provide you with updated versions of a certificate and private key pair, in
|
|
/// 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.
|
|
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``
|
|
/// implementation provides.
|
|
var sslContextConfigurationOverride: NIOSSLContextConfigurationOverride { get }
|
|
}
|
|
|
|
extension TLSConfiguration {
|
|
/// Errors thrown when creating a ``NIOSSL/TLSConfiguration`` with a ``CertificateReloader``.
|
|
public struct CertificateReloaderError: Error, Hashable, CustomStringConvertible {
|
|
private enum _Backing: CustomStringConvertible {
|
|
case missingCertificateChain
|
|
case missingPrivateKey
|
|
|
|
var description: String {
|
|
switch self {
|
|
case .missingCertificateChain:
|
|
return "Missing certificate chain"
|
|
case .missingPrivateKey:
|
|
return "Missing private key"
|
|
}
|
|
}
|
|
}
|
|
|
|
private let _backing: _Backing
|
|
|
|
private init(backing: _Backing) {
|
|
self._backing = backing
|
|
}
|
|
|
|
public var description: String {
|
|
self._backing.description
|
|
}
|
|
|
|
/// The given ``CertificateReloader`` could not provide a certificate chain with which to create this config.
|
|
public static var missingCertificateChain: Self { .init(backing: .missingCertificateChain) }
|
|
|
|
/// The given ``CertificateReloader`` could not provide a private key with which to create this config.
|
|
public static var 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
|
|
)
|
|
configuration.setCertificateReloader(certificateReloader)
|
|
return configuration
|
|
}
|
|
|
|
/// 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.
|
|
mutating public func setCertificateReloader(_ reloader: some CertificateReloader) {
|
|
self.sslContextCallback = { _, promise in
|
|
promise.succeed(reloader.sslContextConfigurationOverride)
|
|
}
|
|
}
|
|
}
|