Skip to content
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

KubernetesClient should use provided SSLContext in config #5316

Closed
AKGarimella opened this issue Jul 6, 2023 · 24 comments · Fixed by #5489
Closed

KubernetesClient should use provided SSLContext in config #5316

AKGarimella opened this issue Jul 6, 2023 · 24 comments · Fixed by #5489
Assignees
Labels
component/kubernetes-client Deals with the kubernetes-client Waiting on feedback Issues that require feedback from User/Other community members
Milestone

Comments

@AKGarimella
Copy link

Is your enhancement related to a problem? Please describe

Fabric8 k8s client library always tries to create an SSLContext by itself using the truststore and keystore related parameters provided using Config object. However, in doing so, there are a lot of assumptions made reg. the runtime viz., format of the store files, whether or not pass phrases should exist, underlying security libraries etc.

In the past when we tried to use fabric8io client in our FIPS env., assuming changeit as the default password caused problem.(See #5126). Though this issue was addressed in later versions, we hit another problem because of PEM vs DER format assumptions made.

It's turning out that basically the fabric8io k8s client is just not compatible with our FIPS compliant JDK runtime.

Describe the solution you'd like

Config object should allow user to pass an SSLContext. When SSLContext is provided in Config, fabric8 k8s client should simply use it AS IS by ignoring all other keystore/truststore related properties.

Config k8sClientConfig = new ConfigBuilder().withSSLContext(mySSLContext).build();

Describe alternatives you've considered

Temporarily, we had to rely on kubernetes.trust.certificates=true to bypass this whole problem.

Additional context

No response

@shawkins
Copy link
Contributor

shawkins commented Jul 7, 2023

Config object should allow user to pass an SSLContext. When SSLContext is provided in Config, fabric8 k8s client should simply use it AS IS by ignoring all other keystore/truststore related properties.

Just the SSLContext may not be enough. At least okhttp wants the trustmanager specified separately. And there may need to be some tweaks to the vertx direct usage of the SSLContext we're passing in as it doesn't seem to fully be working #5170

If the SSLUtils.sslContext method is good enough for your usage, would you be okay with supplying the keymanagers and trustManagers to the KubernetesClientBuilder as a way to by-pass the configuration driven approach?

I realize these may ultimately need to go onto the Config if we want to keep the current Config.ensureHttps functioning the same way in this case.

@AKGarimella
Copy link
Author

AKGarimella commented Jul 10, 2023

@shawkins Thanks for your response. Unfortunately, I won't be able to pass any trustmanager unless I build one explicitly and that's what I have been trying to do. Realized there is nothing I can set that will make it work because of the other ssl properties.

These are our JVM system properties. Apparently we have our own keystore format implemented based on PEM certificates in the underlying JDK.

    "-Djdk.tls.client.protocols=TLSv1.2", \
    "-Djdk.tls.server.protocols=TLSv1.2", \
    "-Djavax.net.ssl.trustStore=/security/cacerts.pem", \
    "-Djavax.net.ssl.trustStoreType=ROTKS", \
    "-Dkubernetes.trust.certificates=true", \

But here's an interesting point. The io.kubernetes works perfectly fine. It's only fabric8io client that is not working without the last -Dkubernetes.trust.certificates=true part, which of course is by passing this entire trust validation path.

@shawkins
Copy link
Contributor

Is the /security/cacerts.pem being picked up from the Config?

Have you tried leaving the system properties javax.net.ssl.trustStore=/security/cacerts.pem and javax.net.ssl.trustStoreType=ROTKS and creating a config without the caCertFile
"new ConfigBuilder().withCaCertFile(null).build()" - without the cacertfile or the cacertdata, that should then use the system default truststore.

we hit another problem because of PEM vs DER format assumptions made.

What specifically is happening there?

@manusa manusa added component/kubernetes-client Deals with the kubernetes-client Waiting on feedback Issues that require feedback from User/Other community members labels Jul 11, 2023
@AKGarimella
Copy link
Author

@shawkins We cannot run without setting javax.net.ssl.trustStore and javax.net.ssl.trustStoreType. Our service won't be able to talk to any other service in our clusters as there is no default truststore in our JDK base image.

@AKGarimella
Copy link
Author

I will get back to you on DER related exception because I have to reproduce that problem.

@shawkins
Copy link
Contributor

@shawkins We cannot run without setting javax.net.ssl.trustStore and javax.net.ssl.trustStoreType. Our service won't be able to talk to any other service in our clusters as there is no default truststore in our JDK base image.

I'm saying leave those system properties set - that makes the /security/cacerts.pem the system default truststore. Then create a KubernetesClient Config that won't autoconfig the truststore: new new ConfigBuilder().withCaCertFile(null).build() - it should pick up the /security/cacerts.pem instead.

@AKGarimella
Copy link
Author

I will try it & get back. Thanks for the suggestion.

@AKGarimella
Copy link
Author

AKGarimella commented Jul 13, 2023

Ok, that's effectively new ConfigBuilder().build(); and that's what we currently have.
With v6.7.2, this is the error that occurs (the DER exception I was talking about).

io.fabric8.kubernetes.client.KubernetesClientException: An error has occurred.
	at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:129) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:122) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.utils.HttpClientUtils.applyCommonConfiguration(HttpClientUtils.java:191) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.okhttp.OkHttpClientFactory.newBuilder(OkHttpClientFactory.java:82) ~[kubernetes-httpclient-okhttp-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.okhttp.OkHttpClientFactory.newBuilder(OkHttpClientFactory.java:29) ~[kubernetes-httpclient-okhttp-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder.getHttpClient(KubernetesClientBuilder.java:90) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder.build(KubernetesClientBuilder.java:79) ~[kubernetes-client-api-6.7.2.jar:?]
	at com.salesforce.carbon.api.k8s.Kubernetes.newKubernetesClient(Kubernetes.java:76) ~[carbon-api-0.0.1-SNAPSHOT.jar:?]
	at com.salesforce.carbon.api.state.ConfigMapAppStateManager.<init>(ConfigMapAppStateManager.java:20) ~[carbon-api-0.0.1-SNAPSHOT.jar:?]
	at com.salesforce.carbon.api.state.ConfigMapAppStateManager.<init>(ConfigMapAppStateManager.java:28) ~[carbon-api-0.0.1-SNAPSHOT.jar:?]
	at com.salesforce.carbon.api.state.AppStateManagerFactory.getAppStateManager(AppStateManagerFactory.java:14) ~[carbon-api-0.0.1-SNAPSHOT.jar:?]
	at com.salesforce.carbon.uniscaler.UniscalerFactory.getUniscaler(UniscalerFactory.java:25) ~[uniscaler.jar:?]
	at com.salesforce.carbon.uniscaler.Main.main(Main.java:19) ~[uniscaler.jar:?]
Caused by: java.security.cert.CertificateException: Unable to initialize, java.io.IOException: Short read of DER length
	at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:197) ~[?:?]
	at sun.security.provider.X509Factory.parseX509orPKCS7Cert(X509Factory.java:482) ~[?:?]
	at sun.security.provider.X509Factory.engineGenerateCertificates(X509Factory.java:364) ~[?:?]
	at java.security.cert.CertificateFactory.generateCertificates(CertificateFactory.java:487) ~[?:?]
	at com.salesforce.security.tks.ReadOnlyPEMTrustStore.engineLoad(ReadOnlyPEMTrustStore.java:129) ~[pemts-1.1.jar:?]
	at java.security.KeyStore.load(KeyStore.java:1487) ~[?:?]
	at io.fabric8.kubernetes.client.internal.CertUtils.createTrustStore(CertUtils.java:89) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.CertUtils.createTrustStore(CertUtils.java:76) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.SSLUtils.trustManagers(SSLUtils.java:169) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.SSLUtils.trustManagers(SSLUtils.java:117) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.utils.HttpClientUtils.applyCommonConfiguration(HttpClientUtils.java:187) ~[kubernetes-client-api-6.7.2.jar:?]
	... 10 more
Caused by: java.io.IOException: Short read of DER length
	at sun.security.util.DerInputStream.getLength(DerInputStream.java:571) ~[?:?]
	at sun.security.util.DerValue.<init>(DerValue.java:257) ~[?:?]
	at sun.security.util.DerInputStream.getDerValue(DerInputStream.java:436) ~[?:?]
	at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1822) ~[?:?]
	at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:194) ~[?:?]
	at sun.security.provider.X509Factory.parseX509orPKCS7Cert(X509Factory.java:482) ~[?:?]
	at sun.security.provider.X509Factory.engineGenerateCertificates(X509Factory.java:364) ~[?:?]
	at java.security.cert.CertificateFactory.generateCertificates(CertificateFactory.java:487) ~[?:?]
	at com.salesforce.security.tks.ReadOnlyPEMTrustStore.engineLoad(ReadOnlyPEMTrustStore.java:129) ~[pemts-1.1.jar:?]
	at java.security.KeyStore.load(KeyStore.java:1487) ~[?:?]
	at io.fabric8.kubernetes.client.internal.CertUtils.createTrustStore(CertUtils.java:89) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.CertUtils.createTrustStore(CertUtils.java:76) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.SSLUtils.trustManagers(SSLUtils.java:169) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.SSLUtils.trustManagers(SSLUtils.java:117) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.utils.HttpClientUtils.applyCommonConfiguration(HttpClientUtils.java:187) ~[kubernetes-client-api-6.7.2.jar:?]
	... 10 more

