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

欢迎!请参阅关于页面以了解更多关于该功能的信息。

+1
&# Emmamento Worm 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 KeyStore,它与工作站上可用的通用密钥库是分开的。因此,任何通过防火墙通过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)。

相反,似乎更容易使用java.next.ssl.*属性(请参见https://stackoverflow.com/questions/5871279/ssl-and-cert-keystore)来指示Java查看通用或备用密钥库。

在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

但它并没有。结果是 $JvmOpts 变量并未通过这里的Powershell脚本传递给 prep 命令 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 在运行时
    • .e.g. (System/setProperty "javax.net.ssl.trustStoreType" "Windows-ROOT")
    • cons
      • 这仅在Windows上有效。
      • 这强制使用通用密钥库,这可能与用户总是想要的不同。
        • 可以通过添加新的脚本选项(例如 -W)来减轻这种影响,该选项将作为选项传递给 clojure.tools.deps.alpha。缺点是用户/工具必须在每次调用时都传递此标志。
      • 如果代码的其他部分之前已建立任何SSL连接,则使用 System/setProperty 设置 javax.net.ssl.tustStoreType 属性可能不会有任何效果。
  2. 与上一条相同,但引入了新的脚本选项(例如 -Sssl EDN),其中的 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.alphamake-classpath2 函数解析,该函数使用 System/setProperty 在运行时设置属性。
    • e.g. 示例 clj 用户配置 {:clojure.tools.deps.alpha/ssl {:trustStore "Windows-ROOT"}}
    • cons
      • 如果代码的其他部分之前已建立任何SSL连接,则使用 System/setProperty 设置 javax.net.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

我认为这是所有正确分析,我们遇到一些情况,其中jvm属性可能需要设置在生成类路径的java调用中,如您所发现的那样,目前无法设置。

我们确实有一个现有的名为https://clojure.atlassian.net/browse/TDEPS-165的工单,但我还没有开始处理它。我怀疑最终的解决方案将是打开一个新的clj选项或环境变量,以便将jvm属性传递到类路径构造的java调用中。

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

还是对将jvm选项传递给所有java调用有疑虑,而不仅仅是用于类路径构造/下载的那个?
我还没有足够考虑这个问题以告诉你补丁应该做什么。我认为将这些传递给每个java调用是没有意义的(特别是对于用户的程序)。
能否请您详细说明一下,为什么在脚本中将 jvm 选项传递给每次 java 调用没有意义?我想不到任何缺点。谢谢
我已经在工单中更新了计划,并且实际上实现了一个预发布(1.11.1.1161)支持新的 CLJ_JVM_OPTS。尽管如此,它目前还不支持 Windows,并期待任何有关如何为 PowerShell 版本中的 https://github.com/clojure/brew-install/commit/d97cf4145ccfee712ec99b906f7bf19b1406c131 找到正确咒语的建议 - 最棘手的事情是正确处理多个参数。
嗨,

我已经对如何将额外的 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

谢谢
好,现在它已经发布为1.11.1.1165。

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

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

使用PowerShell提示中的环境变量来设置项目依赖项的下载时使用Windows证书存储

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

当通过Clojure工具运行项目时使用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依赖项时,我得到与原始问题相同的错误(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来设置它们。
...