Skip to content

Commit b5347dc

Browse files
committed
[JENKINS-75676] Client certificate credentials not handled properly
Add a custom TlsSocketStrategy implementation to retrieve an SSLContext from a specific HttpContext or delegate it to DefaultClientTlsSocketStrategy if it is not present. This way, the SSLContext created from a keyStore (client certificate) is configured to be used by the underlying HttpConnection of the shared connection pool. The HttpContext is created each time a new Bitbucket client is instantiated, which means that the HttpContext is bound to the lifecycle of the client instance.
1 parent 458c083 commit b5347dc

File tree

3 files changed

+70
-16
lines changed

3 files changed

+70
-16
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2025, Falco Nikolas
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
package com.cloudbees.jenkins.plugins.bitbucket.impl.client;
25+
26+
import java.io.IOException;
27+
import java.net.Socket;
28+
import java.security.NoSuchAlgorithmException;
29+
import javax.net.ssl.SSLContext;
30+
import javax.net.ssl.SSLSocket;
31+
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
32+
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
33+
import org.apache.hc.core5.annotation.Contract;
34+
import org.apache.hc.core5.annotation.ThreadingBehavior;
35+
import org.apache.hc.core5.http.protocol.HttpContext;
36+
37+
/**
38+
* Custom implementation of a TlsSocketStrategu to replicate what
39+
* {@code org.apache.http.impl.conn.DefaultHttpClientConnectionOperator#getSocketFactoryRegistry(HttpContext)}
40+
* did in Apache Client HTTP 4 implementation.
41+
*/
42+
@Contract(threading = ThreadingBehavior.SAFE)
43+
public class BitbucketTlsSocketStrategy implements TlsSocketStrategy {
44+
public static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry";
45+
46+
private TlsSocketStrategy defaultStrategy;
47+
48+
@Override
49+
public SSLSocket upgrade(Socket socket, String target, int port, Object attachment, HttpContext context) throws IOException {
50+
TlsSocketStrategy strategy = defaultStrategy;
51+
52+
Object value = context.getAttribute(SOCKET_FACTORY_REGISTRY);
53+
if (value instanceof SSLContext sslContext) {
54+
strategy = new DefaultClientTlsStrategy(sslContext);
55+
} else if (defaultStrategy == null) {
56+
try {
57+
strategy = new DefaultClientTlsStrategy(SSLContext.getDefault());
58+
} catch (NoSuchAlgorithmException e) {
59+
throw new IOException(e);
60+
}
61+
defaultStrategy = strategy;
62+
}
63+
return strategy.upgrade(socket, target, port, attachment, context);
64+
}
65+
66+
}

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/credentials/BitbucketClientCertificateAuthenticator.java

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,34 +26,24 @@
2626

2727
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketAuthenticator;
2828
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketException;
29+
import com.cloudbees.jenkins.plugins.bitbucket.impl.client.BitbucketTlsSocketStrategy;
2930
import com.cloudbees.plugins.credentials.common.StandardCertificateCredentials;
3031
import hudson.util.Secret;
3132
import java.security.KeyManagementException;
3233
import java.security.KeyStore;
3334
import java.security.KeyStoreException;
3435
import java.security.NoSuchAlgorithmException;
3536
import java.security.UnrecoverableKeyException;
36-
import java.util.logging.Logger;
3737
import javax.net.ssl.SSLContext;
3838
import org.apache.hc.client5.http.protocol.HttpClientContext;
39-
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
40-
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
41-
import org.apache.hc.client5.http.ssl.HttpsSupport;
42-
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
4339
import org.apache.hc.core5.http.HttpHost;
44-
import org.apache.hc.core5.http.URIScheme;
45-
import org.apache.hc.core5.http.config.Registry;
46-
import org.apache.hc.core5.http.config.RegistryBuilder;
4740
import org.apache.hc.core5.ssl.SSLContextBuilder;
4841
import org.apache.hc.core5.ssl.SSLContexts;
4942

5043
/**
5144
* Authenticates against Bitbucket using a TLS client certificate
5245
*/
5346
public class BitbucketClientCertificateAuthenticator implements BitbucketAuthenticator {
54-
private static final Logger logger = Logger.getLogger(BitbucketClientCertificateAuthenticator.class.getName());
55-
private static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry";
56-
5747
private final String credentialsId;
5848
private final KeyStore keyStore;
5949
private final Secret password;
@@ -72,11 +62,7 @@ public BitbucketClientCertificateAuthenticator(StandardCertificateCredentials cr
7262
@Override
7363
public void configureContext(HttpClientContext context, HttpHost host) {
7464
try {
75-
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
76-
.register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory())
77-
.register(URIScheme.HTTPS.id, new SSLConnectionSocketFactory(buildSSLContext(), HttpsSupport.getDefaultHostnameVerifier()))
78-
.build();
79-
context.setAttribute(SOCKET_FACTORY_REGISTRY, registry); // override SSL registry for this context
65+
context.setAttribute(BitbucketTlsSocketStrategy.SOCKET_FACTORY_REGISTRY, buildSSLContext()); // override SSL registry for this context
8066
} catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException | KeyManagementException e) {
8167
throw new BitbucketException("Failed to set up SSL context from provided client certificate", e);
8268
}

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/BitbucketServerAPIClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.cloudbees.jenkins.plugins.bitbucket.client.repository.UserRoleInRepository;
4040
import com.cloudbees.jenkins.plugins.bitbucket.filesystem.BitbucketSCMFile;
4141
import com.cloudbees.jenkins.plugins.bitbucket.impl.client.AbstractBitbucketApi;
42+
import com.cloudbees.jenkins.plugins.bitbucket.impl.client.BitbucketTlsSocketStrategy;
4243
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketAccessTokenAuthenticator;
4344
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketClientCertificateAuthenticator;
4445
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketUsernamePasswordAuthenticator;
@@ -141,6 +142,7 @@ public class BitbucketServerAPIClient extends AbstractBitbucketApi implements Bi
141142
private static final HttpClientConnectionManager connectionManager = connectionManagerBuilder()
142143
.setMaxConnPerRoute(20)
143144
.setMaxConnTotal(40 /* should be 20 * number of server instances */)
145+
.setTlsSocketStrategy(new BitbucketTlsSocketStrategy())
144146
.build();
145147

146148
/**

0 commit comments

Comments
 (0)