PR 2938 hardens tls though there are other places that uses TLS as well and setTLSDefaults are not invoked in other paths. This PR hardens tls on all places. Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
146 lines
4.5 KiB
Go
146 lines
4.5 KiB
Go
package tls
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
func setTLSDefaults(ctls *tls.Config) {
|
|
ctls.MinVersion = tls.VersionTLS12
|
|
ctls.MaxVersion = tls.VersionTLS13
|
|
ctls.CipherSuites = []uint16{
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
}
|
|
}
|
|
|
|
// NewTLSConfigFromArgs returns a TLS config based upon the passed
|
|
// in list of arguments. Typically these come straight from the
|
|
// Corefile.
|
|
// no args
|
|
// - creates a Config with no cert and using system CAs
|
|
// - use for a client that talks to a server with a public signed cert (CA installed in system)
|
|
// - the client will not be authenticated by the server since there is no cert
|
|
// one arg: the path to CA PEM file
|
|
// - creates a Config with no cert using a specific CA
|
|
// - use for a client that talks to a server with a private signed cert (CA not installed in system)
|
|
// - the client will not be authenticated by the server since there is no cert
|
|
// two args: path to cert PEM file, the path to private key PEM file
|
|
// - creates a Config with a cert, using system CAs to validate the other end
|
|
// - use for:
|
|
// - a server; or,
|
|
// - a client that talks to a server with a public cert and needs certificate-based authentication
|
|
// - the other end will authenticate this end via the provided cert
|
|
// - the cert of the other end will be verified via system CAs
|
|
// three args: path to cert PEM file, path to client private key PEM file, path to CA PEM file
|
|
// - creates a Config with the cert, using specified CA to validate the other end
|
|
// - use for:
|
|
// - a server; or,
|
|
// - a client that talks to a server with a privately signed cert and needs certificate-based
|
|
// authentication
|
|
// - the other end will authenticate this end via the provided cert
|
|
// - this end will verify the other end's cert using the specified CA
|
|
func NewTLSConfigFromArgs(args ...string) (*tls.Config, error) {
|
|
var err error
|
|
var c *tls.Config
|
|
switch len(args) {
|
|
case 0:
|
|
// No client cert, use system CA
|
|
c, err = NewTLSClientConfig("")
|
|
case 1:
|
|
// No client cert, use specified CA
|
|
c, err = NewTLSClientConfig(args[0])
|
|
case 2:
|
|
// Client cert, use system CA
|
|
c, err = NewTLSConfig(args[0], args[1], "")
|
|
case 3:
|
|
// Client cert, use specified CA
|
|
c, err = NewTLSConfig(args[0], args[1], args[2])
|
|
default:
|
|
err = fmt.Errorf("maximum of three arguments allowed for TLS config, found %d", len(args))
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// NewTLSConfig returns a TLS config that includes a certificate
|
|
// Use for server TLS config or when using a client certificate
|
|
// If caPath is empty, system CAs will be used
|
|
func NewTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) {
|
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not load TLS cert: %s", err)
|
|
}
|
|
|
|
roots, err := loadRoots(caPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: roots}
|
|
setTLSDefaults(tlsConfig)
|
|
|
|
return tlsConfig, nil
|
|
}
|
|
|
|
// NewTLSClientConfig returns a TLS config for a client connection
|
|
// If caPath is empty, system CAs will be used
|
|
func NewTLSClientConfig(caPath string) (*tls.Config, error) {
|
|
roots, err := loadRoots(caPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tlsConfig := &tls.Config{RootCAs: roots}
|
|
setTLSDefaults(tlsConfig)
|
|
|
|
return tlsConfig, nil
|
|
}
|
|
|
|
func loadRoots(caPath string) (*x509.CertPool, error) {
|
|
if caPath == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
roots := x509.NewCertPool()
|
|
pem, err := os.ReadFile(filepath.Clean(caPath))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error reading %s: %s", caPath, err)
|
|
}
|
|
ok := roots.AppendCertsFromPEM(pem)
|
|
if !ok {
|
|
return nil, fmt.Errorf("could not read root certs: %s", err)
|
|
}
|
|
return roots, nil
|
|
}
|
|
|
|
// NewHTTPSTransport returns an HTTP transport configured using tls.Config
|
|
func NewHTTPSTransport(cc *tls.Config) *http.Transport {
|
|
tr := &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
Dial: (&net.Dialer{
|
|
Timeout: 30 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
}).Dial,
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
TLSClientConfig: cc,
|
|
MaxIdleConnsPerHost: 25,
|
|
}
|
|
|
|
return tr
|
|
}
|