@shawkins
Copy link
Contributor

Ok, that's effectively new ConfigBuilder().build(); and that's what we currently have.

It should not be. It's picking up a certFile or data from the Config

} else if (Utils.isNotNullOrEmpty(certData) || Utils.isNotNullOrEmpty(certFile)) {
to enter this logic. If both of those are null, then it will simply init which should pickup whatever is set at the VM level. Obviously if you need whatever is in the certFile to make your connection, this won't work regardless.

With v6.7.2, this is the error that occurs (the DER exception I was talking about).

Would it be possible to provide what is coming from the Config? Redacted, if needed values, of the parameters from

return trustManagers(config.getCaCertData(), config.getCaCertFile(), config.isTrustCerts(), config.getTrustStoreFile(),

That will ensure we know exactly what the logic is acting on.

@AKGarimella
Copy link
Author

Tried new ConfigBuilder().withCaCertFile(null).withCaCertData(null).build();. I definitely went past the issues previously originated from SSLUtils. However, fabric8 client fails during server cert validation.

Caused by: java.io.IOException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at io.fabric8.kubernetes.client.dsl.internal.OperationSupport.waitForResult(OperationSupport.java:543) ~[kubernetes-client-6.5.1.jar:?]
	at io.fabric8.kubernetes.client.dsl.internal.OperationSupport.handleResponse(OperationSupport.java:566) ~[kubernetes-client-6.5.1.jar:?]
	at io.fabric8.kubernetes.client.dsl.internal.OperationSupport.handleGet(OperationSupport.java:492) ~[kubernetes-client-6.5.1.jar:?]
	at io.fabric8.kubernetes.client.dsl.internal.BaseOperation.handleGet(BaseOperation.java:745) ~[kubernetes-client-6.5.1.jar:?]
	at io.fabric8.kubernetes.client.dsl.internal.BaseOperation.requireFromServer(BaseOperation.java:186) ~[kubernetes-client-6.5.1.jar:?]
	... 9 more
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.ssl.Alert.createSSLException(Alert.java:131) ~[?:?]
	at sun.security.ssl.TransportContext.fatal(TransportContext.java:353) ~[?:?]
	at sun.security.ssl.TransportContext.fatal(TransportContext.java:296) ~[?:?]
	at sun.security.ssl.TransportContext.fatal(TransportContext.java:291) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:684) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:503) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369) ~[?:?]
	at sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) ~[?:?]
	at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443) ~[?:?]
	at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421) ~[?:?]
	at sun.security.ssl.TransportContext.dispatch(TransportContext.java:183) ~[?:?]
	at sun.security.ssl.SSLTransport.decode(SSLTransport.java:172) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1507) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1417) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:456) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427) ~[?:?]
	at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:379) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517) ~[okhttp-4.10.0.jar:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:443) ~[?:?]
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:309) ~[?:?]
	at sun.security.validator.Validator.validate(Validator.java:270) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:337) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:237) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:137) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:668) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:503) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369) ~[?:?]
	at sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) ~[?:?]
	at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443) ~[?:?]
	at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421) ~[?:?]
	at sun.security.ssl.TransportContext.dispatch(TransportContext.java:183) ~[?:?]
	at sun.security.ssl.SSLTransport.decode(SSLTransport.java:172) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1507) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1417) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:456) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427) ~[?:?]
	at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:379) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517) ~[okhttp-4.10.0.jar:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:140) ~[?:?]
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:125) ~[?:?]
	at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:307) ~[?:?]
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:438) ~[?:?]
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:309) ~[?:?]
	at sun.security.validator.Validator.validate(Validator.java:270) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:337) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:237) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:137) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:668) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:503) ~[?:?]
	at sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369) ~[?:?]
	at sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) ~[?:?]
	at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443) ~[?:?]
	at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421) ~[?:?]
	at sun.security.ssl.TransportContext.dispatch(TransportContext.java:183) ~[?:?]
	at sun.security.ssl.SSLTransport.decode(SSLTransport.java:172) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1507) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1417) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:456) ~[?:?]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427) ~[?:?]
	at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:379) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201) ~[okhttp-4.10.0.jar:?]
	at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517) ~[okhttp-4.10.0.jar:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]

