-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add support to CRT HTTP Client for providing custom trust and key store #672
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor suggestions
Sources/ClientRuntime/Networking/Http/HttpClientConfiguration.swift
Outdated
Show resolved
Hide resolved
Sources/ClientRuntime/Networking/Http/HttpClientConfiguration.swift
Outdated
Show resolved
Hide resolved
Sources/ClientRuntime/Networking/Http/URLSession/URLSessionHTTPClient.swift
Outdated
Show resolved
Hide resolved
Sources/ClientRuntime/Networking/Http/URLSession/URLSessionHTTPClient.swift
Outdated
Show resolved
Hide resolved
/// Custom TLS configuration for HTTPS connections. | ||
/// | ||
/// Enables specifying client certificates and trust stores for secure communication. | ||
/// Defaults to system's TLS settings if `nil`. | ||
public var tlsContext: TLSContext? | ||
|
||
/// Custom TLS configuration for HTTPS connections with URLSession client. | ||
/// | ||
/// Enables specifying client certificates and trust stores for secure communication. | ||
/// Defaults to system's TLS settings if `nil`. | ||
public var urlSessionTLSOptions: URLSessionTLSOptions? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is the public API, Is it possible to have one TLSContext class and create either a URLSessionTLSOptions or a TLSContext internally from it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be better to just expose a wrapper to TLSContextOptions and create a TLSContext from that. No one should be passing server mode to this TLSContext.init function.
/// Information is provided to use custom trust store | ||
public var useSelfSignedCertificate: Bool | ||
|
||
/// Information is provided to use custom key store | ||
public var useProvidedKeystore: Bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are derived variables, but someone can modify them after the init function. Is that intended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change so they cant modify
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should any of these properties be modifiable? Generally configuration objects are immutable once created.
|
||
let options = [kSecImportExportPassphrase as String: password] as CFDictionary | ||
var items: CFArray? | ||
let status = SecPKCS12Import(p12Data as CFData, options, &items) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SecPKCS12Import
: The latest aws-crt-swift
disabled the API for iOS, tvOS, and watchOS, as we dont really have a way to test the API on those platforms. Would you have a test plan to the feature with pkcs12 key on the platforms? Maybe I could add the tests to CRT as well and enable the API there.
awslabs/aws-crt-swift#246 <= PR that disabled SecPKCS12Import
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree there's no easy way to test on those platforms natively (would work via simulator for dev testing), I manually generated the p12 file using a combination of keychain access and openssl 1.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See individual comments. Also it seems that we have some HTTP client config that is specific to only one client. Short of clearly documenting what affects what, is there a way that we can unify settings so that one setting affects both clients?
if try serverTrust.evaluateAllowing(rootCertificates: [customRoot]) { | ||
completionHandler(.useCredential, URLCredential(trust: serverTrust)) | ||
} else { | ||
logger.debug("Trust evaluation failed, cancelling authentication challenge.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be error
log level?
/// Information is provided to use custom trust store | ||
public var useSelfSignedCertificate: Bool | ||
|
||
/// Information is provided to use custom key store | ||
public var useProvidedKeystore: Bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should any of these properties be modifiable? Generally configuration objects are immutable once created.
var error: CFError? | ||
let evaluationSucceeded = SecTrustEvaluateWithError(self, &error) | ||
guard evaluationSucceeded else { | ||
print(error.debugDescription) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eliminate the print statement, or change it to a log statement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, should error be unwrapped here? Seems odd to log & throw an error you're not sure is there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eliminating print statement, the error has to be there as part of description of SecTrustEvaluateWithError. If result is false, error will be populated with reason, if true error will be null
Sources/ClientRuntime/Networking/Http/URLSession/URLSessionHTTPClient.swift
Outdated
Show resolved
Hide resolved
// Set the custom root certificates as trusted anchors. | ||
let status = SecTrustSetAnchorCertificates(self, rootCertificates as CFArray) | ||
guard status == errSecSuccess else { | ||
throw TrustEvaluationError.evaluationFailed(error: nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The actual error here is that setting anchor certificates failed? Should the error we throw say that more explicitly?
@@ -330,4 +425,71 @@ public enum URLSessionHTTPClientError: Error { | |||
case unresumedConnection | |||
} | |||
|
|||
extension Bundle { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move these extensions to a separate source file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The URLSessionHTTPClient
is getting big
Sources/ClientRuntime/Networking/Http/URLSession/URLSessionHTTPClient.swift
Show resolved
Hide resolved
self.crtTLSContext = nil | ||
|
||
// Set CRT client tls options with .client mode | ||
if let _crtTLSOptions = crtTLSOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why unwrap to a local var with an underscore?
if let crtTLSOptions {
will unwrap the optional then "shadow" it inside the if
.
@@ -39,13 +39,15 @@ public class CRTClientEngine: HTTPClient { | |||
private var http2ConnectionPools: [ConnectionPoolID: HTTP2StreamManager] = [:] | |||
private let sharedDefaultIO = SDKDefaultIO.shared | |||
private let connectTimeoutMs: UInt32? | |||
private let tlsContext: TLSContext? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is TLSContext
a CRT type? If so, let's not expose it directly to customers. CRT is an "implementation detail" of the AWS SDK for Swift that should remain concealed from customers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change this to CRTClientTLSOptions type that I created and provide a resolveContext() method on it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can also rename that if we want to get rid of CRT from it
// - The `as? SecIdentity` casting causes a compiler error on Apple platforms as the cast is guaranteed. | ||
// - Directly returning `identity` works on Linux but not on macOS due to strict type expectations. | ||
// SwiftLint is temporarily disabled for the next line to allow a force cast, acknowledging the platform-specific behavior. | ||
// swiftlint:disable:next force_cast |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually Swift Foundation will eliminate these differences, but for now we're stuck with swift-corelibs-foundation
on Linux which is not always faithful to the original.
self.keyStorePassword = keyStorePassword | ||
|
||
self.useSelfSignedCertificate = certificateFile != nil | ||
self.useProvidedKeystore = keyStoreName != nil && keyStorePassword != nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can this be a computed property instead of stored?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same with the property above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the swiftlint issue before merging
Issue #
#1110
Description of changes
Example Usage with STSClient on Apple devices:
Example Usage with STSClient on Linux:
Directions for testing URLSession changes:
Step 1: Downgrade Openssl on mac from 3.x to 1.1. Mac's Keychain Access and XCode are not compatible with the latest openssl encryption algorithms
Step 2. Generate a key (.pem) and a certificate (.cer) and then include both in a keystore (.p12)
Step 3. Include both the certificate file (.cer) and keystore (.p12) in the project's bundle
Step 4. Create a local test server. You can use mine as an exmaple and run it using
node server.js
:Step 5. Run XCode proj that hits the server. Here is my example code:
Scope
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.