Skip to content

Commit 9b648e9

Browse files
authored
Merge pull request #12 from dev-dsp/authentication-tokens
enable support of AuthenticationTokens, add Google OAuth Credentials Token Source
2 parents cd2c746 + 699c593 commit 9b648e9

25 files changed

+741
-12
lines changed

pom.xml

+39-12
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,32 @@
4141
<java.level>8</java.level>
4242

4343
<!-- dependency versions -->
44-
<jenkins.version>2.60.3</jenkins.version>
44+
<jenkins.version>2.176.1</jenkins.version>
4545

46-
<slf4j.version>1.7.25</slf4j.version>
46+
<slf4j.version>1.7.26</slf4j.version>
4747

4848
<!-- jenkins plugins versions -->
49-
<jenkins-credentials.version>2.1.7</jenkins-credentials.version>
50-
<jenkins-plain-credentials.version>1.3</jenkins-plain-credentials.version>
49+
<jenkins-structs.version>1.19</jenkins-structs.version>
5150

5251
<!-- maven plugins versions -->
5352
<maven-coveralls.version>4.3.0</maven-coveralls.version>
5453
<maven-jacoco.version>0.8.1</maven-jacoco.version>
54+
<kubernetes-client.version>4.3.0</kubernetes-client.version>
5555

5656
</properties>
5757

5858
<dependencies>
59+
<dependency>
60+
<groupId>org.jenkins-ci.plugins</groupId>
61+
<artifactId>kubernetes-client-api</artifactId>
62+
<version>1.0.0</version>
63+
</dependency>
64+
<dependency>
65+
<groupId>org.jenkins-ci.plugins</groupId>
66+
<artifactId>jackson2-api</artifactId>
67+
<version>2.9.9</version>
68+
</dependency>
69+
5970
<dependency>
6071
<groupId>org.jenkins-ci.plugins</groupId>
6172
<artifactId>apache-httpcomponents-client-4-api</artifactId>
@@ -73,28 +84,44 @@
7384
<dependency>
7485
<groupId>org.jenkins-ci.plugins</groupId>
7586
<artifactId>credentials</artifactId>
76-
<version>${jenkins-credentials.version}</version>
7787
</dependency>
7888
<!-- to use StringCredentialsImpl for tokens -->
7989
<dependency>
8090
<groupId>org.jenkins-ci.plugins</groupId>
8191
<artifactId>plain-credentials</artifactId>
82-
<version>${jenkins-plain-credentials.version}</version>
8392
</dependency>
93+
<dependency>
94+
<groupId>org.jenkins-ci.plugins</groupId>
95+
<artifactId>authentication-tokens</artifactId>
96+
<version>1.3</version>
97+
</dependency>
98+
<dependency>
99+
<groupId>org.jenkins-ci.plugins</groupId>
100+
<artifactId>google-oauth-plugin</artifactId>
101+
<version>0.8</version>
102+
</dependency>
103+
<dependency>
104+
<groupId>org.jenkins-ci.plugins</groupId>
105+
<artifactId>docker-commons</artifactId>
106+
<version>1.14</version>
107+
</dependency>
108+
84109
</dependencies>
85110

