1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
package tls
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"net"
"os"
"strings"
"time"
"github.com/lxc/incus/v6/shared/util"
)
// connectErrorPrefix used as prefix to error returned from RFC3493Dialer.
const connectErrorPrefix = "Unable to connect to"
// RFC3493Dialer connects to the specified server and returns the connection.
// If the connection cannot be established then an error with the connectErrorPrefix is returned.
func RFC3493Dialer(context context.Context, network string, address string) (net.Conn, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
addrs, err := net.LookupHost(host)
if err != nil {
return nil, err
}
var errs []error
for _, a := range addrs {
c, err := net.DialTimeout(network, net.JoinHostPort(a, port), 10*time.Second)
if err != nil {
errs = append(errs, err)
continue
}
tc, ok := c.(*net.TCPConn)
if ok {
_ = tc.SetKeepAlive(true)
_ = tc.SetKeepAlivePeriod(3 * time.Second)
}
return c, nil
}
return nil, fmt.Errorf("%s: %s (%v)", connectErrorPrefix, address, errs)
}
// IsConnectionError returns true if the given error is due to the dialer not being able to connect to the target.
func IsConnectionError(err error) bool {
// FIXME: Unfortunately the client currently does not provide a way to differentiate between errors.
return strings.Contains(err.Error(), connectErrorPrefix)
}
// InitTLSConfig returns a tls.Config populated with default encryption
// parameters. This is used as baseline config for both client and server
// certificates.
func InitTLSConfig() *tls.Config {
config := &tls.Config{}
// Restrict to TLS 1.3 unless INCUS_INSECURE_TLS is set.
if util.IsFalseOrEmpty(os.Getenv("INCUS_INSECURE_TLS")) {
config.MinVersion = tls.VersionTLS13
} else {
config.MinVersion = tls.VersionTLS12
}
return config
}
func finalizeTLSConfig(tlsConfig *tls.Config, tlsRemoteCert *x509.Certificate) {
// Setup RootCA
if tlsConfig.RootCAs == nil {
tlsConfig.RootCAs, _ = systemCertPool()
}
// Trusted certificates
if tlsRemoteCert != nil {
if tlsConfig.RootCAs == nil {
tlsConfig.RootCAs = x509.NewCertPool()
}
// Make it a valid RootCA
tlsRemoteCert.IsCA = true
tlsRemoteCert.KeyUsage = x509.KeyUsageCertSign
// Setup the pool
tlsConfig.RootCAs.AddCert(tlsRemoteCert)
// Set the ServerName
if tlsRemoteCert.DNSNames != nil {
tlsConfig.ServerName = tlsRemoteCert.DNSNames[0]
}
}
}
func GetTLSConfig(tlsRemoteCert *x509.Certificate) (*tls.Config, error) {
tlsConfig := InitTLSConfig()
finalizeTLSConfig(tlsConfig, tlsRemoteCert)
return tlsConfig, nil
}
func GetTLSConfigMem(tlsClientCert string, tlsClientKey string, tlsClientCA string, tlsRemoteCertPEM string, insecureSkipVerify bool) (*tls.Config, error) {
tlsConfig := InitTLSConfig()
// Client authentication
if tlsClientCert != "" && tlsClientKey != "" {
cert, err := tls.X509KeyPair([]byte(tlsClientCert), []byte(tlsClientKey))
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
var tlsRemoteCert *x509.Certificate
if tlsRemoteCertPEM != "" {
// Ignore any content outside of the PEM bytes we care about
certBlock, _ := pem.Decode([]byte(tlsRemoteCertPEM))
if certBlock == nil {
return nil, fmt.Errorf("Invalid remote certificate")
}
var err error
tlsRemoteCert, err = x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, err
}
}
if tlsClientCA != "" {
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM([]byte(tlsClientCA))
tlsConfig.RootCAs = caPool
}
finalizeTLSConfig(tlsConfig, tlsRemoteCert)
// Only skip TLS verification if no remote certificate is available.
if tlsRemoteCert == nil {
tlsConfig.InsecureSkipVerify = insecureSkipVerify
}
return tlsConfig, nil
}
|