@shawkins
Copy link
Contributor

This confirms you do need the caCert information coming from the Config (which is expected). It should also confirm that the base truststore is loading correctly via just the javax properties.

As mentioned in the last comment, just to make sure we're fully reproducing your scenario - could provide all of the parameter values to the trustManagers call?

@AKGarimella
Copy link
Author

ACK. I will try this and get back.

@AKGarimella
Copy link
Author

AKGarimella commented Jul 20, 2023

By passing the -Dkubernetes.certs.ca.file=/etc/identity/ca/cacerts.pem I am back to the point that made me ask for this feature. :)

CertUtils fails to add the cacerts to truststore file because its a ReadOnlyPEMTrustStore. This is the incompatibility I was referring to, between what the client code assumes vs. our runtime.

io.fabric8.kubernetes.client.KubernetesClientException: An error has occurred.
	at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:129) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:122) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.utils.HttpClientUtils.applyCommonConfiguration(HttpClientUtils.java:191) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.okhttp.OkHttpClientFactory.newBuilder(OkHttpClientFactory.java:82) ~[kubernetes-httpclient-okhttp-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.okhttp.OkHttpClientFactory.newBuilder(OkHttpClientFactory.java:29) ~[kubernetes-httpclient-okhttp-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder.getHttpClient(KubernetesClientBuilder.java:90) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder.build(KubernetesClientBuilder.java:79) ~[kubernetes-client-api-6.7.2.jar:?]
	at com.salesforce.carbon.api.k8s.Kubernetes.newKubernetesClient(Kubernetes.java:74) ~[carbon-api-0.0.1-SNAPSHOT.jar:?]
...
Caused by: java.security.KeyStoreException: Read only trust store
	at com.salesforce.security.tks.ReadOnlyPEMTrustStore.engineSetCertificateEntry(ReadOnlyPEMTrustStore.java:67) ~[pemts-1.1.jar:?]
	at java.security.KeyStore.setCertificateEntry(KeyStore.java:1243) ~[?:?]
	at io.fabric8.kubernetes.client.internal.CertUtils.createTrustStore(CertUtils.java:100) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.CertUtils.createTrustStore(CertUtils.java:76) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.SSLUtils.trustManagers(SSLUtils.java:169) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.internal.SSLUtils.trustManagers(SSLUtils.java:117) ~[kubernetes-client-api-6.7.2.jar:?]
	at io.fabric8.kubernetes.client.utils.HttpClientUtils.applyCommonConfiguration(HttpClientUtils.java:187) ~[kubernetes-client-api-6.7.2.jar:?]
	... 10 more

@AKGarimella
Copy link
Author

I am confused. The implementation doesn't like adding anything to a truststore. But without adding cacerts, the communication with k8s server was failing because of unable to find valid certification path to requested target. I didn't look into the code of io.kubernetes client but how is that able to work & fabric8io client unable to, is still a mystery to me.

@AKGarimella
Copy link
Author

I think I see the problem: https://github.com/fabric8io/kubernetes-client/blob/main/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/internal/SSLUtils.java#L168

If both caCertFile & caCertData are null or empty, its also failing to add trustStoreFile.
As a work around, I am going to try passing a valid file with empty contents under caCertFile.

@shawkins
Copy link
Contributor

Again please provide the parameters being provided to the trustManagers call so that we can understand what all is being picked up by the auto-configuration.

@AKGarimella
Copy link
Author

AKGarimella commented Jul 20, 2023

@shawkins here you go:

trustManagers(certData: null , certFile: cacerts.pem, isTrustCerts: false, trustStoreFile: cacerts.pem, trustStorePassphrase: null)
OR
trustManagers(certData: null , certFile: null, isTrustCerts: false, trustStoreFile: cacerts.pem, trustStorePassphrase: null)
causes:

Caused by: java.security.KeyStoreException: Read only trust store
	at com.salesforce.security.tks.ReadOnlyPEMTrustStore.engineSetCertificateEntry(ReadOnlyPEMTrustStore.java:67) ~[pemts-1.1.jar:?]
	at java.security.KeyStore.setCertificateEntry(KeyStore.java:1243) ~[?:?]

trustManagers(certData: null , certFile: /tmp/empty, isTrustCerts: false, trustStoreFile: cacerts.pem, trustStorePassphrase: null)
causes:

Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:140) ~[?:?]
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:125) ~[?:?]
	at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:307) ~[?:?]
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:438) ~[?:?]
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:309) ~[?:?]
	at sun.security.validator.Validator.validate(Validator.java:270) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:337) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:237) ~[?:?]
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:137) ~[?:?]

