@@ -10,21 +10,30 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
10
10
import com.jayway.jsonpath.JsonPath
11
11
import eu.rigeldev.kuberig.core.generation.yaml.ByteArrayDeserializer
12
12
import eu.rigeldev.kuberig.core.generation.yaml.ByteArraySerializer
13
+ import org.apache.http.conn.ssl.TrustStrategy
14
+ import org.apache.http.ssl.SSLContexts
13
15
import org.bouncycastle.jce.provider.BouncyCastleProvider
14
16
import org.bouncycastle.openssl.PEMKeyPair
15
17
import org.bouncycastle.openssl.PEMParser
16
18
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
19
+ import org.bouncycastle.openssl.jcajce.JcaPEMWriter
17
20
import org.json.JSONObject
18
21
import org.slf4j.LoggerFactory
19
22
import java.io.File
20
23
import java.io.IOException
24
+ import java.io.StringWriter
25
+ import java.net.URL
21
26
import java.security.KeyStore
22
27
import java.security.Security
23
28
import java.security.cert.CertificateFactory
24
29
import java.security.cert.X509Certificate
25
30
import java.time.Instant
26
31
import java.util.*
27
32
import java.util.concurrent.TimeUnit
33
+ import javax.net.ssl.SSLException
34
+ import javax.net.ssl.SSLSocket
35
+ import javax.net.ssl.TrustManagerFactory
36
+ import javax.net.ssl.X509TrustManager
28
37
29
38
30
39
/* *
@@ -182,18 +191,17 @@ class KubectlConfigReader {
182
191
if (currentNode.get(" name" ).textValue() == clusterName) {
183
192
val clusterNode = currentNode.get(" cluster" )
184
193
194
+ val server = clusterNode.get(" server" ).textValue()
195
+
185
196
val certificateAuthorityData = if (clusterNode.has(" certificate-authority" )) {
186
197
File (clusterNode.get(" certificate-authority" ).textValue()).readText()
187
198
} else if (clusterNode.has(" certificate-authority-data" )) {
188
199
String (Base64 .getDecoder().decode(clusterNode.get(" certificate-authority-data" ).textValue()))
189
200
} else {
190
- " "
201
+ retrieveCertificateAuthorityDataFromServer(server)
191
202
}
192
203
193
- clusterDetail = ClusterDetail (
194
- certificateAuthorityData,
195
- clusterNode.get(" server" ).textValue()
196
- )
204
+ clusterDetail = ClusterDetail (certificateAuthorityData, server)
197
205
}
198
206
199
207
currentIndex++
@@ -308,6 +316,66 @@ class KubectlConfigReader {
308
316
}
309
317
}
310
318
319
+ /* *
320
+ * Not completely happy with this part as it does not yet go up the certificate chain.
321
+ */
322
+ private fun retrieveCertificateAuthorityDataFromServer (url : String ): String {
323
+ println (" Failing back to retrieve certificate from $url , we may not get the CA but only the server certificate in this case." )
324
+
325
+ var result : String? = null
326
+
327
+ val tm = SavingTrustManager ()
328
+
329
+ val context = SSLContexts .custom()
330
+ .loadTrustMaterial(null , tm)
331
+ .build()
332
+
333
+ val urlObject = URL (url)
334
+
335
+ val factory = context.socketFactory
336
+ val socket = factory.createSocket(urlObject.host, urlObject.port) as SSLSocket
337
+ socket.soTimeout = 10000
338
+ try {
339
+ socket.use {
340
+ it.startHandshake()
341
+ }
342
+ }
343
+ catch (e: SSLException ) {
344
+ // ignore
345
+ }
346
+
347
+ if (tm.chain == null ) {
348
+ println (" Could not obtain server certificate chain" )
349
+ } else {
350
+ val certList = tm.chain!! .asList()
351
+ if (certList.isNotEmpty()) {
352
+ // We should improve here and actually detect what the most top level certificate is.
353
+ val certificate = certList[0 ]
354
+ println (" Using ${certificate.subjectDN} " )
355
+ println (" Issued by ${certificate.issuerDN} " )
356
+ val stringWriter = StringWriter ()
357
+ JcaPEMWriter (stringWriter).use { pemWriter ->
358
+ pemWriter.writeObject(certificate)
359
+ }
360
+ result = stringWriter.toString()
361
+ }
362
+ }
363
+
364
+ check(result != null ) { " Failed to retrieve Certificate from $url " }
365
+
366
+ return result
367
+ }
368
+
369
+ }
370
+
371
+ class SavingTrustManager : TrustStrategy {
372
+
373
+ var chain: Array <out X509Certificate >? = null
374
+
375
+ override fun isTrusted (chain : Array <out X509Certificate >? , authType : String? ): Boolean {
376
+ this .chain = chain
377
+ return false
378
+ }
311
379
}
312
380
313
381
data class ContextDetail (val clusterName : String , val userName : String , val namespace : String = " " )
0 commit comments