Currently, on Unix and after #77865 on macOS and Windows, the behavior of $SSL_CERT_FILE / $SSL_CERT_DIR is:
- if the file pointed to by
$SSL_CERT_FILE exists, it is loaded
- if the file does not exist, it is silently skipped
- if
$SSL_CERT_FILE is empty/unset, the first file from a default list that works is loaded
- if any directories pointed to by
$SSL_CERT_DIR exist, they are all loaded
- if they do not exist, they are silently skipped
- if
$SSL_CERT_DIR is empty/unset, all the directories from a default list are loaded
- if SetFallbackRoots was/is called, and the logic above produces no roots or x509usefallbackroots=1 is set, the fallback roots are used instead
- on macOS and Windows, if x509sslcertoverrideplatform=0 is set or
$SSL_CERT_FILE / $SSL_CERT_DIR are both empty/unset, the platform verifier is used
This is, uh, harder to explain in prose than in code, which is never a good sign.
Also, it has the following unexpected behaviors:
- even if setting one of
$SSL_CERT_FILE / $SSL_CERT_DIR, on Unix the default paths for the other one will be searched
- if setting
$SSL_CERT_FILE / $SSL_CERT_DIR to a path with a typo, no error is surfaced (and the default paths might still get used, or the fallbacks)
- on macOS and Windows, setting
$SSL_CERT_FILE / $SSL_CERT_DIR to an empty file/directory or a path with a typo causes the fallback roots to be used if SetFallbackRoots was called, and an empty pool if SetFallbackRoots was not called (instead of using the platform verifier)
I think we should aim to preserve these invariants instead:
- if setting either
$SSL_CERT_FILE / $SSL_CERT_DIR, that's an absolute statement and we'll either use those or fail
- the fallback roots are only used if x509usefallbackroots=1 is set, or if no default ones are available on Unix
I propose we change the logic to:
- if x509usefallbackroots=1 is set and SetFallbackRoots was called, use the fallback roots
- if x509sslcertoverrideplatform=0 is set on Windows / macOS, use the platform verifier
- if either/both of
$SSL_CERT_FILE / $SSL_CERT_DIR are not empty, they are searched—ignoring the defaults—and if they yield zero roots and an error was encountered, SystemCertPool returns an error (the assumption here is that they might be set to multiple paths to get a fallback behavior, but if none work it was probably unintended, while SSL_CERT_FILE=/dev/null or SSL_CERT_DIR=/var/empty might be intentional)
- on Unix, search the default paths, and if they yield zero roots, use the fallback roots (if any)
- on Windows / macOS, use the platform verifier
We should introduce a x509legacyroots=1 GODEBUG setting to revert to the previous behavior, slated to be removed in two years.
/cc @golang/security
Currently, on Unix and after #77865 on macOS and Windows, the behavior of
$SSL_CERT_FILE/$SSL_CERT_DIRis:$SSL_CERT_FILEexists, it is loaded$SSL_CERT_FILEis empty/unset, the first file from a default list that works is loaded$SSL_CERT_DIRexist, they are all loaded$SSL_CERT_DIRis empty/unset, all the directories from a default list are loaded$SSL_CERT_FILE/$SSL_CERT_DIRare both empty/unset, the platform verifier is usedThis is, uh, harder to explain in prose than in code, which is never a good sign.
Also, it has the following unexpected behaviors:
$SSL_CERT_FILE/$SSL_CERT_DIR, on Unix the default paths for the other one will be searched$SSL_CERT_FILE/$SSL_CERT_DIRto a path with a typo, no error is surfaced (and the default paths might still get used, or the fallbacks)$SSL_CERT_FILE/$SSL_CERT_DIRto an empty file/directory or a path with a typo causes the fallback roots to be used if SetFallbackRoots was called, and an empty pool if SetFallbackRoots was not called (instead of using the platform verifier)I think we should aim to preserve these invariants instead:
$SSL_CERT_FILE/$SSL_CERT_DIR, that's an absolute statement and we'll either use those or failI propose we change the logic to:
$SSL_CERT_FILE/$SSL_CERT_DIRare not empty, they are searched—ignoring the defaults—and if they yield zero roots and an error was encountered,SystemCertPoolreturns an error (the assumption here is that they might be set to multiple paths to get a fallback behavior, but if none work it was probably unintended, whileSSL_CERT_FILE=/dev/nullorSSL_CERT_DIR=/var/emptymight be intentional)We should introduce a x509legacyroots=1 GODEBUG setting to revert to the previous behavior, slated to be removed in two years.
/cc @golang/security