来自 Jenkins 管道的带有代理身份验证和客户端证书的 HTTP 请求
HTTP request from Jenkins pipeline with proxy auth and client certificate
我需要从我的 Jenkins 管道对需要客户端证书的外部 HTTPS 资源进行 HTTP 调用。此外,Jenkins 在需要身份验证的公司代理后面。
经过一番努力后,我设法使用 Apache HTTP 客户端使独立代码正常工作。问题是 Apache HTTP 客户端 类 不可序列化,因此代码在 Jenkins 中失败。这是我的 groovy 代码(简化):
KeyStore keyStore = KeyStore.getInstance("PKCS12")
keyStore.load(new ByteArrayInputStream(clientPfxBytes), "".toCharArray())
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
keyManagerFactory.init(keyStore, "".toCharArray())
SSLContext context = SSLContext.getInstance("TLS")
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom())
connection.setSSLSocketFactory(context.getSSLSocketFactory())
HttpHost proxy = new HttpHost(proxyHost, proxyPort, proxyProtocol)
HttpClientBuilder builder = HttpClientBuilder.create().setProxy(proxy).setSSLSocketFactory(new SSLConnectionSocketFactory(context))
CredentialsProvider credProvider = new BasicCredentialsProvider()
credProvider.setCredentials(
new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUser, proxyPass)
)
CloseableHttpClient client = builder.build()
def url = "https://my.target.url/with/path"
URIBuilder builder = new URIBuilder(url)
builder.setParameter("name", "value")
HttpPost request = new HttpPost(builder.build())
request.addHeader("x-customer-header", "custom-value")
request.addHeader("Content-Type", "application/xml")
request.setEntity(new StringEntity(inputXml))
HttpResponse response = client.execute(request)
int result = response.statusLine.statusCode
正如我指出的那样,它作为控制台应用程序可以独立正常工作,运行,但是当我尝试从 Jenkins 管道执行它时,我得到 NotSerializableException
:
an exception which occurred:
in field groovy.lang.Reference.value
in object groovy.lang.Reference@16eeaf
in field WorkflowScript$_getHttpClient_closure1.builder
in object WorkflowScript$_getHttpClient_closure1@3739c8
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.closures
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@4c1866
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@4c1866
Caused: java.io.NotSerializableException: org.apache.http.impl.client.HttpClientBuilder
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:926)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65)
at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56)
at org.jboss.marshalling.MarshallerObjectOutputStream.writeObjectOverride(MarshallerObjectOutputStream.java:50)
at org.jboss.marshalling.river.RiverObjectOutputStream.writeObjectOverride(RiverObjectOutputStream.java:179)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at java.util.HashMap.internalWriteEntries(Unknown Source)
at java.util.HashMap.writeObject(Unknown Source)
at sun.reflect.GeneratedMethodAccessor177.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.jboss.marshalling.reflect.JDKSpecific$SerMethods.callWriteObject(JDKSpecific.java:156)
at org.jboss.marshalling.reflect.SerializableClass.callWriteObject(SerializableClass.java:191)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1028)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:58)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:111)
at org.jenkinsci.plugins.workflow.support.pickles.serialization.RiverWriter.lambda$writeObject[=11=](RiverWriter.java:144)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:121)
at org.jenkinsci.plugins.workflow.support.pickles.serialization.RiverWriter.writeObject(RiverWriter.java:143)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgram(CpsThreadGroup.java:482)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgram(CpsThreadGroup.java:458)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgramIfPossible(CpsThreadGroup.java:445)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:372)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access0(CpsThreadGroup.java:83)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.call(CpsThreadGroup.java:244)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.call(CpsThreadGroup.java:232)
at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService.call(CpsVmExecutorService.java:64)
at java.util.concurrent.FutureTask.run(Unknown Source)
at hudson.remoting.SingleLaneExecutorService.run(SingleLaneExecutorService.java:131)
at jenkins.util.ContextResettingExecutorService.run(ContextResettingExecutorService.java:28)
at jenkins.security.ImpersonatingExecutorService.run(ImpersonatingExecutorService.java:59)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
那么问题是我该如何拨打这个电话?要么更改我的 Jenkins 管道中的某些内容,要么使用完全不同的方法。我确实可以完全控制 Jenkins 安装,并且可以根据需要对其进行任何更改。
如果没有别的办法,我将不得不编写一个插件来进行这种通信,但我宁愿避免走那条路。
花了很多时间,我仍然没有找到答案。我最终编写了自己的 Jenkins 管道步骤插件来执行此 HTTP 通信。对于那些感兴趣的人,这里是关于如何编写此类插件的相关文档的link:https://github.com/jenkinsci/workflow-step-api-plugin
我需要从我的 Jenkins 管道对需要客户端证书的外部 HTTPS 资源进行 HTTP 调用。此外,Jenkins 在需要身份验证的公司代理后面。
经过一番努力后,我设法使用 Apache HTTP 客户端使独立代码正常工作。问题是 Apache HTTP 客户端 类 不可序列化,因此代码在 Jenkins 中失败。这是我的 groovy 代码(简化):
KeyStore keyStore = KeyStore.getInstance("PKCS12")
keyStore.load(new ByteArrayInputStream(clientPfxBytes), "".toCharArray())
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
keyManagerFactory.init(keyStore, "".toCharArray())
SSLContext context = SSLContext.getInstance("TLS")
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom())
connection.setSSLSocketFactory(context.getSSLSocketFactory())
HttpHost proxy = new HttpHost(proxyHost, proxyPort, proxyProtocol)
HttpClientBuilder builder = HttpClientBuilder.create().setProxy(proxy).setSSLSocketFactory(new SSLConnectionSocketFactory(context))
CredentialsProvider credProvider = new BasicCredentialsProvider()
credProvider.setCredentials(
new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUser, proxyPass)
)
CloseableHttpClient client = builder.build()
def url = "https://my.target.url/with/path"
URIBuilder builder = new URIBuilder(url)
builder.setParameter("name", "value")
HttpPost request = new HttpPost(builder.build())
request.addHeader("x-customer-header", "custom-value")
request.addHeader("Content-Type", "application/xml")
request.setEntity(new StringEntity(inputXml))
HttpResponse response = client.execute(request)
int result = response.statusLine.statusCode
正如我指出的那样,它作为控制台应用程序可以独立正常工作,运行,但是当我尝试从 Jenkins 管道执行它时,我得到 NotSerializableException
:
an exception which occurred:
in field groovy.lang.Reference.value
in object groovy.lang.Reference@16eeaf
in field WorkflowScript$_getHttpClient_closure1.builder
in object WorkflowScript$_getHttpClient_closure1@3739c8
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.closures
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@4c1866
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@4c1866
Caused: java.io.NotSerializableException: org.apache.http.impl.client.HttpClientBuilder
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:926)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65)
at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56)
at org.jboss.marshalling.MarshallerObjectOutputStream.writeObjectOverride(MarshallerObjectOutputStream.java:50)
at org.jboss.marshalling.river.RiverObjectOutputStream.writeObjectOverride(RiverObjectOutputStream.java:179)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at java.util.HashMap.internalWriteEntries(Unknown Source)
at java.util.HashMap.writeObject(Unknown Source)
at sun.reflect.GeneratedMethodAccessor177.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.jboss.marshalling.reflect.JDKSpecific$SerMethods.callWriteObject(JDKSpecific.java:156)
at org.jboss.marshalling.reflect.SerializableClass.callWriteObject(SerializableClass.java:191)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1028)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:58)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:111)
at org.jenkinsci.plugins.workflow.support.pickles.serialization.RiverWriter.lambda$writeObject[=11=](RiverWriter.java:144)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:121)
at org.jenkinsci.plugins.workflow.support.pickles.serialization.RiverWriter.writeObject(RiverWriter.java:143)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgram(CpsThreadGroup.java:482)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgram(CpsThreadGroup.java:458)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgramIfPossible(CpsThreadGroup.java:445)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:372)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access0(CpsThreadGroup.java:83)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.call(CpsThreadGroup.java:244)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.call(CpsThreadGroup.java:232)
at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService.call(CpsVmExecutorService.java:64)
at java.util.concurrent.FutureTask.run(Unknown Source)
at hudson.remoting.SingleLaneExecutorService.run(SingleLaneExecutorService.java:131)
at jenkins.util.ContextResettingExecutorService.run(ContextResettingExecutorService.java:28)
at jenkins.security.ImpersonatingExecutorService.run(ImpersonatingExecutorService.java:59)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
那么问题是我该如何拨打这个电话?要么更改我的 Jenkins 管道中的某些内容,要么使用完全不同的方法。我确实可以完全控制 Jenkins 安装,并且可以根据需要对其进行任何更改。
如果没有别的办法,我将不得不编写一个插件来进行这种通信,但我宁愿避免走那条路。
花了很多时间,我仍然没有找到答案。我最终编写了自己的 Jenkins 管道步骤插件来执行此 HTTP 通信。对于那些感兴趣的人,这里是关于如何编写此类插件的相关文档的link:https://github.com/jenkinsci/workflow-step-api-plugin