-
Notifications
You must be signed in to change notification settings - Fork 58
Support custom trust stores for https #295
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
Changes from all commits
4e4f014
eb93d75
37095bc
f3ab9e7
b5da6a7
206832b
44c4212
b60f6b0
0f6a39e
77bbe75
a20ce63
0d21144
6639b09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| /* | ||
| * Copyright 2023 Aiven Oy and http-connector-for-apache-kafka project contributors | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| package io.aiven.kafka.connect.http.sender; | ||
|
|
||
| import javax.net.ssl.SSLContext; | ||
| import javax.net.ssl.SSLEngine; | ||
| import javax.net.ssl.TrustManager; | ||
| import javax.net.ssl.TrustManagerFactory; | ||
| import javax.net.ssl.X509ExtendedTrustManager; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.net.Socket; | ||
| import java.security.KeyManagementException; | ||
| import java.security.KeyStore; | ||
| import java.security.KeyStoreException; | ||
| import java.security.NoSuchAlgorithmException; | ||
| import java.security.SecureRandom; | ||
| import java.security.cert.CertificateException; | ||
| import java.security.cert.X509Certificate; | ||
|
|
||
| import io.aiven.kafka.connect.http.config.HttpSinkConfig; | ||
|
|
||
| final class SslContextBuilder { | ||
|
|
||
| private static final TrustManager DUMMY_TRUST_MANAGER = new X509ExtendedTrustManager() { | ||
| @Override | ||
| public void checkClientTrusted(final X509Certificate[] chain, final String authType, final Socket socket) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public void checkServerTrusted(final X509Certificate[] chain, final String authType, final Socket socket) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public void checkClientTrusted(final X509Certificate[] chain, final String authType, final SSLEngine engine) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public void checkServerTrusted(final X509Certificate[] chain, final String authType, final SSLEngine engine) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public java.security.cert.X509Certificate[] getAcceptedIssuers() { | ||
| return new java.security.cert.X509Certificate[0]; | ||
| } | ||
|
|
||
| @Override | ||
| public void checkClientTrusted(final X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| } | ||
|
|
||
| @Override | ||
| public void checkServerTrusted(final java.security.cert.X509Certificate[] chain, final String authType) | ||
| throws CertificateException { | ||
| } | ||
| }; | ||
|
|
||
| static SSLContext createSslContext(final HttpSinkConfig config) | ||
| throws NoSuchAlgorithmException, KeyManagementException { | ||
| final SSLContext sslContext = SSLContext.getInstance("TLS"); | ||
| if (config.sslTrustAllCertificates()) { | ||
| sslContext.init(null, new TrustManager[] {DUMMY_TRUST_MANAGER}, new SecureRandom()); | ||
| } else { | ||
| final TrustManagerFactory tmf = loadTrustStore(config); | ||
| sslContext.init(null, tmf != null ? tmf.getTrustManagers() : null, new SecureRandom()); | ||
| } | ||
| return sslContext; | ||
| } | ||
|
|
||
| private static TrustManagerFactory loadTrustStore(final HttpSinkConfig config) { | ||
| if (config.sslTrustStoreLocation() == null) { | ||
| return null; | ||
| } | ||
| try { | ||
| final KeyStore trustStore = KeyStore.getInstance("JKS"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be a good idea to also add a config for the keystore type?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but jks meets my need. I can think of adding other types later if I have the time, but it would be good to not hold this off until other support is available. Let me know what you think. if you agree, I can update teh documentation to make it clear that only JKS is supported for now.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's probably fine to not add the keystore type immediately -- IIUC, adding the truststore here is just a mechanism to create an allow list of acceptable HTTPS servers (not to verify that the client is acceptable). This is by far the most frequent usage (e.g., when my browser contacts https://github.com, the server never checks who I am).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. This is only for the client to validate the server. This is not a mechanism for the server to check who is connecting. But makes me think if mTLS might be a future feature request. Anyway, that is out of scope for this PR. |
||
| final String path = config.sslTrustStoreLocation(); | ||
|
|
||
| try (InputStream is = TrustStoreLoader.findTrustStoreInputStream(path)) { | ||
| if (is == null) { | ||
| throw new RuntimeException("TrustStore file not found: " + path | ||
| + ". Tried classpath and file system locations."); | ||
| } | ||
| trustStore.load(is, config.sslTrustStorePassword() != null | ||
| ? config.sslTrustStorePassword().toCharArray() : null); | ||
| } | ||
| final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); | ||
| tmf.init(trustStore); | ||
| return tmf; | ||
| } catch (KeyStoreException | ||
| | IOException | ||
| | NoSuchAlgorithmException | ||
| | CertificateException e) { | ||
| throw new RuntimeException("Failed to load truststore: " + config.sslTrustStoreLocation(), e); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.