/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package tls import ( "crypto/tls" "crypto/x509" "github.com/pkg/errors" "github.com/talos-systems/talos/pkg/userdata" ) // Type represents the TLS authentication type. type Type int const ( // Mutual configures the server's policy for TLS Client Authentication to // mutual TLS. Mutual Type = 1 << iota // ServerOnly configures the server's policy for TLS Client Authentication // to server only. ServerOnly ) // ConfigOptionFunc describes a configuration option function for the TLS config type ConfigOptionFunc func(*tls.Config) error // WithClientAuthType declares the server's policy regardling TLS Client Authentication func WithClientAuthType(t Type) func(*tls.Config) error { return func(cfg *tls.Config) error { switch t { case Mutual: cfg.ClientAuth = tls.RequireAndVerifyClientCert case ServerOnly: cfg.ClientAuth = tls.NoClientCert default: return errors.Errorf("unhandled client auth type %+v", t) } return nil } } // WithCertificateProvider declares a dynamic provider for the client // certificate. // // NOTE: specifying this option will CLEAR any configured Certificates, since // they would otherwise override this option // func WithCertificateProvider(p CertificateProvider) func(*tls.Config) error { return func(cfg *tls.Config) error { if p == nil { return errors.New("no provider") } cfg.Certificates = nil cfg.GetCertificate = p.GetCertificate return nil } } // WithKeypair declares a specific TLS keypair to be used. This can be called // multiple times to add additional keypairs. func WithKeypair(cert tls.Certificate) func(*tls.Config) error { return func(cfg *tls.Config) error { cfg.Certificates = append(cfg.Certificates, cert) return nil } } // WithCACertPEM declares a PEM-encoded CA Certificate to be used. func WithCACertPEM(ca []byte) func(*tls.Config) error { return func(cfg *tls.Config) error { if len(ca) == 0 { return errors.New("no CA cert provided") } if ok := cfg.ClientCAs.AppendCertsFromPEM(ca); !ok { return errors.New("failed to append CA certificate to ClientCAs pool") } if ok := cfg.RootCAs.AppendCertsFromPEM(ca); !ok { return errors.New("failed to append CA certificate to RootCAs pool") } return nil } } func defaultConfig() *tls.Config { return &tls.Config{ RootCAs: x509.NewCertPool(), ClientCAs: x509.NewCertPool(), // Use the X25519 elliptic curve for the ECDHE key exchange algorithm. CurvePreferences: []tls.CurveID{tls.X25519}, SessionTicketsDisabled: true, // TLS protocol, ECDHE key exchange algorithm, ECDSA digital signature algorithm, AES_256_GCM bulk encryption algorithm, and SHA384 hash algorithm. CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, // Force the above cipher suites. PreferServerCipherSuites: true, // TLS 1.2 MinVersion: tls.VersionTLS12, } } // NewConfigWithOpts returns a new TLS Configuration modified by any provided configuration options func NewConfigWithOpts(opts ...ConfigOptionFunc) (cfg *tls.Config, err error) { cfg = defaultConfig() for _, f := range opts { if err = f(cfg); err != nil { return } } return } // NewConfig initializes a TLS config for the specified type. func NewConfig(t Type, data *userdata.OSSecurity) (config *tls.Config, err error) { config = defaultConfig() if err = WithClientAuthType(t)(config); err != nil { return nil, errors.Wrap(err, "failed to apply ClientAuthType preference") } if err = WithCACertPEM(data.CA.Crt)(config); err != nil { return nil, errors.Wrap(err, "failed to apply CA Certificate from UserData") } if err = WithCertificateProvider(&userDataCertificateProvider{data: data})(config); err != nil { return nil, errors.Wrap(err, "failed to apply userdata-sourced CertificateProvider") } return }