Description
Abstract
This proposal introduces a new mechanism for configuring the standard library default crypto/tls.Config settings using a file on disk. The addition of this on-disk configuration mechanism enables consistent and auditable cryptographic policies to be deployed at scale. This configuration file would be scoped to only the configuration of the crypto/tls default ciphers, curves, and min/max TLS versions allowed. Allowing for this type of on-disk configuration will give those deploying Go applications using the standard library TLS stack greater control over the cryptographic settings used by an application, especially when the one deploying the software is not in direct control of the source code.
Background
Many operating systems allow for the creation of on-disk cryptographic policies which are then consumed by processes running on that system in order to apply default system-wide configuration [1][2]. Allowing for this type of centralized configuration enables those on the operations side to have peace of mind knowing that their systems are secure and auditable.
Proposal
This proposal describes both a way to read an on-disk configuration file and subsequently apply that configuration via the Config
object by leveraging the unexported methods of that Config
object in order to enforce the loaded cryptographic policy. This section will describe the technical details of the implementation.
Configuration format
The on-disk configuration file will be a JSON file, allowing use of the standard library encoding/json
package to easily read and parse the file.
Locating the On-Disk Configuration
This proposal introduces two ways to specify the location of the on disk configuration file. The first would be by way of the environment variable GOTLSCONFIG
having been set to a full path to the configuration file, which would then be used to set the value of a variable within the crypto/tls
package to be used to load and parse the configuration. The second option is to specify the path directly via -ldflags=”-X crypto/tls.configFilepath=/path/to/config.json”
.
Loading the Configuration From Disk
The configuration would be read and parsed using the encoding/json
package and would be used to set an internal default configuration variable.
Using the Configuration
The internal configuration would be applied automatically to the lists of default curves, signature algorithms and cipher suites, like so:
// applyAlgorithmPolicyToDefault applies the policy to library defaults.
func applyAlgorithmPolicyToDefault() {
defaultCurvePreferences =
filterCurves(defaultCurvePreferences)
defaultSupportedSignatureAlgorithms =
filterSignatureAlgorithms(defaultSupportedSignatureAlgorithms)
defaultCipherSuites =
filterCipherSuites(defaultCipherSuites)
defaultCipherSuitesLen = len(defaultCipherSuites)
defaultCipherSuitesTLS13 =
filterCipherSuitesTLS13(defaultCipherSuitesTLS13)
defaultCipherSuitesTLS13NoAES =
filterCipherSuitesTLS13(defaultCipherSuitesTLS13NoAES)
if defaultFIPSCurvePreferences != nil {
defaultFIPSCurvePreferences =
filterCurves(defaultFIPSCurvePreferences)
}
if fipsSupportedSignatureAlgorithms != nil {
fipsSupportedSignatureAlgorithms =
filterSignatureAlgorithms(fipsSupportedSignatureAlgorithms)
}
if defaultCipherSuitesFIPS != nil {
defaultCipherSuitesFIPS =
filterCipherSuites(defaultCipherSuitesFIPS)
}
}
The configuration would also be applied to any user-created config object via a method (c *Config) applyAlgorithmPolicy()
. This would be called when initializing a new TLS server or client, and would be used to enforce the on-disk policy for custom config objects created by users.
Rationale
This approach is a non-invasive way to ensure any Go application using the standard library TLS implementation can be configured using an on-disk cryptographic configuration file. An alternative implementation would be to create an external package which could read and parse a configuration file and produce a corresponding tls.Config
object, however that approach is only applicable to code you have access to modify. For those wishing to apply a consistent configuration across all applications on a given system, this alternative solution would still fall short.
Compatibility
This change would maintain backward compatibility by skipping any newly introduced code path in the absence of an on-disk configuration.
Implementation
There is a WIP implementation from a coworker that may be helpful to look at while considering this proposal [3].
The representation of the configuration would be:
type onDiskConfig struct {
SupportedVersions []string `json:"supportedVersions"`
SupportedSignatureAlgorithms []string `json:"supportedSignatureAlgorithms"`
SupportedCurves []string `json:"supportedCurves"`
SupportedKeyAgreementAlgorithms []string `json:"supportedKeyAgreementAlgorithms"`
SupportedHashes []string `json:"supportedHashes"`
SupportedCiphers []string `json:"supportedCiphers"`
SupportedMACs []string `json:"supportedMACs"`
}
Along with a parsed policy object:
// An algorithmPolicy defines a set of algorithms can be used in TLS,
// populated from the onDiskConfig struct.
type algorithmPolicy struct {
versions []uint16
curves []CurveID
signatureAlgorithms []SignatureScheme
keyAgreementAlgorithms []keyAgreementType
hashes []crypto.Hash
ciphers []*cipherWithKeyLen
macs []macType
aeads []*aeadWithKeyLen
}
Then, within the crypto/tls
package there would be an unexported variable named something along the lines of policy
which would be a pointer to the above algorithmPolicy
struct. If that variable is non-nil it will be used to provide default configuration values.
This policy would be loaded via an init function in the crypto/tls
package, populating the aforementioned unexported variables. This would apply the policy to all internal defaults such as curves, signature algorithms and cipher suites. Additionally, when a user passes a config object into tls.Server
or tls.Client
, the implementation would call config.applyAlgorithmPolicy
to ensure that the policy is enforced.
Open Issues
None.
Links
1: https://access.redhat.com/articles/3666211
2: https://manpages.ubuntu.com/manpages/focal/en/man7/crypto-policies.7.html
3: master...ueno:go:wip/tls-policy