Skip to content

Commit 855d5f5

Browse files
committed
use IDEA certificate manager when connecting to the cluster (#600)
Signed-off-by: Andre Dietisheim <[email protected]>
1 parent 2c16a91 commit 855d5f5

File tree

2 files changed

+82
-6
lines changed

2 files changed

+82
-6
lines changed

build.gradle

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ dependencies {
6060
"io.fabric8:kubernetes-model-common:${kubernetesClientVersion}",
6161
"io.fabric8:openshift-client:${kubernetesClientVersion}",
6262
"io.fabric8:kubernetes-httpclient-okhttp:${kubernetesClientVersion}",
63-
"org.apache.commons:commons-lang3:3.12.0"
63+
"org.apache.commons:commons-lang3:3.12.0",
64+
"io.github.hakky54:sslcontext-kickstart:8.0.0"
6465
)
6566
testImplementation(
6667
"org.assertj:assertj-core:3.22.0",

src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ClientAdapter.kt

+80-5
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,29 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.kubernetes.model.client
1212

13+
import com.intellij.openapi.diagnostic.logger
1314
import com.intellij.util.net.ssl.CertificateManager
15+
import com.intellij.util.net.ssl.ConfirmingTrustManager
1416
import com.redhat.devtools.intellij.kubernetes.model.util.isUnauthorized
1517
import io.fabric8.kubernetes.client.Client
1618
import io.fabric8.kubernetes.client.Config
1719
import io.fabric8.kubernetes.client.KubernetesClient
1820
import io.fabric8.kubernetes.client.KubernetesClientBuilder
1921
import io.fabric8.kubernetes.client.KubernetesClientException
22+
import io.fabric8.kubernetes.client.http.HttpClient
2023
import io.fabric8.kubernetes.client.impl.AppsAPIGroupClient
2124
import io.fabric8.kubernetes.client.impl.BatchAPIGroupClient
2225
import io.fabric8.kubernetes.client.impl.NetworkAPIGroupClient
2326
import io.fabric8.kubernetes.client.impl.StorageAPIGroupClient
27+
import io.fabric8.kubernetes.client.internal.SSLUtils
2428
import io.fabric8.openshift.client.NamespacedOpenShiftClient
2529
import io.fabric8.openshift.client.OpenShiftClient
2630
import java.util.concurrent.ConcurrentHashMap
31+
import javax.net.ssl.X509ExtendedTrustManager
32+
import javax.net.ssl.X509TrustManager
33+
import nl.altindag.ssl.trustmanager.CompositeX509ExtendedTrustManager
34+
import nl.altindag.ssl.util.TrustManagerUtils
35+
import org.apache.commons.lang3.reflect.FieldUtils
2736

2837
open class OSClientAdapter(client: OpenShiftClient, private val kubeClient: KubernetesClient) :
2938
ClientAdapter<OpenShiftClient>(client) {
@@ -55,14 +64,16 @@ abstract class ClientAdapter<C: KubernetesClient>(private val fabric8Client: C)
5564
companion object Factory {
5665
fun create(namespace: String? = null, context: String? = null): ClientAdapter<out KubernetesClient> {
5766
val config = Config.autoConfigure(context)
58-
setAcceptCertificates(config)
5967
return create(namespace, config)
6068
}
6169

6270
fun create(namespace: String? = null, config: Config): ClientAdapter<out KubernetesClient> {
6371
setNamespace(namespace, config)
6472
val kubeClient = KubernetesClientBuilder()
6573
.withConfig(config)
74+
.withHttpClientBuilderConsumer { builder ->
75+
setSslContext(builder, config)
76+
}
6677
.build()
6778
val osClient = kubeClient.adapt(NamespacedOpenShiftClient::class.java)
6879
val isOpenShift = isOpenShift(osClient)
@@ -73,10 +84,74 @@ abstract class ClientAdapter<C: KubernetesClient>(private val fabric8Client: C)
7384
}
7485
}
7586

76-
private fun setAcceptCertificates(config: Config) {
77-
val manager = CertificateManager.getInstance().state;
78-
config.isTrustCerts = manager.ACCEPT_AUTOMATICALLY
79-
config.isDisableHostnameVerification = manager.ACCEPT_AUTOMATICALLY
87+
private fun setSslContext(builder: HttpClient.Builder, config: Config) {
88+
val clientTrustManagers = SSLUtils.trustManagers(config)
89+
.filterIsInstance<X509ExtendedTrustManager>()
90+
.toTypedArray()
91+
val ideTrustManager = configureIdeTrustManager(clientTrustManagers)
92+
builder.sslContext(SSLUtils.keyManagers(config), arrayOf(ideTrustManager))
93+
}
94+
95+
private fun configureIdeTrustManager(clientTrustManagers: Array<X509ExtendedTrustManager>): ConfirmingTrustManager {
96+
val ideTrustManager = CertificateManager.getInstance().trustManager
97+
try {
98+
// < IC-2022.2
99+
if (!setCompositeManager(clientTrustManagers, ideTrustManager)) {
100+
// >= IC-2022.2
101+
addCompositeManager(clientTrustManagers, ideTrustManager)
102+
}
103+
} catch (e: RuntimeException) {
104+
logger<ClientAdapter<*>>().warn("Could not configure IDEA trust manager.", e)
105+
}
106+
return ideTrustManager
107+
}
108+
109+
private fun setCompositeManager(
110+
clientTrustManagers: Array<X509ExtendedTrustManager>,
111+
ideTrustManager: ConfirmingTrustManager
112+
): Boolean {
113+
val systemManagerField = FieldUtils.getDeclaredField(
114+
ideTrustManager::class.java, "mySystemManager", true) ?: return false
115+
val systemManager = systemManagerField.get(ideTrustManager) as? X509ExtendedTrustManager ?: return false
116+
val compositeTrustManager = createCompositeTrustManager(systemManager, clientTrustManagers)
117+
systemManagerField.set(ideTrustManager, compositeTrustManager)
118+
return true
119+
}
120+
121+
private fun addCompositeManager(
122+
clientTrustManagers: Array<X509ExtendedTrustManager>,
123+
ideTrustManager: ConfirmingTrustManager
124+
) {
125+
val systemManagersField =
126+
FieldUtils.getDeclaredField(ideTrustManager::class.java, "mySystemManagers", true)
127+
val managers =
128+
systemManagersField.get(ideTrustManager) as? MutableList<X509TrustManager> ?: return
129+
val nonCompositeManagers = managers.filter { it !is CompositeX509ExtendedTrustManager }
130+
val clientTrustManager = CompositeX509ExtendedTrustManager(clientTrustManagers.asList())
131+
managers.clear()
132+
managers.addAll(nonCompositeManagers)
133+
managers.add(clientTrustManager)
134+
}
135+
136+
private fun createCompositeTrustManager(
137+
systemManager: X509ExtendedTrustManager,
138+
clientTrustManagers: Array<X509ExtendedTrustManager>
139+
): X509ExtendedTrustManager {
140+
val compositeTrustManager = if (systemManager is CompositeX509ExtendedTrustManager
141+
) {
142+
// already patched CertificateManager, re-create composite manager
143+
TrustManagerUtils.trustManagerBuilder()
144+
.withTrustManager(systemManager.innerTrustManagers[0])
145+
.withTrustManagers(*clientTrustManagers)
146+
.build()
147+
} else {
148+
// 1st time we patch CertificateManager, create composite manager
149+
TrustManagerUtils.trustManagerBuilder()
150+
.withTrustManager(systemManager)
151+
.withTrustManagers(*clientTrustManagers)
152+
.build()
153+
}
154+
return compositeTrustManager
80155
}
81156

82157
private fun isOpenShift(osClient: NamespacedOpenShiftClient): Boolean {

0 commit comments

Comments
 (0)