@shawkins
Copy link
Contributor

trustManagers(certData: null , certFile: cacerts.pem, isTrustCerts: false, trustStoreFile: cacerts.pem, trustStorePassphrase: null)

Is this what is picked up from just auto-configuration - that is no modification of the Config via withXXX methods? And just to confirm cacerts.pem as both certFile and the trustStoreFile refer to the same file which is also what you have set as javax.net.ssl.trustStore=/security/cacerts.pem ?

trustManagers(certData: null , certFile: null, isTrustCerts: false, trustStoreFile: cacerts.pem, trustStorePassphrase: null)

This is using withCaCertFile(null), correct? I'm confused that it is producing the same exception as the first case, because this should not call createTrustStore. I thought this should produce #5316 (comment) - my initial thought here was that having the appropriate javax.net.ssl.trustStoreType and javax.net.ssl.trustStore system properties set would have caused the existing logic to use that trustStore by default. I'll double check that.

@AKGarimella
Copy link
Author

AKGarimella commented Jul 20, 2023

Is this what is picked up from just auto-configuration - that is no modification of the Config via withXXX methods?

No. I set caCertData(null). Because it tries to add to trustStore and would fail with
Caused by: java.security.KeyStoreException: Read only trust store exception.

And just to confirm cacerts.pem as both certFile and the trustStoreFile refer to the same file which is also what you have set as javax.net.ssl.trustStore=/security/cacerts.pem ?

