package org.csanchez.jenkins.plugins.kubernetes; import com.cloudbees.plugins.credentials.CredentialsMatchers; import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.common.StandardCertificateCredentials; import com.cloudbees.plugins.credentials.common.StandardCredentials; import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials; import com.cloudbees.plugins.credentials.domains.DomainRequirement; import hudson.security.ACL; import hudson.util.Secret; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; import jenkins.model.Jenkins; import org.apache.commons.codec.binary.Base64; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.concurrent.TimeUnit; import javax.annotation.CheckForNull; /** * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a> */ public class KubernetesFactoryAdapter { private static final int DEFAULT_CONNECT_TIMEOUT = 5; private static final int DEFAULT_READ_TIMEOUT = 15; private final String serviceAddress; private final String namespace; @CheckForNull private final String caCertData; @CheckForNull private final StandardCredentials credentials; private final boolean skipTlsVerify; private final int connectTimeout; private final int readTimeout; public KubernetesFactoryAdapter(String serviceAddress, @CheckForNull String caCertData, @CheckForNull String credentials, boolean skipTlsVerify) { this(serviceAddress, null, caCertData, credentials, skipTlsVerify); } public KubernetesFactoryAdapter(String serviceAddress, String namespace, @CheckForNull String caCertData, @CheckForNull String credentials, boolean skipTlsVerify) { this(serviceAddress, namespace, caCertData, credentials, skipTlsVerify, DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT); } public KubernetesFactoryAdapter(String serviceAddress, String namespace, @CheckForNull String caCertData, @CheckForNull String credentials, boolean skipTlsVerify, int connectTimeout, int readTimeout) { this.serviceAddress = serviceAddress; this.namespace = namespace; this.caCertData = caCertData; this.credentials = credentials != null ? getCredentials(credentials) : null; this.skipTlsVerify = skipTlsVerify; this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; } private StandardCredentials getCredentials(String credentials) { return CredentialsMatchers.firstOrNull( CredentialsProvider.lookupCredentials(StandardCredentials.class, Jenkins.getInstance(), ACL.SYSTEM, Collections.<DomainRequirement>emptyList()), CredentialsMatchers.withId(credentials) ); } public KubernetesClient createClient() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateEncodingException { ConfigBuilder builder = new ConfigBuilder().withMasterUrl(serviceAddress) .withRequestTimeout(readTimeout * 1000) .withConnectionTimeout(connectTimeout * 1000); if (namespace != null && !namespace.isEmpty()) { builder.withNamespace(namespace); } if (credentials != null) { if (credentials instanceof TokenProducer) { final String token = ((TokenProducer)credentials).getToken(serviceAddress, caCertData, skipTlsVerify); builder.withOauthToken(token); } else if (credentials instanceof UsernamePasswordCredentials) { UsernamePasswordCredentials usernamePassword = (UsernamePasswordCredentials) credentials; builder.withUsername(usernamePassword.getUsername()).withPassword(Secret.toString(usernamePassword.getPassword())); } else if (credentials instanceof StandardCertificateCredentials) { StandardCertificateCredentials certificateCredentials = (StandardCertificateCredentials) credentials; KeyStore keyStore = certificateCredentials.getKeyStore(); String alias = keyStore.aliases().nextElement(); X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias); Key key = keyStore.getKey(alias, Secret.toString(certificateCredentials.getPassword()).toCharArray()); builder.withClientCertData(Base64.encodeBase64String(certificate.getEncoded())) .withClientKeyData(pemEncodeKey(key)) .withClientKeyPassphrase(Secret.toString(certificateCredentials.getPassword())); } } if (skipTlsVerify) { builder.withTrustCerts(true); } if (caCertData != null) { builder.withCaCertData(caCertData); } return new DefaultKubernetesClient(builder.build()); } private static String pemEncodeKey(Key key) { return Base64.encodeBase64String(new StringBuilder() // .append("-----BEGIN PRIVATE KEY-----\n") // .append(Base64.encodeBase64String(key.getEncoded())) // .append("\n-----END PRIVATE KEY-----\n") // .toString().getBytes(StandardCharsets.UTF_8)); } }