Skip to content

Commit 71b1a82

Browse files
authored
Merge pull request #50 from jmdesprez/JENKINS-73525
2 parents 8ea0029 + 3f7ec97 commit 71b1a82

18 files changed

+581
-5
lines changed

pom.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@
4242
<gitHubRepo>jenkinsci/${project.artifactId}-plugin</gitHubRepo>
4343

4444
<!-- jenkins versions -->
45-
<jenkins.version>2.401.3</jenkins.version>
46-
<bom.artifactId>bom-2.401.x</bom.artifactId>
47-
<bom.version>2745.vc7b_fe4c876fa_</bom.version>
45+
<jenkins.version>2.426.3</jenkins.version>
46+
<bom.artifactId>bom-2.426.x</bom.artifactId>
47+
<bom.version>3208.vb_21177d4b_cd9</bom.version>
4848

4949
<!-- maven plugins versions -->
5050
<maven-coveralls.version>4.3.0</maven-coveralls.version>

src/main/java/org/jenkinsci/plugins/kubernetes/auth/KubernetesAuthConfig.java

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.jenkinsci.plugins.kubernetes.auth;
22

3+
import org.jenkinsci.plugins.kubernetes.credentials.Utils;
4+
35
/**
46
* Configuration object for {@link KubernetesAuth} operations.
57
*/
@@ -18,6 +20,7 @@ public class KubernetesAuthConfig {
1820
private final boolean skipTlsVerify;
1921

2022
public KubernetesAuthConfig(String serverUrl, String caCertificate, boolean skipTlsVerify) {
23+
Utils.ensureFIPSCompliantRequest(serverUrl, skipTlsVerify);
2124
this.serverUrl = serverUrl;
2225
this.caCertificate = caCertificate;
2326
this.skipTlsVerify = skipTlsVerify;

src/main/java/org/jenkinsci/plugins/kubernetes/credentials/HttpClientWithTLSOptionsFactory.java

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ private static SSLConnectionSocketFactory getVerifyCertSSLFactory(String hostnam
7979
}
8080

8181
public static HttpClientBuilder getBuilder(URI uri, String caCertData, boolean skipTLSVerify) throws TLSConfigurationError {
82+
Utils.ensureFIPSCompliantURIRequest(uri, skipTLSVerify);
8283
final HttpClientBuilder builder = HttpClients.custom().setRedirectStrategy(NO_HTTP_REDIRECT);
8384

8485
try {

src/main/java/org/jenkinsci/plugins/kubernetes/credentials/OpenShiftBearerTokenCredentialImpl.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import net.sf.json.JSONObject;
88
import org.apache.commons.codec.binary.Base64;
99
import org.apache.http.Header;
10+
import org.apache.http.HttpHeaders;
1011
import org.apache.http.NameValuePair;
1112
import org.apache.http.client.ClientProtocolException;
1213
import org.apache.http.client.methods.CloseableHttpResponse;
@@ -135,7 +136,9 @@ private synchronized Token refreshToken(String apiServerURL, String caCertData,
135136

136137
final HttpClientBuilder builder = HttpClientWithTLSOptionsFactory.getBuilder(uri, caCertData, skipTLSVerify);
137138
HttpGet authorize = new HttpGet(oauthServerURL + "?client_id=openshift-challenging-client&response_type=token");
138-
authorize.setHeader("Authorization", getBasicAuthenticationHeader(getUsername(), getPassword()));
139+
authorize.setHeader(HttpHeaders.AUTHORIZATION, getBasicAuthenticationHeader(getUsername(), getPassword()));
140+
141+
Utils.ensureFIPSCompliantURIRequest(authorize.getURI(), skipTLSVerify);
139142
final CloseableHttpResponse response = builder.build().execute(authorize);
140143

141144
if (response.getStatusLine().getStatusCode() != 302) {
@@ -154,6 +157,7 @@ private String getOauthServerUrl(String apiServerURL, String caCertData, boolean
154157
URI uri = new URI(apiServerURL);
155158
final HttpClientBuilder builder = HttpClientWithTLSOptionsFactory.getBuilder(uri, caCertData, skipTLSVerify);
156159
HttpGet discover = new HttpGet(apiServerURL + "/.well-known/oauth-authorization-server");
160+
Utils.ensureFIPSCompliantURIRequest(discover.getURI(), skipTLSVerify);
157161
final CloseableHttpResponse response = builder.build().execute(discover);
158162
return JSONObject.fromObject(EntityUtils.toString(response.getEntity())).getString("authorization_endpoint");
159163
}

src/main/java/org/jenkinsci/plugins/kubernetes/credentials/Utils.java

+59-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package org.jenkinsci.plugins.kubernetes.credentials;
22

3+
import jenkins.security.FIPS140;
34
import org.apache.commons.codec.binary.Base64;
45

6+
import java.net.URI;
57
import java.nio.charset.StandardCharsets;
68
import java.security.Key;
79
import java.security.cert.Certificate;
810
import java.security.cert.CertificateEncodingException;
9-
import java.security.cert.X509Certificate;
1011

1112
public abstract class Utils {
1213

14+
/**
15+
* Error message used to indicate that skipping TLS verification is not accepted in FIPS mode.
16+
*/
17+
public static final String FIPS140_ERROR_MESSAGE =
18+
"Using an insecure connection and/or skipping TLS verification is not accepted in FIPS mode.";
19+
1320
public static String wrapWithMarker(String begin, String end, String encodedBody) {
1421
return new StringBuilder(begin).append("\n")
1522
.append(encodedBody).append("\n")
@@ -46,4 +53,55 @@ public static String encodeCertificate(Certificate certificate) throws Certifica
4653
public static String encodeKey(Key key) {
4754
return encodeBase64(wrapPrivateKey(Base64.encodeBase64String(key.getEncoded())));
4855
}
56+
57+
/**
58+
* Ensure that the URI request is FIPS compliant for the given HttpUriRequest object and skipTLSVerify option.
59+
* Throw an exception if the request is invalid.
60+
* A request is considered valid if the connection is either using TLS or a local pipe
61+
* and if the TLS verification is not skipped.
62+
* If FIPS mode is not enabled, this method does nothing.
63+
*
64+
* @param uri The request to validate
65+
* @param skipTLSVerify A flag indicating whether to skip TLS verification or not
66+
* @throws IllegalArgumentException If the request is invalid
67+
*/
68+
public static void ensureFIPSCompliantURIRequest(URI uri, boolean skipTLSVerify) {
69+
boolean isInsecure = uri.getScheme().equals("http");
70+
ensureFIPSCompliant(isInsecure, skipTLSVerify);
71+
}
72+
73+
/**
74+
* Ensure that the request is FIPS compliant for the given URL and skipTLSVerify option.
75+
* Throw an exception if the request is invalid.
76+
* A request is considered valid if the connection is either using TLS or a local pipe
77+
* and if the TLS verification is not skipped.
78+
* If FIPS mode is not enabled, this method does nothing.
79+
*
80+
* @param stringRequest The request to validate
81+
* @param skipTLSVerify A flag indicating whether to skip TLS verification or not
82+
* @throws IllegalArgumentException If the request is invalid
83+
*/
84+
public static void ensureFIPSCompliantRequest(String stringRequest, boolean skipTLSVerify) {
85+
boolean isInsecure = stringRequest.startsWith("http://");
86+
ensureFIPSCompliant(isInsecure, skipTLSVerify);
87+
}
88+
89+
/**
90+
* Ensure FIPS compliance based on the following rules:
91+
* <ul>
92+
* <li>Must use a secure connection</li>
93+
* <li>TLS verification is mandatory</li>
94+
* </ul>
95+
* Throw an exception if not compliant.
96+
* If FIPS mode is not enabled, this method does nothing.
97+
*
98+
* @param insecureConnection If the connection is insecure
99+
* @param skipTLSVerify A flag indicating whether to skip TLS verification or not
100+
* @throws IllegalArgumentException If not FIPS compliant
101+
*/
102+
private static void ensureFIPSCompliant(boolean insecureConnection, boolean skipTLSVerify) {
103+
if (FIPS140.useCompliantAlgorithms() && (insecureConnection || skipTLSVerify)) {
104+
throw new IllegalArgumentException(Utils.FIPS140_ERROR_MESSAGE);
105+
}
106+
}
49107
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.jenkinsci.plugins.kubernetes.auth;
2+
3+
import static org.junit.Assert.fail;
4+
5+
import org.junit.Test;
6+
7+
public abstract class AbstractKubernetesAuthConfigFIPSTest {
8+
protected String scheme;
9+
10+
protected boolean skipTLSVerify;
11+
12+
protected boolean shouldPass;
13+
14+
protected String motivation;
15+
16+
public AbstractKubernetesAuthConfigFIPSTest(
17+
String scheme, boolean skipTLSVerify, boolean shouldPass, String motivation) {
18+
this.scheme = scheme;
19+
this.skipTLSVerify = skipTLSVerify;
20+
this.shouldPass = shouldPass;
21+
this.motivation = motivation;
22+
}
23+
24+
@Test
25+
public void testCreateKubernetesAuthConfig() {
26+
try {
27+
new KubernetesAuthConfig(scheme + "://server", null, skipTLSVerify);
28+
if (!shouldPass) {
29+
fail("This test was expected to fail, reason: " + motivation);
30+
}
31+
} catch (IllegalArgumentException e) {
32+
if (shouldPass) {
33+
fail("This test was expected to pass, reason: " + motivation);
34+
}
35+
}
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.jenkinsci.plugins.kubernetes.auth;
2+
3+
import java.util.Arrays;
4+
import java.util.Collection;
5+
import jenkins.security.FIPS140;
6+
import org.junit.ClassRule;
7+
import org.junit.runner.RunWith;
8+
import org.junit.runners.Parameterized;
9+
import org.jvnet.hudson.test.FlagRule;
10+
11+
@RunWith(Parameterized.class)
12+
public class KubernetesAuthConfigWithFIPSTest extends AbstractKubernetesAuthConfigFIPSTest {
13+
@ClassRule
14+
public static FlagRule<String> fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "true");
15+
16+
public KubernetesAuthConfigWithFIPSTest(
17+
String scheme, boolean skipTLSVerify, boolean shouldPass, String motivation) {
18+
super(scheme, skipTLSVerify, shouldPass, motivation);
19+
}
20+
21+
@Parameterized.Parameters
22+
public static Collection<Object[]> parameters() {
23+
return Arrays.asList(new Object[][] {
24+
// Valid use cases
25+
{"https", false, true, "TLS is used and the TLS verification is not skipped, this should be accepted"},
26+
// Invalid use cases
27+
{"https", true, false, "Skip TLS check is not accepted in FIPS mode"},
28+
{"http", false, false, "TLS is mandatory when in FIPS mode"},
29+
{"http", true, false, "TLS and TLS check are mandatory when in FIPS mode"},
30+
});
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.jenkinsci.plugins.kubernetes.auth;
2+
3+
import java.util.Arrays;
4+
import java.util.Collection;
5+
import jenkins.security.FIPS140;
6+
import org.junit.ClassRule;
7+
import org.junit.runner.RunWith;
8+
import org.junit.runners.Parameterized;
9+
import org.jvnet.hudson.test.FlagRule;
10+
11+
@RunWith(Parameterized.class)
12+
public class KubernetesAuthConfigWithoutFIPSTest extends AbstractKubernetesAuthConfigFIPSTest {
13+
@ClassRule
14+
public static FlagRule<String> fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "false");
15+
16+
public KubernetesAuthConfigWithoutFIPSTest(
17+
String scheme, boolean skipTLSVerify, boolean shouldPass, String motivation) {
18+
super(scheme, skipTLSVerify, shouldPass, motivation);
19+
}
20+
21+
@Parameterized.Parameters
22+
public static Collection<Object[]> parameters() {
23+
return Arrays.asList(new Object[][] {
24+
// Valid use cases
25+
{"https", false, true, "Not in FIPS mode, any combination should be valid"},
26+
{"http", false, true, "Not in FIPS mode, any combination should be valid"},
27+
{"http", true, true, "Not in FIPS mode, any combination should be valid"},
28+
{"https", true, true, "Not in FIPS mode, any combination should be valid"},
29+
});
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.jenkinsci.plugins.kubernetes.credentials;
2+
3+
import static org.junit.Assert.fail;
4+
5+
import java.net.URI;
6+
import java.net.URISyntaxException;
7+
import org.junit.Test;
8+
9+
public abstract class AbstractHttpClientWithTLSOptionsFactoryFIPSTest {
10+
protected String scheme;
11+
12+
protected boolean skipTLSVerify;
13+
14+
protected boolean shouldPass;
15+
16+
protected String motivation;
17+
18+
public AbstractHttpClientWithTLSOptionsFactoryFIPSTest(String scheme, boolean skipTLSVerify, boolean shouldPass, String motivation) {
19+
this.scheme = scheme;
20+
this.skipTLSVerify = skipTLSVerify;
21+
this.shouldPass = shouldPass;
22+
this.motivation = motivation;
23+
}
24+
25+
@Test
26+
public void testCreateKubernetesAuthConfig() throws URISyntaxException {
27+
try {
28+
HttpClientWithTLSOptionsFactory.getBuilder(new URI(scheme, "localhost", null, null), null, skipTLSVerify);
29+
if (!shouldPass) {
30+
fail("This test was expected to fail, reason: " + motivation);
31+
}
32+
} catch (IllegalArgumentException e) {
33+
if (shouldPass) {
34+
fail("This test was expected to pass, reason: " + motivation);
35+
}
36+
} catch (HttpClientWithTLSOptionsFactory.TLSConfigurationError e) {
37+
fail("This test should not cause a TLSConfigurationError");
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)