Yes.

This is using withCaCertFile(null), correct? I'm confused that it is producing the same exception as the first case, because this should not call createTrustStore.

Sorry for the confusion. I mistyped setting caCertData as null as method param. caCertData wouldn't be null and hence the exception.
Corrected API call:

trustManagers(certData: <autoconfigured> , certFile: null, isTrustCerts: false, trustStoreFile: cacerts.pem, trustStorePassphrase: null)

@AKGarimella
Copy link
Author

In summary,

  • new ConfigBuilder().build() will autoload caCertData causing Read only trust store error.
  • new ConfigBuilder().withCaCertFile(cacerts.pem)...build() will also cause Read only trust store error.
  • new ConfigBuilder().withCaCertData(null).withCaCertFile(null)..build() will cause unable to find valid certification path to requested target which apparently is obvious (took me a while to understand what's going on internally).

@shawkins
Copy link
Contributor

shawkins commented Jul 21, 2023

new ConfigBuilder().build() will autoload caCertData causing Read only trust store error.

Somewhere via the env or the kube config the auto configuration is picking up that there is BOTH a caCert and a trustStore that is the same file - that seems wrong. Depending on what certs it holds your cacerts.pem should be seen as one or the other, but not both. Are you setting the kubernetes.truststore.file env / system property?

Next when the logic sees that there is a caCert it tries to create a merged trustStore - but that is failing due to the custom ROTKS keystore type (that should be picked up from your javax.net.ssl.trustStoreType system property). It seems like the logic could account for this by creating yet another trustStore, such as jks, and then put all the certs into that. But I'm not sure yet if that change is necessary.

new ConfigBuilder().withCaCertData(null).withCaCertFile(null)..build() will cause unable to find valid certification path to requested target which apparently is obvious (took me a while to understand what's going on internally).

In this scenario did you leave the system properties javax.net.ssl.trustStore and javax.net.ssl.trustStoreType set? I can confirm locally my expectation that this case will try to load the trust store by default from those properties. Please also set javax.net.debug=all to see what is happening.

If you can see that the javax.net.ssl.trustStore is being used, then the problem is that you are supposed to be picking up another caCert file from the Config - like the one that would be associated with the serviceaccount - /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - but the usage of other env / system properties is causing it not to be picked up.

@AKGarimella
Copy link
Author

Do you think if I tried with .withTrustStoreFile(null), it would create a new in mem truststore with just cacerts added to it & that might work?

@shawkins
Copy link
Contributor

Corrected my last comment to be the kubernetes.truststore.file property.

Do you think if I tried with .withTrustStoreFile(null), it would create a new in mem truststore with just cacerts added to it & that might work?

The issue is what you are doing with other system / env properties. In this case if you don't configure the fabric8 kubernetes client with an explicit trustStore (by not setting kubernetes.truststore.file or by using withTrustStore(null)), it will look for the default via the javax system properties - as long as you have set the truststore type to ROTKS that won't work.

I think you need to take a step back and properly identify what trustStore(s) you have in play and what are their purpose. Also for any scenario discussed you should also include all relevant system / env properties. I'm having a hard time following why the auto configuration is picking up the values that it is, because it does not seem to match what you showed in #5316 (comment)

shawkins added a commit to shawkins/kubernetes-client that referenced this issue Sep 29, 2023
@shawkins
Copy link
Contributor

@AKGarimella I have reviewed this issue again and believe the base case of what you are doing - that is have the system default truststore as read-only is something that we can support. I don't see a good workaround for you unless you take responsbility for creating a writable store type containing the ROTKS store entries. There is now a pr for this #5489

@shawkins shawkins self-assigned this Sep 29, 2023
manusa pushed a commit to shawkins/kubernetes-client that referenced this issue Oct 4, 2023
@manusa manusa added this to the 6.9.0 milestone Oct 4, 2023
shawkins added a commit to shawkins/kubernetes-client that referenced this issue Oct 4, 2023
manusa pushed a commit that referenced this issue Oct 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/kubernetes-client Deals with the kubernetes-client Waiting on feedback Issues that require feedback from User/Other community members
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants