2024年度Clojure调查中分享您的想法!

欢迎!请参阅关于页面了解有关此工作方式的一些更多信息。

+1
Clojure CLI

你好,

在尝试使用clojure工具在MS-Windows的严格防火墙后下载项目依赖项时,我遇到了PKIX证书异常。

无法从中央仓库传输工件...
(https://repo1.maven.org/maven2/)
sun.security.validator.ValidatorException: PKIX路径构建失败
sun.security.provider.certpath.SunCertPathBuilderException: 无法
找到到请求目标的有效证书路径

我相信这是由于防火墙使用的自签名根证书所导致的,用于控制和管理网络流量。

以下是分析,并对冗长的阅读表示歉意。


在大型组织中,工作站通常位于防火墙之后,该防火墙控制并监控所有来往互联网的流量。公司创建自己的自签名根证书并替换https流量中找到的证书,以解密和分析数据流,实际上是利用中间人攻击来对自己有利,这也是一种常见做法。

为了实现这一点,发起连接的用户工作站需要将其自签名证书安装到受信任的证书存储中。在Windows上,这将是在工作站上可用的受信任根证书密钥存储库。

Java自带其自己的证书存储,即java密钥存储,它独立于工作站上可用的常规密钥存储。因此,通过防火墙进行互联网的任何Java https连接很可能因为未知证书而失败,因为自签名证书尚未安装在Java密钥存储中。

这不利于clojure/clj命令行工具下载库,因为它在Java密钥存储中找不到自签名证书。例如,尝试从这样的计算机访问互联网时将抛出异常。

 > clojure -P
 
 Error building classpath ... 
 org.eclipse.aether.resolution.ArtifactDescriptorException:   Failed to
 read artifact descriptor for ....  ...  Caused by:
 org.eclipse.aether.resolution.ArtifactResolutionException:   Could not
 transfer artifact ... from/to central
 (https://repo1.maven.org/maven2/):   
 sun.security.validator.ValidatorException: PKIX path building failed:
      sun.security.provider.certpath.SunCertPathBuilderException:
        unable to find valid certification path to requested target  ...
  ...

我想至少有两种解决方案可以绕过这个问题。
1. 在Java密钥存储中安装缺失的证书,或者
2. 指导Java实例查看常规密钥存储。

似乎没有直接的方法可以找到缺失的证书,可能需要用户的一些努力,使用java keytool将这些证书安装到Java密钥存储中(请参阅https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html中的证书和SSL工作)。

相反,似乎更容易通过使用java.next.ssl.*属性来指导Java查看常规或替代密钥存储(请参阅https://stackoverflow.com/questions/5871279/ssl-and-cert-keystore)。

在Windows上,我假设大多数使用这种限制性防火墙的用户都在使用这个系统,所以只需设置一个简单的Java属性来指明Java使用通用的Windows密钥库(https://stackoverflow.com/questions/41257366/import-windows-certificates-to-java

javax.net.ssl.trustStoreType=Windows-ROOT

鉴于这一点,我原本期望以下内容在Windows上可以工作

clojure -J-D'javax.net.ssl.trustStoreType=Windows-ROOT' -P

但它并没有。结果证明在这个powershell脚本中,$JvmOpts变量没有被传递到打包命令中https://github.com/clojure/brew-install/blob/b91fb78e321b5e39bedda594f5d578579d448d19/src/main/resources/clojure/install/ClojureTools.psm1#L388

& $JavaCmd -classpath $ToolsCp clojure.main -m clojure.tools.deps.alpha.script.make-classpath2 --config-user $ConfigUser --config-project $ConfigProject --basis-file $BasisFile --libs-file $LibsFile --cp-file $CpFile --jvm-file $JvmFile --main-file $MainFile --manifest-file $ManifestFile @ToolsArgs

我可以想到几种利用Java属性来解决这个问题的方法,从最侵入性/特定到一般情况

  1. 在Windows上运行时,将负责下载依赖的clojure.tools.deps.alpha.script.make-classpath2函数设置为javax.net.ssl.trustStoreType=Windows-ROOT
    • 例如:(System/setProperty "javax.net.ssl.trustStoreType" "Windows-ROOT")
    • cons
      • 这仅在Windows上有效。
      • 这会强制使用通用密钥库,而这可能不是用户总是想要的。
        • 这可以通过具有新脚本选项(例如-W)来缓解,它将作为选项传递到clojure.tools.deps.alpha。缺点是用户/工具必须在每次调用时始终传递此标志。
      • 使用System/setProperty在运行时设置javax.net.ssl.tustStoreType属性可能不会有任何效果,如果代码的其他部分已建立任何SSL连接。
  2. 与上一个相同,但使用一个新脚本选项(例如-Sssl EDN),其EDN为javax.net.ssl.*属性,并将以这种形式传递给clojure.tools.deps.alpha,在运行时设置。
    • 例如:clojure -Sssl {:trustStoreType "Windows-ROOT"} -P
    • cons
      • 与第1个相同,但用户体验更加繁琐,因为用户必须在每次调用clojure时输入edn映射。
  3. 发明一个新的deps.edn键(例如:clojure.tools.deps.alpha/ssl),其中对应该对java.net.ssl.*属性,可以在用户配置的deps.edn中设置并由clojure.tools.deps.alphamake-classpath2函数在运行时使用System/setProperty设置属性。
    • 例如:示例clj用户配置{:clojure.tools.deps.alpha/ssl {:trustStore "Windows-ROOT"}}
    • cons
      • 使用System/setProperty在运行时设置javax.net.ssl.*属性可能不会有任何效果,如果代码的其他部分已建立任何SSL连接。
  4. 更新脚本,以便将-J JVM选项传递到-P命令的Java调用中(我已测试过此功能)。
    • 例如:clojure -J-D'javax.net.ssl.trustStoreType=Windows-ROOT' -P
    • cons
      • 用户/工具仍必须在每次调用clojure时提供-J-Djavax.net.ssl.*选项。
  5. 与上一个相同,但引入一个CLJ_JVMOPTS环境变量,其值将插入到脚本的$JvmOpts变量中。
    • 例如:set CLJ_JVMOPTS=-D'javax.net.ssl.trustStoreType=Windows-ROOT',然后clojure -P

我更倾向于第5个,因为它似乎没有缺点,可以设置一次并在所有clojure调用中应用,从而提升用户/工具的使用体验。

请告诉我您的想法。我很乐意处理脚本和/或tools.deps.alpha库中的更改。

谢谢

PS:我也尝试在MAVEN_OPTS变量中设置属性,就像在https://maven.apache.org/guides/mini/guide-repository-ssl.html中所做的,但它没有任何效果。我怀疑这只有在直接调用mvn命令行工具时才会生效。

1 个回答

+1

我认为这是所有正确的分析,我们在几个案例中遇到了需要在生成类路径的java调用上设置jvm属性的,正如你所发现的,目前无法设置。

我们已经在https://clojure.atlassian.net/browse/TDEPS-165上对此问题创建了现有的工单,但我还没有着手处理。我相信最终解决方案可能是通过新的clj选项或环境变量来允许向类路径构造函数java调用传递jvm属性。

我应该继续创建一个原型补丁,引入一个名为CLJ_JVMOPTS的新环境变量,并将其与标准的-J选项合并,传递给所有脚本中的Java调用吗?

还是担心将jvm选项传递给所有Java调用(而不仅仅是用于类路径构造/下载的)?
我还没有想这么多,无法告诉你补丁应该做什么。我认为将它们传递给每一个Java调用没有意义(特别是用户的程序调用)。
您能否进一步说明为什么在脚本中将jvm选项传递给每个java调用没有意义?我想不出任何缺点。谢谢
你好,









是的,我不太喜欢那样。我已经推送了一个更新,在Windows中添加了对空格分隔的env变量和splat到命令行的支持,这个更新在我的测试中表现得很不错。如果您能尝试一下,可以在版本1.11.1.1165中找到它(安装器在https://download.clojure.org/install/win-install-1.11.1.1165.ps1
by
在之前评论中提到,由于不规则的值可能会对Java调用产生不利影响,所以仍然要谨慎。但在我的测试用例中,它的表现良好。

可以请问一下第二个新的JAVA_OPTS环境变量的用途,以及为什么Java调用在这两者之间分开?我原本期待一个单独的CLJ_JVM_OPTS环境变量传递给所有的Java调用。

https://github.com/clojure/brew-install/commit/7914954030ca21f7b928b6f064ace086efdc9057

谢谢
by
好的,现在它已经作为1.11.1.1165版本发布了。

有两个属性,因为有两个具有(可能)不同需要的上下文,而且在许多情况下,您可能想在其中一个而不是另一个上设置属性。
by
谢谢!

以下是如何使用新的环境变量解决这个问题示例用例,供其他有兴趣的人参考。

要在PowerShell提示符中设置环境变量以在下载项目依赖关系时使用Windows证书存储

$env:CLJ_JVM_OPTS = "-Djavax.net.ssl.trustStoreType=Windows-ROOT"

要通过Clojure Tools运行项目并使用Windows证书存储,在PowerShell提示符中设置环境变量

$env:JAVA_OPTS = "-Djavax.net.ssl.trustStoreType=Windows-ROOT"
你好,

希望我能参与这个讨论。这里也有类似的问题。企业防火墙进行SSL检查并替换SSL证书(ZScaler)。我最简单的方法是将证书安装到CA存储中。

"%JAVA_HOME%\bin\keytool.exe" -import -trustcacerts -cacerts -storepass XXX -file somefile.cer

并且我可以验证它是否正常工作(使用https://github.com/MichalHecko/SSLPoke
> java.exe SSLPoke download.clojure.org 443
成功连接

但是当安装clojure依赖关系时,我遇到了与OP相同的问题(PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到指向请求目标的认证路径)。

我认为我不需要使用Windows-ROOT信任存储库。我只需要默认的Java CA存储库(位于$JAVA_HOME/lib/security/cacerts)并且这应该工作,但似乎Clojure没有检测到这一点。

关于如何调试此问题的建议?

谢谢
我认为这个stackoverflow答案https://stackoverflow.com/a/5871352/7671对可能需要设置的属性有很好的概述,并且您可以通过CLJ_JVM_OPTS进行设置。
...