-
Notifications
You must be signed in to change notification settings - Fork 144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SCAN4NET-227 Use system trusted certificate or JVM certificate store #2330
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good for me and should do the trick. I left some small comments but overall this looks promising.
|
||
// Act | ||
processor.Update(config); | ||
|
||
// Assert | ||
config.LocalSettings.Should().ContainSingle(x => x.Id == SonarProperties.TruststorePassword && x.Value == input); | ||
config.ScannerOptsSettings.Should().ContainSingle(); | ||
config.ScannerOptsSettings.Should().HaveCount(2); | ||
AssertExpectedScannerOptsSettings("javax.net.ssl.trustStore", javaHomeCacerts.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), config); | ||
AssertExpectedScannerOptsSettings("javax.net.ssl.trustStorePassword", expected, config); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could additionally assert that fileWrapper.Exists(javaHomeCacerts)
was called once.
var config = new AnalysisConfig { LocalSettings = [] }; | ||
using var envScope = new EnvironmentVariableScope(); | ||
envScope.SetVariable("JAVA_HOME", javaHome); | ||
envScope.SetVariable("SONAR_SCANNER_OPTS", "-Djavax.net.ssl.trustStorePassword=itchange"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if we also set another property here, e.g. -Xmx2048m
?
MapProperty(config, "javax.net.ssl.trustStore", PropertyValueOrDefault(SonarProperties.TruststorePath, LocalSettings.TruststorePath), ConvertToJavaPath, EnsureSurroundedByQuotes); | ||
var password = PropertyValueOrDefault(SonarProperties.TruststorePassword, LocalSettings.TruststorePassword); | ||
MapProperty(config, "javax.net.ssl.trustStorePassword", password, EnsureSurroundedByQuotes); | ||
if (new Uri(LocalSettings.ServerInfo.ServerUrl).Scheme != "https" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constructor throws. Consider using TryCreate(url, UriKind.Absolute, out var uri)
instead.
MapProperty(config, "javax.net.ssl.trustStore", PropertyValueOrDefault(SonarProperties.TruststorePath, LocalSettings.TruststorePath), ConvertToJavaPath, EnsureSurroundedByQuotes); | ||
var password = PropertyValueOrDefault(SonarProperties.TruststorePassword, LocalSettings.TruststorePassword); | ||
MapProperty(config, "javax.net.ssl.trustStorePassword", password, EnsureSurroundedByQuotes); | ||
if (new Uri(LocalSettings.ServerInfo.ServerUrl).Scheme != "https" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (new Uri(LocalSettings.ServerInfo.ServerUrl).Scheme != "https" | |
if (new Uri(LocalSettings.ServerInfo.ServerUrl).Scheme != Uri.UriSchemeHttps |
return null; | ||
} | ||
|
||
return sonarScannerOpts.Split(' ').FirstOrDefault(x => x.StartsWith($"-D{JAVAX_NET_SSL_TRUST_STORE_PASSWORD}=")) is { } truststorePassword |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is safe to specify StringComparison.OrdinalIgnoreCase
:
return sonarScannerOpts.Split(' ').FirstOrDefault(x => x.StartsWith($"-D{JAVAX_NET_SSL_TRUST_STORE_PASSWORD}=")) is { } truststorePassword | |
return sonarScannerOpts.Split(' ').FirstOrDefault(x => x.StartsWith($"-D{JAVAX_NET_SSL_TRUST_STORE_PASSWORD}=", StringComparison.OrdinalIgnoreCase)) is { } truststorePassword |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure, I think the properties are case sensitive.
During my test I made several times the mistake of writing down the javax properties like truststore
instead of trustStore
.
var javaHome = Environment.GetEnvironmentVariable("JAVA_HOME"); | ||
if (javaHome is null) | ||
{ | ||
// TODO: propagate the error? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. A debug log should be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will not fail before the end step then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the JRE provisioning is enabled, JAVA_HOME
does not have to be set. There might be cases where the provisioned JRE trusts the server anyhow. The JRE comes with a default set of CA roots. If we fail it here, it would break https servers with a proper purchased certificate or one from https://letsencrypt.org/
Or am I miss something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is from letsencrypt, it should be trusted without giving a store.
My understanding of purchased certificates is that you don't need to provide a truststore as they should be valid from some public CA.
I would be for failing the execution to follow the fail fast principle but I am not 100% sure that in this case there is no scenario where it would work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scenario is:
- JRE provisioning is enabled and executed
- JAVA_HOME is not set
- The server is providing a certificate from a CA root trusted by the provisioned JRE (e.g. Let's Encrypt, Digi Cert, etc.)
- The user doesn't specify anything (
truststorePath is null
) - We are on Linux
FindJavaTruststorePath
is called
-> We would fail the begin step, even though everything would just work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we safely assume that the cacerts
of JAVA_HOME would be the same the one from the provisioned JRE?
If we take the same scenario with JAVA_HOME set, we will tell the provisioned JRE to use the local cacerts
rather that the one download. I wonder if there is a possibility for the scenario to succeed with one and not the other where in theory it should succeed with both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The root ca certs change on a regular basis (see e.g. here https://learn.microsoft.com/en-us/security/trusted-root/release-notes). It is likely that we will have different CA roots in both locations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I think this is a problem the scanner-cli may have as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also: As a user, you can install custom root CAs in your JAVA_HOME, but you can not install root CAs in the provisioned JRE. Therefore, we should prefer JAVA_HOME over the provisioned JRE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me, I will only do a log.
return null; | ||
} | ||
|
||
var javaTruststorePath = Path.Combine(javaHome, "lib", "security", "cacerts"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a link to some kind of documentation or ticket about the source of these path.
@@ -131,6 +131,7 @@ private static bool ServerCertificateCustomValidationCallback(X509Certificate2Co | |||
{ | |||
if (errors is SslPolicyErrors.None) | |||
{ | |||
logger.LogWarning(Resources.WARN_CertificateTrustedBySystem); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The remote server certificate is trusted by the operating system. Provided truststore was not required.
If this is issued as a warning, it sounds like the command line argument can be removed from the begin step call. Is this always true? I know we set JAVAX_NET_SSL_TRUST_STORE_TYPE=Windows-ROOT
but is this always working as intended. And what about Linux, here? I would turn this into a debug message. It does no harm if the user specified the file, right?
I would also remove this part: Provided truststore was not required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will update the message. I think it is important to keep in the message that a truststore has been provided to have context.
Something like: Provided truststore was not used.
|
1068943
to
70e2345
Compare
70e2345
to
7bb1f16
Compare
SCAN4NET-227