jfrog / jfrog-azure-devops-extension

Apache License 2.0
48 stars 66 forks source link

Jfrog DotNet Core task does not use private CA certificate #461

Open fourpastmidnight opened 1 year ago

fourpastmidnight commented 1 year ago

Describe the bug

My build fails when attempting to push packages to Artifactory with tls: failed to verify certificate: x509: certificate signed by unknown authority.

We are using a private PKI, and all steps were followed that were specified in the README, as well as several additional attempts to get this to work.

Current behavior

The current behavior is that the Azure DevOps Server extension tasks are not always setting the JFROG_CLI_HOME environment variable, and even when they are, are not using the certificates located in $(Agent.ToolsDirectory)/_jf/security/certs to validate the Artifactory server certificate when using a private PKI.

Also, why the Jfrog CLI doesn't use the Windows Certificate Store when executing on Windows is beyond me. This is a whole lot of unnecessary work for DevOps teams using the Windows platform that does not need to be done--this is why there is the Windows Certificate Store--which is akin to the CA certificate bundles found on Linux.

Reproduction steps

  1. Use a private CA certificate for Artifactory for securing all communications within and to Artifactory
  2. Create an Azure DevOps Server Build Pipeline that builds a .NET project that produces NuGet package artifacts
  3. I do not have access to the build servers in my environment. Therefore, I wrote a file to the $(Agent.ToolsDirectory)/_jf/security/certs, as specified in the README (and at the JF CLI GH repo) called custom.crt via 2 inline PowerShell scripts. I tried 2 different versions of this file:
    1. Version 1: Contained the full certificate chain, from Artifactory Endpoint Entity cert to the Root CA certificate in reverse order (Endpoint Entity cert at top, CA cert at bottom), in PEM format.
    2. Version 2: Only contained the Chain of Trust certificates (not the Artifactory server certificate)
  4. Run the build, watch it fail.

After looking at the source code for this task, it appears that the JFROG_CLI_HOME environment variable is not set by this task.

So, I also tried using the generic Jfrog CLI V2 task, which does appear to set the JFROG_CLI_HOME environment variable, and recreated the command that is issued by the Jfrog DotNet Core task. But that failed in the same way. Then, to force the issue, I set a variable on my pipeline, JFROG_CLI_HOME with the value $(Agent.ToolsDirectory)/_jf (both with a trailing slash, and without a shown), but still I got the same TLS error.

Expected behavior

All tasks should set the JFROG_CLI_HOME environment variable. All tasks should use any certificate files found in $(Agent.ToolsDirectory)/_jf/security/certs, as that does not appear to be working AT ALL. I had to resort to using the --insecure-tls option while using the Jfrog CLI V2 task, instead of the built-in Jfrog DotNet Core task that should've done the trick.

Azure DevOps extension name and version

I don't know the name, but it's the v2 extension, v1.8.0

JFrog CLI version

2.50.4

Operating system type and version

Windows Server 2019

JFrog Artifactory version (if relevant)

7.63.7

JFrog Xray version (if relevant)

No response

JFrog Distribution version (if relevant)

No response

fourpastmidnight commented 11 months ago

I solved my particular problem.

The main issue is with the Jfrog Artifactory documentation for setting up TLS. Not everybody knows PKI well. I only just learned it well (ok, better 😉 ) a few years ago—but apparently, I had a gap in my knowledge.

The JFrog Artifactory documentation seems to assume that, when setting up the server with TLS (not using a reverse proxy), that you have a simple, 2-layer PKI hierarchy: Endpoint-Entity → Root CA. In this extremely simple case, you put the Root CA certificate into a file named ca-certs.crt and the endpoint entity certificate (whose subject common name is the IP address of the JFrog Artifactory node) into the custom-server.crt file. And this would work just fine.

What tripped me up, is that I have (at least) a 3-layer PKI hierarchy: Endpoint-Entity → Subordinate CA → Root CA. The documentation proffers no information on how to configure JFrog Artifactory in this situation (perhaps because it is presumed that configuring this correctly ought to be known?). What I didn't understand at the time is that when a server sends its endpoint-entity certificate to a requesting browser agent, it also sends any issuing subordinate CA certificates in the trust path back to the trust anchor Root CA for the endpoint-entity. And because I didn't know this, and the CA certificate file is literally named ca-certs.crt (notice it's plural--which is fine if you have multiple Root CAs you need to have JFrog Artifactory trust), I believed that the subordinate CA certificate should also be placed in the ca-certs.crt file rather than the custom-server.crt file—and so the custom-server.crt file contained only the endpoint-entity certificate. So, while browsing the JFrog Artifactory application interface, the browser indicated that the connection is secure (On my local machine, the Subordinate CA certificate must be in my certificate store, perhaps under Intermediate Trust Authorities (on Windows)). But when the JF CLI tried to connect to JFfrog Artifactory, and because it is not using the Windows Certificate Store, when the endpoint-entity certificate was validated, the certificate chain of trust could not be completed back to a known Root CA certificate (even though the Subordinate CA certificate was in ca-certs.crt—I assume this has something to do with how this file is processed during validation. In any event, there's no problem with validation; my configuration is just wrong.)

After searching about certificate bundles, I came across a Security Stack Exchange post that helped me understand how web servers send not only the endpoint-entity certificate, but also subordinate CA certificates in the endpoint-entity certificate trust path, I realized I had mis-configured the certificate files.

After moving the Subordinate CA certificate to the custom-server.crt file and removing it from ca-certs.crt, the JFrog CLI tasks worked as intended.

I implore JFrog to please make the documentation around configuring JFrog Artifactory more clear. PKI is, unfortunately, NOT common knowledge. I took the time to learn it because I was, at one point, afraid of it to the point where it was just ridiculous—I just needed to learn it. But even so, this gap in my knowledge lead to a lot of tinkering, and had the documentation been more clear about how to configure JFrog Artifacotry, this would have been unnecessary wasted time and effort, not only by myself, but also JFrog support staff.