将内部 CA 导入 Jenkins
Importing internal CA to Jenkins
我正在尝试使用 Jenkins 作业(管道)从 json 端点读取一些信息,然后根据该信息做一些事情。
它正在读取的端点是一个内部端点,应用程序可以通过 https 访问,证书由我们的内部 CA 自签名。
这里是示例代码,即运行通过管道解析json:
new JsonSlurper().parse(new URL('https://my.internal.url/info'))?.application?.git?.commit
为了让它更复杂一些,我还在管道中使用了来自 全局工具配置 的 Java 二进制文件。
当我运行管道时,我得到以下错误:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
Caused: sun.security.validator.ValidatorException: PKIX path building failed
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1496)
Caused: javax.net.ssl.SSLHandshakeException
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1959)
...
Caused: groovy.json.JsonException: Unable to process url: https://my.internal.url/info
at groovy.json.JsonSlurper.parseURL(JsonSlurper.java:416)
at groovy.json.JsonSlurper.parse(JsonSlurper.java:379)
...
所以现在我试图让 JVM 知道这个 CA,运行 是这段代码,但我不知道该怎么做。
我尝试下载根 CA 的 pem 文件,将其添加到系统证书中,然后将其导入到 java 的系统默认 cacerts
文件中(/etc/ssl/certs/java/cacerts
) 通过执行以下操作:
curl -Lso /etc/ssl/certs/rootca1.pem "<DOWNLOAD LINK>" \
&& chmod 777 /etc/ssl/certs/rootca1.pem \
&& mkdir -p /usr/share/ca-certificates/projectname \
&& cp /etc/ssl/certs/rootca1.pem /usr/share/ca-certificates/projectname/rootca1.crt \
&& echo "projectname/rootca1.crt" >> /etc/ca-certificates.conf \
&& update-ca-certificates -f
之后,当我使用 class 测试 java(如 https://gist.github.com/4ndrej/4547029)和 运行 java SSLPoke https://my.internal.url/info 443
中的 ssl 连接时,我可以成功连接。 Jenkins 管道仍然失败并出现相同的错误。
然后我想,也许管道使用了全局工具配置复制的 java 二进制文件(即使这还没有在 Jenkinsfile 中完成)到工作区,所以我将 CA 添加到该 工具 的 cacerts
密钥库已复制到工作区 ($WORKSPACE/tools/hudson.model.JDK/Java_8/jre/lib/security/cacerts
)。
之后我可以再次使用该二进制文件和 SSLPoke class 成功连接到 URL,但管道仍然失败...
所以我现在没主意了...有没有人遇到过类似的问题并设法解决了?没有移动到正式签名的证书(由于各种原因,内部 url 不是一个选项,甚至 Let's Encrypt)。
提前致谢!
所以我们设法解决了这个问题。问题是,我们没有让我们的代理交付整个证书链。除了浏览器之外,例如 openssl 二进制文件(因此也是 Java 实现)无法即时加载丢失的中间证书。
因此我们创建了一个证书,其中包含整个证书链(从叶证书到根证书),现在一切正常。
我没能解决这个问题,但我们有解决方法。我们将证书放入从容器中,并定期将其安装在我们所有节点上的所有 Java 安装中:
def jdksJava8 = ['Java 8', 'Java 8 Oracle']
def jdksJava11 = ['Java 11']
def nodeNames = env.'NODE_NAME' ? [env.'NODE_NAME'] : getNodeNames().sort().findAll { it.startsWith('my-node') }
stage 'Install myRootCA', {
currentBuild.displayName = "${env.'BUILD_NUMBER'}: ${env.'NODE_NAME' ?: 'all nodes'}"
parallel nodeNames.collectEntries { nodeName ->
[
(nodeName): {
node nodeName, {
jdksJava8.each { jdkName ->
withEnv(["JAVA_HOME=${tool jdkName}"]) {
sh '$JAVA_HOME/bin/java -version'
sh script: '$JAVA_HOME/bin/keytool -delete -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -alias myRootCA', returnStatus: true
sh script: '$JAVA_HOME/bin/keytool -import -trustcacerts -noprompt -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -alias myRootCA -file /usr/share/ca-certificates/myCompany/myRootCA.crt', returnStatus: true
}
}
jdksJava11.each { jdkName ->
withEnv(["JAVA_HOME=${tool jdkName}"]) {
sh script: '$JAVA_HOME/bin/keytool -delete -cacerts -storepass changeit -noprompt -alias myRootCA', returnStatus: true
sh script: '$JAVA_HOME/bin/keytool -importcert -cacerts -storepass changeit -noprompt -alias myRootCA -file /usr/share/ca-certificates/myCompany/myRootCA.crt', returnStatus: true
}
}
}
}
]
}
}
@NonCPS
def getNodeNames() {
jenkins.model.Jenkins.instance.nodes.collect { node -> node.name }
}
现在每晚 运行 使用此脚本进行作业。如果我们必须重新启动节点,我们 运行 之后手动安装证书。
我正在尝试使用 Jenkins 作业(管道)从 json 端点读取一些信息,然后根据该信息做一些事情。
它正在读取的端点是一个内部端点,应用程序可以通过 https 访问,证书由我们的内部 CA 自签名。
这里是示例代码,即运行通过管道解析json:
new JsonSlurper().parse(new URL('https://my.internal.url/info'))?.application?.git?.commit
为了让它更复杂一些,我还在管道中使用了来自 全局工具配置 的 Java 二进制文件。
当我运行管道时,我得到以下错误:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
Caused: sun.security.validator.ValidatorException: PKIX path building failed
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1496)
Caused: javax.net.ssl.SSLHandshakeException
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1959)
...
Caused: groovy.json.JsonException: Unable to process url: https://my.internal.url/info
at groovy.json.JsonSlurper.parseURL(JsonSlurper.java:416)
at groovy.json.JsonSlurper.parse(JsonSlurper.java:379)
...
所以现在我试图让 JVM 知道这个 CA,运行 是这段代码,但我不知道该怎么做。
我尝试下载根 CA 的 pem 文件,将其添加到系统证书中,然后将其导入到 java 的系统默认 cacerts
文件中(/etc/ssl/certs/java/cacerts
) 通过执行以下操作:
curl -Lso /etc/ssl/certs/rootca1.pem "<DOWNLOAD LINK>" \
&& chmod 777 /etc/ssl/certs/rootca1.pem \
&& mkdir -p /usr/share/ca-certificates/projectname \
&& cp /etc/ssl/certs/rootca1.pem /usr/share/ca-certificates/projectname/rootca1.crt \
&& echo "projectname/rootca1.crt" >> /etc/ca-certificates.conf \
&& update-ca-certificates -f
之后,当我使用 class 测试 java(如 https://gist.github.com/4ndrej/4547029)和 运行 java SSLPoke https://my.internal.url/info 443
中的 ssl 连接时,我可以成功连接。 Jenkins 管道仍然失败并出现相同的错误。
然后我想,也许管道使用了全局工具配置复制的 java 二进制文件(即使这还没有在 Jenkinsfile 中完成)到工作区,所以我将 CA 添加到该 工具 的 cacerts
密钥库已复制到工作区 ($WORKSPACE/tools/hudson.model.JDK/Java_8/jre/lib/security/cacerts
)。
之后我可以再次使用该二进制文件和 SSLPoke class 成功连接到 URL,但管道仍然失败...
所以我现在没主意了...有没有人遇到过类似的问题并设法解决了?没有移动到正式签名的证书(由于各种原因,内部 url 不是一个选项,甚至 Let's Encrypt)。
提前致谢!
所以我们设法解决了这个问题。问题是,我们没有让我们的代理交付整个证书链。除了浏览器之外,例如 openssl 二进制文件(因此也是 Java 实现)无法即时加载丢失的中间证书。
因此我们创建了一个证书,其中包含整个证书链(从叶证书到根证书),现在一切正常。
我没能解决这个问题,但我们有解决方法。我们将证书放入从容器中,并定期将其安装在我们所有节点上的所有 Java 安装中:
def jdksJava8 = ['Java 8', 'Java 8 Oracle']
def jdksJava11 = ['Java 11']
def nodeNames = env.'NODE_NAME' ? [env.'NODE_NAME'] : getNodeNames().sort().findAll { it.startsWith('my-node') }
stage 'Install myRootCA', {
currentBuild.displayName = "${env.'BUILD_NUMBER'}: ${env.'NODE_NAME' ?: 'all nodes'}"
parallel nodeNames.collectEntries { nodeName ->
[
(nodeName): {
node nodeName, {
jdksJava8.each { jdkName ->
withEnv(["JAVA_HOME=${tool jdkName}"]) {
sh '$JAVA_HOME/bin/java -version'
sh script: '$JAVA_HOME/bin/keytool -delete -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -alias myRootCA', returnStatus: true
sh script: '$JAVA_HOME/bin/keytool -import -trustcacerts -noprompt -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -alias myRootCA -file /usr/share/ca-certificates/myCompany/myRootCA.crt', returnStatus: true
}
}
jdksJava11.each { jdkName ->
withEnv(["JAVA_HOME=${tool jdkName}"]) {
sh script: '$JAVA_HOME/bin/keytool -delete -cacerts -storepass changeit -noprompt -alias myRootCA', returnStatus: true
sh script: '$JAVA_HOME/bin/keytool -importcert -cacerts -storepass changeit -noprompt -alias myRootCA -file /usr/share/ca-certificates/myCompany/myRootCA.crt', returnStatus: true
}
}
}
}
]
}
}
@NonCPS
def getNodeNames() {
jenkins.model.Jenkins.instance.nodes.collect { node -> node.name }
}
现在每晚 运行 使用此脚本进行作业。如果我们必须重新启动节点,我们 运行 之后手动安装证书。