86111
<!-- just to fix enforcer RequireUpperBoundDeps -->
87112
<dependencyManagement>
88113
<dependencies>
89114
<dependency>
90-
<groupId>org.jenkins-ci.plugins</groupId>
91-
<artifactId>structs</artifactId>
92-
<version>${jenkins-structs.version}</version>
115+
<groupId>io.jenkins.tools.bom</groupId>
116+
<artifactId>bom</artifactId>
117+
<version>2.176.2</version>
118+
<scope>import</scope>
119+
<type>pom</type>
93120
</dependency>
94121
<dependency>
95-
<groupId>org.jenkins-ci.plugins</groupId>
96-
<artifactId>ssh-credentials</artifactId>
97-
<version>1.12</version>
122+
<groupId>commons-io</groupId>
123+
<artifactId>commons-io</artifactId>
124+
<version>2.6</version>
98125
</dependency>
99126
</dependencies>
100127
</dependencyManagement>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.jenkinsci.plugins.kubernetes.auth;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import io.fabric8.kubernetes.client.ConfigBuilder;
5+
6+
/**
7+
* Abstracts away a Kubernetes authentication either through kubeconfig format or {@link ConfigBuilder}.
8+
*/
9+
public interface KubernetesAuth {
10+
/**
11+
* Decorates a {@link ConfigBuilder} to connect using the current authentication object.
12+
* @param builder the configuration to decorate
13+
* @return the decorated configuration
14+
* @throws KubernetesAuthException if anything fails during the processing of the authentication configuration
15+
*/
16+
ConfigBuilder decorate(ConfigBuilder builder, KubernetesAuthConfig config) throws KubernetesAuthException;
17+
18+
/**
19+
* Builds a kube config file content based on the current authentication object.
20+
*
21+
* @return Kubeconfig file content corresponding to this authentication object.
22+
* @throws JsonProcessingException if something fails while generating the json document for kubeconfig
23+
* @throws KubernetesAuthException if something fails while dealing with credentials
24+
*/
25+
String buildKubeConfig(KubernetesAuthConfig config) throws JsonProcessingException, KubernetesAuthException;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.jenkinsci.plugins.kubernetes.auth;
2+
3+
/**
4+
* Configuration object for {@link KubernetesAuth} operations.
5+
*/
6+
public class KubernetesAuthConfig {
7+
/**
8+
* Server URL of the API endpoint
9+
*/
10+
private final String serverUrl;
11+
/**
12+
* Server certificate
13+
*/
14+
private final String caCertificate;
15+
/**
16+
* Set to true to skip TLS verification
17+
*/
18+
private final boolean skipTlsVerify;
19+
20+
public KubernetesAuthConfig(String serverUrl, String caCertificate, boolean skipTlsVerify) {
21+
this.serverUrl = serverUrl;
22+
this.caCertificate = caCertificate;
23+
this.skipTlsVerify = skipTlsVerify;
24+
}
25+
26+
public String getServerUrl() {
27+
return serverUrl;
28+
}
29+
30+
public String getCaCertificate() {
31+
return caCertificate;
32+
}
33+
34+
public boolean isSkipTlsVerify() {
35+
return skipTlsVerify;
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.jenkinsci.plugins.kubernetes.auth;
2+
3+
public class KubernetesAuthException extends Exception {
4+
public KubernetesAuthException() { super(); }
5+
public KubernetesAuthException(String message) { super(message); }
6+
public KubernetesAuthException(String message, Throwable cause) { super(message, cause); }
7+
public KubernetesAuthException(Throwable cause) { super(cause); }
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.jenkinsci.plugins.kubernetes.auth.impl;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import io.fabric8.kubernetes.api.model.AuthInfoBuilder;
5+
import io.fabric8.kubernetes.api.model.Cluster;
6+
import io.fabric8.kubernetes.client.internal.SerializationUtils;
7+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuth;
8+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthConfig;
9+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthException;
10+
import org.jenkinsci.plugins.kubernetes.credentials.Utils;
11+
12+
abstract class AbstractKubernetesAuth implements KubernetesAuth {
13+
abstract AuthInfoBuilder decorate(AuthInfoBuilder builder, KubernetesAuthConfig config) throws KubernetesAuthException;
14+
15+
public String buildKubeConfig(KubernetesAuthConfig config) throws JsonProcessingException, KubernetesAuthException {
16+
io.fabric8.kubernetes.api.model.ConfigBuilder configBuilder = new io.fabric8.kubernetes.api.model.ConfigBuilder();
17+
// setup cluster
18+
Cluster cluster = new Cluster();
19+
cluster.setServer(config.getServerUrl());
20+
String caCertificate = config.getCaCertificate();
21+
if (caCertificate != null && !caCertificate.isEmpty()) {
22+
cluster.setCertificateAuthorityData(Utils.encodeBase64(Utils.wrapCertificate(caCertificate)));
23+
}
24+
cluster.setInsecureSkipTlsVerify(config.isSkipTlsVerify());
25+
configBuilder
26+
.addNewCluster()
27+
.withName("k8s")
28+
.withCluster(cluster)
29+
.endCluster();
30+
31+
// setup user (class-specific)
32+
configBuilder
33+
.addNewUser()
34+
.withName("cluster-admin")
35+
.withUser(decorate(new AuthInfoBuilder(), config).build())
36+
.endUser();
37+
// setup context
38+
configBuilder
39+
.addNewContext()
40+
.withName("k8s")
41+
.withNewContext()
42+
.withCluster("k8s")
43+
.withUser("cluster-admin")
44+
.endContext()
45+
.endContext();
46+
return SerializationUtils.getMapper().writeValueAsString(configBuilder.build());
47+
}
48+
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.jenkinsci.plugins.kubernetes.auth.impl;
2+
3+
import io.fabric8.kubernetes.api.model.AuthInfoBuilder;
4+
import io.fabric8.kubernetes.client.ConfigBuilder;
5+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuth;
6+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthConfig;
7+
import org.jenkinsci.plugins.kubernetes.credentials.Utils;
8+
9+
public class KubernetesAuthCertificate extends AbstractKubernetesAuth implements KubernetesAuth {
10+
private final String certificate;
11+
12+
private final String key;
13+
14+
public KubernetesAuthCertificate(String certificate, String key) {
15+
this.certificate = certificate;
16+
this.key = key;
17+
}
18+
19+
@Override
20+
public AuthInfoBuilder decorate(AuthInfoBuilder builder, KubernetesAuthConfig config) {
21+
return builder
22+
.withClientCertificateData(Utils.encodeBase64(certificate))
23+
.withClientKeyData(Utils.encodeBase64(key));
24+
}
25+
26+
@Override
27+
public ConfigBuilder decorate(ConfigBuilder builder, KubernetesAuthConfig config) {
28+
return builder
29+
.withClientCertData(Utils.encodeBase64(certificate))
30+
.withClientKeyData(Utils.encodeBase64(key));
31+
}
32+
33+
public String getCertificate() {
34+
return certificate;
35+
}
36+
37+
public String getKey() {
38+
return key;
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.jenkinsci.plugins.kubernetes.auth.impl;
2+
3+
import io.fabric8.kubernetes.api.model.AuthInfoBuilder;
4+
import io.fabric8.kubernetes.client.ConfigBuilder;
5+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuth;
6+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthConfig;
7+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthException;
8+
import org.jenkinsci.plugins.kubernetes.credentials.Utils;
9+
10+
import javax.annotation.Nonnull;
11+
import java.security.Key;
12+
import java.security.KeyStore;
13+
import java.security.KeyStoreException;
14+
import java.security.NoSuchAlgorithmException;
15+
import java.security.UnrecoverableKeyException;
16+
import java.security.cert.CertificateEncodingException;
17+
18+
/**
19+
* Kubernetes authentication using certificate and private key obtained from a keystore with a passphrase.
20+
*/
21+
public class KubernetesAuthKeystore extends AbstractKubernetesAuth implements KubernetesAuth {
22+
private KeyStore keyStore;
23+
24+
private final String passPhrase;
25+
26+
public KubernetesAuthKeystore(@Nonnull KeyStore keyStore, String passPhrase) {
27+
this.keyStore = keyStore;
28+
this.passPhrase = passPhrase;
29+
}
30+
31+
@Override
32+
public AuthInfoBuilder decorate(AuthInfoBuilder builder, KubernetesAuthConfig config) throws KubernetesAuthException {
33+
try {
34+
String alias = keyStore.aliases().nextElement();
35+
// Get private key using passphrase
36+
Key key = keyStore.getKey(alias, passPhrase.toCharArray());
37+
return builder
38+
.withClientCertificateData(Utils.encodeCertificate(keyStore.getCertificate(alias)))
39+
.withClientKeyData(Utils.encodeKey(key));
40+
} catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateEncodingException e) {
41+
throw new KubernetesAuthException(e.getMessage(), e);
42+
}
43+
}
44+
45+
@Override
46+
public ConfigBuilder decorate(ConfigBuilder builder, KubernetesAuthConfig config) throws KubernetesAuthException {
47+
try {
48+
String alias = keyStore.aliases().nextElement();
49+
// Get private key using passphrase
50+
Key key = keyStore.getKey(alias, passPhrase.toCharArray());
51+
return builder
52+
.withClientCertData(Utils.encodeCertificate(keyStore.getCertificate(alias)))
53+
.withClientKeyData(Utils.encodeKey(key));
54+
} catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateEncodingException e) {
55+
throw new KubernetesAuthException(e.getMessage(), e);
56+
}
57+
}
58+
59+
public KeyStore getKeyStore() {
60+
return keyStore;
61+
}
62+
63+
public String getPassPhrase() {
64+
return passPhrase;
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.jenkinsci.plugins.kubernetes.auth.impl;
2+
3+
import io.fabric8.kubernetes.client.ConfigBuilder;
4+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuth;
5+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthConfig;
6+
import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthException;
7+
8+
import java.io.IOException;
9+
10+
/**
11+
* Kubernetes authentication using a raw kubeconfig string.
12+
*/
13+
public class KubernetesAuthKubeconfig implements KubernetesAuth {
14+
private final String kubeconfig;
15+
16+
public KubernetesAuthKubeconfig(String kubeconfig) {
17+
this.kubeconfig = kubeconfig;
18+
}
19+
20+
@Override
21+
public String buildKubeConfig(KubernetesAuthConfig config) {
22+
return getKubeconfig();
23+
}
24+
25+
@Override
26+
public ConfigBuilder decorate(ConfigBuilder builder, KubernetesAuthConfig config) throws KubernetesAuthException {
27+
try {
28+
return new ConfigBuilder(io.fabric8.kubernetes.client.Config.fromKubeconfig(getKubeconfig()));
29+
} catch (IOException e) {
30+
throw new KubernetesAuthException(e);
31+
}
32+
}
33+
34+
public String getKubeconfig() {
35+
return kubeconfig;
36+
}
37+
38+
}

0 commit comments

Comments
 (0)