From 3c5cb4615be4757148761de7bbbac10f5518e65f Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Wed, 2 Nov 2022 15:49:45 +0100 Subject: [PATCH 01/12] HttpRequestExecution: pass ignoreSslErrors to CertificateAuthentication --- .../java/jenkins/plugins/http_request/HttpRequestExecution.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java b/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java index 026c71e1..0794b5a1 100644 --- a/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java +++ b/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java @@ -243,7 +243,7 @@ private HttpRequestExecution( } } if (credential instanceof StandardCertificateCredentials) { - auth = new CertificateAuthentication((StandardCertificateCredentials) credential); + auth = new CertificateAuthentication((StandardCertificateCredentials) credential, this.ignoreSslErrors); } } } From b6d751d430f06da4e8e94de093c53b13d0e80e19 Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Wed, 2 Nov 2022 15:55:16 +0100 Subject: [PATCH 02/12] HttpRequestExecution: (optionally) trace details of executeRequest() --- .../jenkins/plugins/http_request/HttpRequestExecution.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java b/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java index 0794b5a1..0fe2e607 100644 --- a/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java +++ b/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java @@ -440,6 +440,12 @@ private ResponseContentSupplier executeRequest( CloseableHttpClient httpclient, HttpClientUtil clientUtil, HttpRequestBase httpRequestBase, HttpContext context) throws IOException { ResponseContentSupplier responseContentSupplier; + logger().println("Sending HTTP request with" + + " CloseableHttpClient=" + httpclient.toString() + + " HttpClientUtil=" + clientUtil.toString() + + " HttpRequestBase=" + httpRequestBase.toString() + + " HttpContext=" + context.toString() + ); try { final HttpResponse response = clientUtil.execute(httpclient, context, httpRequestBase, logger()); // The HttpEntity is consumed by the ResponseContentSupplier From 661fedb0643b0ecefb2e34c3f21b02f0eb45b740 Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Wed, 2 Nov 2022 15:48:38 +0100 Subject: [PATCH 03/12] CertificateAuthentication.java: extend with support for Certificate Credentials with trusted certs (private CA) * try to loadTrustMaterial(), * warn if keystore pass is trivial so PKCS12 does not load everything, * support ignoreSslErrors flag, * and log progress through auth() --- .../auth/CertificateAuthentication.java | 66 ++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java b/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java index 54d44a57..068873b9 100644 --- a/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java +++ b/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java @@ -2,21 +2,35 @@ import java.io.IOException; import java.io.PrintStream; +import java.security.KeyStore; import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.conn.ssl.TrustAllStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.protocol.HttpContext; +import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.SSLContexts; +import org.apache.http.ssl.TrustStrategy; import com.cloudbees.plugins.credentials.common.StandardCertificateCredentials; +import hudson.Util; + public class CertificateAuthentication implements Authenticator { private final StandardCertificateCredentials credentials; + private final boolean ignoreSslErrors; + public CertificateAuthentication(StandardCertificateCredentials credentials) { this.credentials = credentials; + this.ignoreSslErrors = false; + } + + public CertificateAuthentication(StandardCertificateCredentials credentials, boolean ignoreSslErrors) { + this.credentials = credentials; + this.ignoreSslErrors = ignoreSslErrors; } @Override @@ -30,12 +44,58 @@ public CloseableHttpClient authenticate(HttpClientBuilder clientBuilder, HttpRequestBase requestBase, PrintStream logger) throws IOException { try { - clientBuilder.setSSLContext( - SSLContexts.custom().loadKeyMaterial(credentials.getKeyStore(), - credentials.getPassword().getPlainText().toCharArray()).build()); + KeyStore keyStore = credentials.getKeyStore(); + // Note: modeled after CertificateCredentialsImpl.toCharArray() + // which ignores both null and "" empty passwords, even though + // technically the byte stream reader there *can* decipher with + // "" as the password. The null value is explicitly ignored by + // ultimate sun.security.pkcs12.PKCS12KeyStore::engineLoad(), + // for more context see comments in its sources. + String keyStorePass = Util.fixEmpty(credentials.getPassword().getPlainText()); + char[] keyStorePassChars = (keyStorePass == null ? null : keyStorePass.toCharArray()); + SSLContextBuilder contextBuilder = SSLContexts.custom(); + + if (keyStorePassChars == null) { + logger.println("WARNING: Jenkins Certificate Credential '" + + credentials.getId() + "' was saved without a password, " + + "so any certificates (and chain of trust) in it would " + + "be ignored by Java PKCS12 support!"); + } + + try { + TrustStrategy trustStrategy = null; + if (ignoreSslErrors) { + // e.g. for user certificate issued by test CA so + // is not persisted in the system 'cacerts' file. + // Hopefully it is at least added/trusted in the + // generated keystore... + trustStrategy = new TrustAllStrategy(); + //trustStrategy = new TrustSelfSignedStrategy(); + } + System.err.println("Adding Trust Material from provided KeyStore"); + contextBuilder = contextBuilder.loadTrustMaterial(keyStore, trustStrategy); + System.err.println("Added Trust Material from provided KeyStore"); + + logger.println("Added Trust Material from provided KeyStore"); + } catch (Exception e) { + logger.println("Failed to add Trust Material from provided KeyStore (so Key Material might end up untrusted): " + e.getMessage()); + // Do no re-throw, maybe system trust would suffice? + // TODO: Can we identify lack of trust material in + // key store vs. inability to load what exists?.. + // And do we really care about the difference? + } + + System.err.println("Adding Key Material from provided KeyStore"); + contextBuilder = contextBuilder.loadKeyMaterial(keyStore, keyStorePassChars); + System.err.println("Added Key Material from provided KeyStore"); + logger.println("Added Key Material from provided KeyStore"); + + clientBuilder = clientBuilder.setSSLContext(contextBuilder.build()); + logger.println("Set SSL context for the HTTP client builder"); return clientBuilder.build(); } catch (Exception e) { + logger.println("Failed to set SSL context: " + e.getMessage()); throw new IOException(e); } } From b5faa43039cb2a60b67b55bac0692082755da557 Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Wed, 2 Nov 2022 21:59:37 +0100 Subject: [PATCH 04/12] CertificateAuthentication.java: drop dev-test logging --- .../http_request/auth/CertificateAuthentication.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java b/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java index 068873b9..47994907 100644 --- a/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java +++ b/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java @@ -72,10 +72,8 @@ public CloseableHttpClient authenticate(HttpClientBuilder clientBuilder, trustStrategy = new TrustAllStrategy(); //trustStrategy = new TrustSelfSignedStrategy(); } - System.err.println("Adding Trust Material from provided KeyStore"); - contextBuilder = contextBuilder.loadTrustMaterial(keyStore, trustStrategy); - System.err.println("Added Trust Material from provided KeyStore"); + contextBuilder = contextBuilder.loadTrustMaterial(keyStore, trustStrategy); logger.println("Added Trust Material from provided KeyStore"); } catch (Exception e) { logger.println("Failed to add Trust Material from provided KeyStore (so Key Material might end up untrusted): " + e.getMessage()); @@ -85,9 +83,7 @@ public CloseableHttpClient authenticate(HttpClientBuilder clientBuilder, // And do we really care about the difference? } - System.err.println("Adding Key Material from provided KeyStore"); contextBuilder = contextBuilder.loadKeyMaterial(keyStore, keyStorePassChars); - System.err.println("Added Key Material from provided KeyStore"); logger.println("Added Key Material from provided KeyStore"); clientBuilder = clientBuilder.setSSLContext(contextBuilder.build()); From f15fd889c53c94878cf678fde54611548da0bd91 Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Thu, 3 Nov 2022 02:20:14 +0100 Subject: [PATCH 05/12] CertificateAuthentication: set SSLConnectionSocketFactory as well --- .../http_request/auth/CertificateAuthentication.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java b/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java index 47994907..39c42cc4 100644 --- a/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java +++ b/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java @@ -5,6 +5,7 @@ import java.security.KeyStore; import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustAllStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; @@ -89,6 +90,11 @@ public CloseableHttpClient authenticate(HttpClientBuilder clientBuilder, clientBuilder = clientBuilder.setSSLContext(contextBuilder.build()); logger.println("Set SSL context for the HTTP client builder"); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + contextBuilder.build()); + clientBuilder = clientBuilder.setSSLSocketFactory(sslsf); + logger.println("Set SSL socket factory for the HTTP client builder"); + return clientBuilder.build(); } catch (Exception e) { logger.println("Failed to set SSL context: " + e.getMessage()); From f4ab5b1a43c37a83827faeee9e7073c6fd523651 Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Thu, 3 Nov 2022 09:25:59 +0100 Subject: [PATCH 06/12] README.adoc: add notes on Certificate-based Authentication with a Jenkins stored credential --- README.adoc | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index d3985ba3..d5c26e62 100644 --- a/README.adoc +++ b/README.adoc @@ -35,7 +35,8 @@ The following features are available in both Pipeline and traditional project ty * Programmable range of expected response codes (a response code outside the range fails the build) * Supports Basic Authentication (see global configuration) * Supports Form Authentication (see global configuration) -* You can specify a string that must be present in the response (if the string is not present, the +* Supports Certificate-based Authentication with a Jenkins stored credential +* You can specify a string that must be present in the response (if the string is not present, the build fails) * You can set a connection timeout limit (build fails if timeout is exceeded) * You can set an "Accept" header directly @@ -256,7 +257,23 @@ def response = httpRequest authenticate: 'my-jenkins-credential-id', url: 'https://api.github.com/user/jenkinsci' ---- -For details on the Pipeline features, use the Pipeline snippet generator in the Pipeline job +You can send an SSL request with authentication by user certificate; +for a private CA, make sure to first add the CA certificate is as +"Trusted", then add the user key along with certification chain up +to same CA certificate, into your PKCS12 keystore file which you +upload to Jenkins credentials, and you also must use a non-trivial +password for that keystore. Keep in mind that for systems under test +which create their own self-signed CA and HTTPS protection, you can +programmatically create and upload the credentials, into a domain +where the job has write access (its folder etc.) + +[source,groovy] +---- +def response = httpRequest authentication: 'user_with_cert_and_ca', + url: 'https://sut123.local.domain:8443/api/v1/status/debug' +---- + +For details on the Pipeline features, use the Pipeline snippet generator in the Pipeline job configuration. [WARNING] From 06dbf1272a28759484a9a2a5c1e64accc346c96e Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Fri, 4 Nov 2022 03:16:20 +0100 Subject: [PATCH 07/12] HttpRequestExecution.java: hush down a remaining bit of debug logging --- .../jenkins/plugins/http_request/HttpRequestExecution.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java b/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java index 0fe2e607..9d001447 100644 --- a/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java +++ b/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java @@ -440,12 +440,15 @@ private ResponseContentSupplier executeRequest( CloseableHttpClient httpclient, HttpClientUtil clientUtil, HttpRequestBase httpRequestBase, HttpContext context) throws IOException { ResponseContentSupplier responseContentSupplier; +/* + // TODO: pick interesting fields/getters from these classes: logger().println("Sending HTTP request with" + " CloseableHttpClient=" + httpclient.toString() + " HttpClientUtil=" + clientUtil.toString() + " HttpRequestBase=" + httpRequestBase.toString() + " HttpContext=" + context.toString() ); +*/ try { final HttpResponse response = clientUtil.execute(httpclient, context, httpRequestBase, logger()); // The HttpEntity is consumed by the ResponseContentSupplier From 91e0ab6618b329d7dc5a9557c9e7910e77d38813 Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Wed, 23 Nov 2022 12:31:40 +0100 Subject: [PATCH 08/12] Add test keystore for HttpRequestStepCredentialsTest (lift from credentials-plugin) --- .../HttpRequestStepCredentialsTest/test.p12 | Bin 0 -> 2685 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/test.p12 diff --git a/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/test.p12 b/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/test.p12 new file mode 100644 index 0000000000000000000000000000000000000000..253d4081a3aeffab7d17e8c0a308ee1e85d6456f GIT binary patch literal 2685 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 Date: Wed, 23 Nov 2022 12:46:08 +0100 Subject: [PATCH 09/12] Add test keystore with trusted CA for HttpRequestStepCredentialsTest (lift from credentials-plugin) --- .../testTrusted.p12 | Bin 0 -> 3832 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/testTrusted.p12 diff --git a/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/testTrusted.p12 b/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/testTrusted.p12 new file mode 100644 index 0000000000000000000000000000000000000000..4c81ef138ec7533a0f2110f7d71c6f7241d0419b GIT binary patch literal 3832 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 literal 0 HcmV?d00001 From 219bf032db344e2ba2a69006ff36f207bab236cd Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Wed, 23 Nov 2022 12:55:29 +0100 Subject: [PATCH 10/12] Add README about test keystores for HttpRequestStepCredentialsTest --- .../HttpRequestStepCredentialsTest/README.txt | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/README.txt diff --git a/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/README.txt b/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/README.txt new file mode 100644 index 00000000..3ff1a6dc --- /dev/null +++ b/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/README.txt @@ -0,0 +1,145 @@ +Test keystores: + +* test.p12 was picked from credentials-plugin + It includes one entry aliased "1" with a private key, + its cert, and issuing CA cert (as a simple chain). + The keystore password is "password" + +* testTrusted.p12 is a modification of that, adding an + entry aliased "ca" with that same CA cert imported as + trusted: +```` +# Show certs in BASE64 format, last of these is CA: +:; keytool -list -keystore test.p12 -storepass password -alias 1 -rfc + +# Save second block to "ca.pem" + +# Re-import: +:; cp test.p12 testTrusted.p12 +:; keytool -importcert -trustcacerts -alias ca -file ca.pem -keystore testTrusted.p12 -storepass password +... +Trust this certificate? [no]: yes +Certificate was added to keystore +```` + +Verification: +```` +:; keytool -list -keystore testTrusted.p12 -storepass password -rfc +Keystore type: PKCS12 +Keystore provider: SUN + +Your keystore contains 2 entries + +Alias name: 1 +Creation date: 23.11.2022 +Entry type: PrivateKeyEntry +Certificate chain length: 2 +Certificate[1]: +-----BEGIN CERTIFICATE----- +MIIDRzCCArCgAwIBAgIBATANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL +MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t +VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA1MDgw +NDE4MTYyMFoXDTE1MDgwMjE4MTYyMFowfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgT +AkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZvcnQtRnVuc3Rv +bjEPMA0GA1UEAxMGcGtjczEyMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlk +b21haW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMOT0PRbWiTEJTUjjiwW +yPC7hR2ruxshzWcgWZUuNg5RARnnsQfGpBK+kKp4QsJSunVCo2fmUFkU/UGYVVXK +nHMEcDtX2JqVY/bAPjxptn5k1bnvMFkKFnaAZl5Mi0K0s+D9U0ivpIaw1QXdQbw+ +w3STcv1kpy8rmyerH6KOXL1bAgMBAAGjge4wgeswCQYDVR0TBAIwADAsBglghkgB +hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE +FP9dcedV6TFtLIWOWXxIQ5h6JR45MIGQBgNVHSMEgYgwgYWAFImmYOO66j6v/GR/ +TL2M0kiN4MxGoWqkaDBmMQswCQYDVQQGEwJLRzELMAkGA1UECBMCTkExEDAOBgNV +BAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4tVEVTVDEhMB8GCSqGSIb3DQEJ +ARYSbWVAbXlob3N0Lm15ZG9tYWluggEAMA0GCSqGSIb3DQEBBAUAA4GBABP/5mXw +ttXKG6dqQl5kPisFs/c+0j64xytp5/cdB/zMpEWRWTBXtyL3T5T16xs52kJS0VfT +t+jezYbeu/dCdBL8Moz3RTYb1aY2/xymZ433kWjvgtrOzgGlaW3eKXcQpQEyK2v/ +J4q7+oDCElBRilZCm0mBcQsySKsZjGm8BMjh +-----END CERTIFICATE----- +Certificate[2]: +-----BEGIN CERTIFICATE----- +MIIDBjCCAm+gAwIBAgIBADANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL +MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t +VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA0MTEy +NTE0NDA1NVoXDTE0MTEyMzE0NDA1NVowZjELMAkGA1UEBhMCS0cxCzAJBgNVBAgT +Ak5BMRAwDgYDVQQHEwdCSVNIS0VLMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxITAf +BgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpbjCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAqPjWJnesPu6bR/iec4FMz3opVaPdBHxg+ORKNmrnVZPh0t8/ +ZT34KXkYoI9B82scurp8UlZVXG8JdUsz+yai8ti9+g7vcuyKUtcCIjn0HLgmdPu5 +gFX25lB0pXw+XIU031dOfPvtROdG5YZN5yCErgCy7TE7zntLnkEDuRmyU6cCAwEA +AaOBwzCBwDAdBgNVHQ4EFgQUiaZg47rqPq/8ZH9MvYzSSI3gzEYwgZAGA1UdIwSB +iDCBhYAUiaZg47rqPq/8ZH9MvYzSSI3gzEahaqRoMGYxCzAJBgNVBAYTAktHMQsw +CQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UEChMMT3BlblZQTi1U +RVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW6CAQAwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBfJoiWYrYdjM0mKPEzUQk0nLYTovBP +I0es/2rfGrin1zbcFY+4dhVBd1E/StebnG+CP8r7QeEIwu7x8gYDdOLLsZn+2vBL +e4jNU1ClI6Q0L7jrzhhunQ5mAaZztVyYwFB15odYcdN2iO0tP7jtEsvrRqxICNy3 +8itzViPTf5W4sA== +-----END CERTIFICATE----- + + +******************************************* +******************************************* + + +Alias name: ca +Creation date: 23.11.2022 +Entry type: trustedCertEntry + +-----BEGIN CERTIFICATE----- +MIIDBjCCAm+gAwIBAgIBADANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL +MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t +VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA0MTEy +NTE0NDA1NVoXDTE0MTEyMzE0NDA1NVowZjELMAkGA1UEBhMCS0cxCzAJBgNVBAgT +Ak5BMRAwDgYDVQQHEwdCSVNIS0VLMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxITAf +BgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpbjCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAqPjWJnesPu6bR/iec4FMz3opVaPdBHxg+ORKNmrnVZPh0t8/ +ZT34KXkYoI9B82scurp8UlZVXG8JdUsz+yai8ti9+g7vcuyKUtcCIjn0HLgmdPu5 +gFX25lB0pXw+XIU031dOfPvtROdG5YZN5yCErgCy7TE7zntLnkEDuRmyU6cCAwEA +AaOBwzCBwDAdBgNVHQ4EFgQUiaZg47rqPq/8ZH9MvYzSSI3gzEYwgZAGA1UdIwSB +iDCBhYAUiaZg47rqPq/8ZH9MvYzSSI3gzEahaqRoMGYxCzAJBgNVBAYTAktHMQsw +CQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UEChMMT3BlblZQTi1U +RVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW6CAQAwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBfJoiWYrYdjM0mKPEzUQk0nLYTovBP +I0es/2rfGrin1zbcFY+4dhVBd1E/StebnG+CP8r7QeEIwu7x8gYDdOLLsZn+2vBL +e4jNU1ClI6Q0L7jrzhhunQ5mAaZztVyYwFB15odYcdN2iO0tP7jtEsvrRqxICNy3 +8itzViPTf5W4sA== +-----END CERTIFICATE----- + + +******************************************* +******************************************* + + + +Warning: +<1> uses the MD5withRSA signature algorithm which is considered a security risk and is disabled. +<1> uses a 1024-bit RSA key which is considered a security risk. This key size will be disabled in a future update. +<1> uses a 1024-bit RSA key which is considered a security risk. This key size will be disabled in a future update. + uses a 1024-bit RSA key which is considered a security risk. This key size will be disabled in a future update. + + + +# Keytool forbids to export private key; openssl can do it: +:; openssl pkcs12 -in testTrusted.p12 -nodes -nocerts -password pass:password +Bag Attributes + friendlyName: 1 + localKeyID: 2E 39 A5 71 AE FD F1 64 40 83 69 72 3A B6 3D 64 05 18 58 B7 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMOT0PRbWiTEJTUj +jiwWyPC7hR2ruxshzWcgWZUuNg5RARnnsQfGpBK+kKp4QsJSunVCo2fmUFkU/UGY +VVXKnHMEcDtX2JqVY/bAPjxptn5k1bnvMFkKFnaAZl5Mi0K0s+D9U0ivpIaw1QXd +Qbw+w3STcv1kpy8rmyerH6KOXL1bAgMBAAECgYBommqsByATggUUgsvLsPQQLXto +/yy3ukCN47OGIo0u4wxfupfovMmMbPga9O9f17d6eAXF0F0xCBTcPImHtTIvMLIt +UY4U4xwtdlEM3G5ToBxNvCHvtkkDiUVW8AorZfLFY9Agnsc3cTarrvEkdtzyYN8k +246tqACTJZEW8b/QoQJBAP/cd5sEPACChyHx7jr44mMBUppfu/5QC3+0N39XhTx+ +gXNfpRe/V77Qn0CMcH8RqkQVWTaSzPrpzJcAXgc9cB8CQQDDrvois8c+5ZSGu8MG +1zNCEjxTU9BBjWEkGLgwMwsH+5BlA7QT8B9QGWYiCJ4pJVJQ37AyQrUqt4at19Yb +vdtFAkEAgylFtxXInIpNM72N3nVPuGkpKzIAcTIfcuuzt3fqOUSwn7BcNXxFQvA3 +cyOLV9h6bER1Y2CF6+qGkrIBgbyhCQJBAKR14vRXdBWAjhvOolKVexcEjH7b6iOt +1v6nZ+XagGLtIqZDPo2jOi3vqs7fv02FeHFQDp2vQuPr6t0gkWovXqECQFEAAAj3 +TKXvRs1jL5gKNifOBJgeEqzZJXpLMkWDGTgImu+VyKcGE6+pie0okh4rmIoJqNx0 +EzBIslSYYUz8Q+A= +-----END PRIVATE KEY----- +```` + From 41cfa894df5febceafa9d85c7cfe9f372c39f8e4 Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Wed, 23 Nov 2022 14:30:32 +0100 Subject: [PATCH 11/12] Relocate test*.p12 to propoer location for test suite visibility --- .../{HttpRequestStepCredentialsTest => }/README.txt | 0 .../{HttpRequestStepCredentialsTest => }/test.p12 | Bin .../testTrusted.p12 | Bin 3 files changed, 0 insertions(+), 0 deletions(-) rename src/test/resources/jenkins/plugins/http_request/{HttpRequestStepCredentialsTest => }/README.txt (100%) rename src/test/resources/jenkins/plugins/http_request/{HttpRequestStepCredentialsTest => }/test.p12 (100%) rename src/test/resources/jenkins/plugins/http_request/{HttpRequestStepCredentialsTest => }/testTrusted.p12 (100%) diff --git a/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/README.txt b/src/test/resources/jenkins/plugins/http_request/README.txt similarity index 100% rename from src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/README.txt rename to src/test/resources/jenkins/plugins/http_request/README.txt diff --git a/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/test.p12 b/src/test/resources/jenkins/plugins/http_request/test.p12 similarity index 100% rename from src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/test.p12 rename to src/test/resources/jenkins/plugins/http_request/test.p12 diff --git a/src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/testTrusted.p12 b/src/test/resources/jenkins/plugins/http_request/testTrusted.p12 similarity index 100% rename from src/test/resources/jenkins/plugins/http_request/HttpRequestStepCredentialsTest/testTrusted.p12 rename to src/test/resources/jenkins/plugins/http_request/testTrusted.p12 From 6aaebf1ff950d4ea5b4c2ab2fc470bee47d70bf0 Mon Sep 17 00:00:00 2001 From: Evgeny Klimov Date: Wed, 23 Nov 2022 14:14:36 +0100 Subject: [PATCH 12/12] HttpRequestStepCredentialsTest: import and adapt tests from PR credentials-plugin#391 --- .../HttpRequestStepCredentialsTest.java | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/src/test/java/jenkins/plugins/http_request/HttpRequestStepCredentialsTest.java b/src/test/java/jenkins/plugins/http_request/HttpRequestStepCredentialsTest.java index 2e678504..39303aed 100644 --- a/src/test/java/jenkins/plugins/http_request/HttpRequestStepCredentialsTest.java +++ b/src/test/java/jenkins/plugins/http_request/HttpRequestStepCredentialsTest.java @@ -6,27 +6,54 @@ import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.CredentialsStore; +import com.cloudbees.plugins.credentials.SecretBytes; import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import com.cloudbees.plugins.credentials.common.StandardCredentials; import com.cloudbees.plugins.credentials.domains.Domain; +import com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl; import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; import hudson.model.Fingerprint; import hudson.model.Result; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.Collections; import jenkins.model.Jenkins; + +import org.apache.commons.io.FileUtils; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import com.gargoylesoftware.htmlunit.html.HtmlPage; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; /** * @author Mark Waite */ public class HttpRequestStepCredentialsTest extends HttpRequestTestBase { + // For developers: set to `true` so that pipeline console logs show + // up in System.out (and/or System.err) of the plugin test run by + // mvn test -Dtest="HttpRequestStepCredentialsTest" + private boolean verbosePipelines = false; + String getLogAsStringPlaintext(WorkflowRun f) throws java.io.IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + f.getLogText().writeLogTo(0, baos); + return baos.toString(); + } + + // From CertificateCredentialImplTest + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + private File p12simple; + private File p12trusted; private StandardCredentials getInvalidCredential() { String username = "bad-user"; @@ -36,6 +63,32 @@ private StandardCredentials getInvalidCredential() { return new UsernamePasswordCredentialsImpl(scope, id, "desc: " + id, username, password); } + private StandardCredentials getCertificateCredentialSimple() throws IOException { + if (p12simple == null) { + // Contains a private key + openvpn certs, + // as alias named "1" (according to keytool) + p12simple = tmp.newFile("test.p12"); + FileUtils.copyURLToFile(HttpRequestStepCredentialsTest.class.getResource("test.p12"), p12simple); + } + + SecretBytes uploadedKeystore = SecretBytes.fromBytes(Files.readAllBytes(p12simple.toPath())); + CertificateCredentialsImpl.UploadedKeyStoreSource storeSource = new CertificateCredentialsImpl.UploadedKeyStoreSource(uploadedKeystore); + return new CertificateCredentialsImpl(null, "cred_cert_simple", null, "password", storeSource); + } + + private StandardCredentials getCertificateCredentialTrusted() throws IOException { + if (p12trusted == null) { + // Contains a private key + openvpn certs as alias named "1", + // and another alias named "ca" with trustedKeyEntry for CA + p12trusted = tmp.newFile("testTrusted.p12"); + FileUtils.copyURLToFile(HttpRequestStepCredentialsTest.class.getResource("testTrusted.p12"), p12trusted); + } + + SecretBytes uploadedKeystore = SecretBytes.fromBytes(Files.readAllBytes(p12trusted.toPath())); + CertificateCredentialsImpl.UploadedKeyStoreSource storeSource = new CertificateCredentialsImpl.UploadedKeyStoreSource(uploadedKeystore); + return new CertificateCredentialsImpl(null, "cred_cert_with_ca", null, "password", storeSource); + } + private CredentialsStore store = null; @Before @@ -105,4 +158,166 @@ public void trackCredentials() throws Exception { assertThat(page.getElementById("usage-present"), notNullValue()); assertThat(page.getAnchorByText(proj.getFullDisplayName()), notNullValue()); } + + // A set of tests with certificate credentials in different contexts + // TODO: Test on remote agent as in https://github.com/jenkinsci/credentials-plugin/pull/391 + // but this requires that PR to be merged first, so credentials-plugin + // processes snapshot() and readable keystore data gets to remote agent. + // Note that the tests below focus on ability of the plugin to load and + // process the key store specified by the credential, rather than that + // it is usable further. It would be a separate effort to mock up a web + // server protected by HTTPS and using certificates for login (possibly + // user and server backed by two different CA's), and query that. + String cpsScriptCredentialTestHttpRequest(String id, String runnerTag) { + // Note: we accept any outcome (for the plugin, unresolved host is HTTP-404) + // but it may not crash making use of the credential + // Note: cases withLocalCertLookup also need cpsScriptCredentialTestImports() + return "def authentication='" + id + "';\n" + + "\n" + + "echo \"Querying HTTPS with credential...\"\n" + + "def response = httpRequest(url: 'https://github.xcom/api/v3',\n" + + " httpMode: 'GET',\n" + + " authentication: authentication,\n" + + " consoleLogResponseBody: true,\n" + + " contentType : 'APPLICATION_FORM',\n" + + " validResponseCodes: '100:599',\n" + + " quiet: false)\n" + + "println('First HTTP Request Plugin Status: '+ response.getStatus())\n" + + "println('First HTTP Request Plugin Response: '+ response.getContent())\n" + + "\n" + + "echo \"Querying HTTPS with credential again (reentrability)...\"\n" + + "response = httpRequest(url: 'https://github.xcom/api/v3',\n" + + " httpMode: 'GET',\n" + + " authentication: authentication,\n" + + " consoleLogResponseBody: true,\n" + + " contentType : 'APPLICATION_FORM',\n" + + " validResponseCodes: '100:599',\n" + + " quiet: false)\n" + + "println('Second HTTP Request Plugin Status: '+ response.getStatus())\n" + + "println('Second HTTP Request Plugin Response: '+ response.getContent())\n" + + "\n"; + } + + @Test + @Issue({"JENKINS-70000", "JENKINS-70101"}) + public void testCertSimpleHttpRequestOnController() throws Exception { + // Check that credentials are usable with pipeline script + // running without a node{} + StandardCredentials credential = getCertificateCredentialSimple(); + store.addCredentials(Domain.global(), credential); + + // Configure the build to use the credential + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "proj"); + String script = + cpsScriptCredentialTestHttpRequest("cred_cert_simple", "CONTROLLER BUILT-IN"); + proj.setDefinition(new CpsFlowDefinition(script, false)); + + // Execute the build + WorkflowRun run = proj.scheduleBuild2(0).get(); + if (verbosePipelines) System.out.println(getLogAsStringPlaintext(run)); + + // Check expectations + j.assertBuildStatus(Result.SUCCESS, run); + // Got to the end? + j.assertLogContains("HTTP Request Plugin Response: ", run); + j.assertLogContains("Using authentication: cred_cert_simple", run); + // Currently we always try adding the material + // and report if not failed trying (might have + // had 0 entries to add though): + //j.assertLogNotContains("Added Trust Material from provided KeyStore", run); + j.assertLogContains("Added Key Material from provided KeyStore", run); + j.assertLogContains("Treating UnknownHostException", run); + } + + @Test + @Issue({"JENKINS-70000", "JENKINS-70101"}) + public void testCertSimpleHttpRequestOnNodeLocal() throws Exception { + // Check that credentials are usable with pipeline script + // running on a node{} (provided by the controller) + StandardCredentials credential = getCertificateCredentialSimple(); + store.addCredentials(Domain.global(), credential); + + // Configure the build to use the credential + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "proj"); + String script = + "node {\n" + + cpsScriptCredentialTestHttpRequest("cred_cert_simple", "CONTROLLER NODE") + + "}\n"; + proj.setDefinition(new CpsFlowDefinition(script, false)); + + // Execute the build + WorkflowRun run = proj.scheduleBuild2(0).get(); + if (verbosePipelines) System.out.println(getLogAsStringPlaintext(run)); + + // Check expectations + j.assertBuildStatus(Result.SUCCESS, run); + // Got to the end? + j.assertLogContains("HTTP Request Plugin Response: ", run); + j.assertLogContains("Using authentication: cred_cert_simple", run); + // Currently we always try adding the material + // and report if not failed trying (might have + // had 0 entries to add though): + //j.assertLogNotContains("Added Trust Material from provided KeyStore", run); + j.assertLogContains("Added Key Material from provided KeyStore", run); + j.assertLogContains("Treating UnknownHostException", run); + } + + @Test + @Issue({"JENKINS-70000", "JENKINS-70101"}) + public void testCertTrustedHttpRequestOnController() throws Exception { + // Check that credentials are usable with pipeline script + // running without a node{} + StandardCredentials credential = getCertificateCredentialTrusted(); + store.addCredentials(Domain.global(), credential); + + // Configure the build to use the credential + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "proj"); + String script = + cpsScriptCredentialTestHttpRequest("cred_cert_with_ca", "CONTROLLER BUILT-IN"); + proj.setDefinition(new CpsFlowDefinition(script, false)); + + // Execute the build + WorkflowRun run = proj.scheduleBuild2(0).get(); + if (verbosePipelines) System.out.println(getLogAsStringPlaintext(run)); + + // Check expectations + j.assertBuildStatus(Result.SUCCESS, run); + // Got to the end? + j.assertLogContains("HTTP Request Plugin Response: ", run); + j.assertLogContains("Using authentication: cred_cert_with_ca", run); + j.assertLogContains("Added Trust Material from provided KeyStore", run); + j.assertLogContains("Added Key Material from provided KeyStore", run); + j.assertLogContains("Treating UnknownHostException", run); + } + + @Test + @Issue({"JENKINS-70000", "JENKINS-70101"}) + public void testCertTrustedHttpRequestOnNodeLocal() throws Exception { + // Check that credentials are usable with pipeline script + // running on a node{} (provided by the controller) + StandardCredentials credential = getCertificateCredentialTrusted(); + store.addCredentials(Domain.global(), credential); + + // Configure the build to use the credential + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "proj"); + String script = + "node {\n" + + cpsScriptCredentialTestHttpRequest("cred_cert_with_ca", "CONTROLLER NODE") + + "}\n"; + proj.setDefinition(new CpsFlowDefinition(script, false)); + + // Execute the build + WorkflowRun run = proj.scheduleBuild2(0).get(); + if (verbosePipelines) System.out.println(getLogAsStringPlaintext(run)); + + // Check expectations + j.assertBuildStatus(Result.SUCCESS, run); + // Got to the end? + j.assertLogContains("HTTP Request Plugin Response: ", run); + j.assertLogContains("Using authentication: cred_cert_with_ca", run); + j.assertLogContains("Added Trust Material from provided KeyStore", run); + j.assertLogContains("Added Key Material from provided KeyStore", run); + j.assertLogContains("Treating UnknownHostException", run); + } + }