2024 状态调查!中分享你的想法。

欢迎!请查看关于页面以获取更多关于如何工作的信息。

+1
Clojure CLI

你好,

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

无法从 central 转移工件…
(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脚本中 https://github.com/clojure/brew-install/blob/b91fb78e321b5e39bedda594f5d578579d448d19/src/main/resources/clojure/install/ClojureTools.psm1#L388,并未将$JvmOpts变量传递给pre命令

& $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
    • .e.g. (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),其中包含javax.net.ssl.*属性,并将作为此类传递给clojure.tools.deps.alpha,在运行时设置。
    • e.g. 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.alpha s make-classpath2 fn在运行时使用System/setProperty设置属性。
    • e.g. 示例clj用户配置 {:clojure.tools.deps.alpha/ssl {:trustStore "Windows-ROOT"}}
    • cons
      • 使用System/setProperty在运行时设置javax.net.ssl.*属性可能没有效果,如果代码的其他部分之前已经进行了任何SSL连接。
  4. 更新脚本,以便将-J JVM选项传递给-P命令的Java调用(我已经测试过该功能)。
    • e.g. clojure -J-D'javax.net.ssl.trustStoreType=Windows-ROOT' -P
    • cons
      • 用户/工具仍然必须在每次调用clojure时提供-J-Djavax.net.ssl.*选项。
  5. 与前面的相同,但引入一个名为CLJ_JVMOPTS的环境变量,其值插入到脚本的$JvmOpts变量中。
    • e.g. 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选项或env变量,以便将jvm属性传递给类路径构造的Java调用。

我是否可以创建一个引入新CLJ_JVMOPTS环境变量的原型补丁,该变量将与标准-J选项合并,并传递到所有脚本中的Java调用中?

或者,您对将jvm选项传递给所有Java调用而不是仅传递用于类路径构造/下载的选项有任何顾虑吗?
我对此还没有进行足够的思考,无法告诉您补丁应该做什么。我认为将这些传递给每个Java调用(尤其是用户的程序)是没有意义的。
请详细说明一下为什么在脚本中将jvm选项传递给每个Java调用是没有意义的?我想不出任何缺点。谢谢。
我已经在工单中更新了计划,并且实际上实现了对新的 CLJ_JVM_OPTS 的支持,同时进行了预发布(1.11.1.1161)。但是目前还不支持 Windows,如果能给出在 PowerShell 版本中实现 right equivalent incantation 的建议将不胜感激 - 这个复杂的点在于正确处理多个参数。
你好,

我进行了一点点研究,如何将额外的 JVM 参数传递给 java 执行,我觉得唯一的好方法是通过 `JAVA_TOOL_OPTIONS` 环境变量来传递。如果我们能将额外的参数添加到这个环境变量的末尾,那么它们将被 java 工具捕获并按预期处理。

我创建了一个草稿实现来展示: https://github.com/ikappaki/brew-install/commit/3584b02caced8e4a15c6993c73ceb4c202a6c623。这种方法的一个轻微的复杂性是我们必须在 java 命令执行完成后恢复环境变量,以避免其更新值泄漏到环境中。

我考虑的另一种选项是使用 `iex` 解析环境变量,如 `$CljJvmOpts = iex "echo $env:CLJ_JVM_OPTS"`,但这会破坏标准的引号机制,并且很难使用引号和双引号传递。

将任何用户值传递给 $java 命令行调用的问题在于,我们可能会遇到不希望的情景,比如命令行中出现分号或其他 PowerShell 操作符,这些问题可能会破坏调用语法,更不用说它可能变成一个安全风险(例如,将环境变量设置为 `;rm -fr ./;`)。

你有什么想法?谢谢
是的,我不太喜欢这个东西。我已经推送了一个更改,增加了在Windows上支持在空格处分割环境变量并将其展开到命令行中的功能,这似乎与我尝试过的所有内容都配合得很好。如果你可以尝试的话,这个功能在版本1.11.1.1165中(下载器在 https://download.clojure.org/install/win-install-1.11.1.1165.ps1
看起来不错,但要注意,不寻常的值可能会影响Java调用,正如我之前的评论中提到的。对于我的测试用例,它工作得很好。

请问第二个新的JAVA_OPTS环境变量的用途是什么,以及为什么Java调用在这两者之间分割?我原本预期将所有Java调用都传递给单个CLJ_JVM_OPTS环境变量。

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

谢谢
好的,现在 diese wurde als 1.11.1.1165发布。

有两个属性,因为有两个上下文(可能)不同的需求,还有很多可能希望在其中一个而不在另一个上设置属性的情况。
谢谢!

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

通过在PowerShell提示符中设置环境变量,使用Windows证书存储来下载项目依赖项

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

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

$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没有检测到这一点。

关于如何调试这个问题的任何建议?

谢谢
我认为这个Stack Overflow答案https://stackoverflow.com/a/5871352/7671详细说明了可能需要设置的属性,并且可以通过CLJ_JVM_OPTS来设置它们。
...