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

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

+1
Clojure CLI

大家好,

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

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

我认为这是由于防火墙使用的自签名根证书导致的,该证书用于控制与监控流量。

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


在大组织中,工作站通常位于防火墙之后,该防火墙控制和监控所有互联网的流量。公司创建自己的自签名根证书并将它们替换到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中的证书和SSL处理)。

相反,似乎通过使用 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 在运行时,。
    • 例如: (System/setProperty "javax.net.ssl.trustStoreType" "Windows-ROOT")
    • cons
      • 这仅在 Windows 上有效。
      • 强制使用通用密钥存储库,这也许不是用户始终希望看到的。
        • 可以通过增加一个脚本选项(例如 -W)来减轻这种影响,该选项作为一个选项传递给 clojure.tools.deps.alpha。缺点是用户/工具在每次调用 clojure 时都必须始终传递此标志。
      • 使用 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
      • 与前述内容相同的缺点,并且对于用户来说更繁琐,每次调用 clojure 时都必须键入 edn map。
  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 变量中,如 per https://maven.apache.org/guides/mini/guide-repository-ssl.html,但这没有效果。我怀疑这只能直接调用 mvn 命令行工具时才有效果。

1 个回答

+1

我认为这是所有正确的分析,我们遇到了几个需要设置jvm属性的情况,即在生成类路径的java调用中设置,正如你所发现的,现在还不能设置这些属性。

我们已经在https://clojure.atlassian.net/browse/TDEPS-165为这个问题创建了现有工单,但我还没有处理它。我怀疑最终的解决方案可能是打开新的clj选项或env变量,以便将jvm属性传递给类路径构造java调用。

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

还是担心将jvm选项传递给所有java调用,而不仅仅是用于类路径构造/下载的调用?
我对此思考不足,无法告诉您补丁应该做什么。我不认为应该将这些属性传递给每个java调用(特别是用户的程序)。
能否进一步说明一下,为什么在脚本中为每次Java调用传递jvm选项没有意义?我实在想不出任何缺点。谢谢
我已经在工单中更新了计划,并实际上已经实现了对新的CLJ_JVM_OPTS的支持,并发布了预发布版(1.11.1.1161)。不过目前还不支持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
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工具运行项目时使用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进行设置。
...