-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: move
pkg/grpc/tls
from github.com/talos-systems/talos
as `.…
…/tls` Just moving code without any changes. Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
- Loading branch information
Showing
5 changed files
with
342 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// 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 ( | ||
"context" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"errors" | ||
"fmt" | ||
"log" | ||
"net" | ||
"sync" | ||
"time" | ||
|
||
talosx509 "github.com/talos-systems/crypto/x509" | ||
) | ||
|
||
// CertificateProvider describes an interface by which TLS certificates may be managed. | ||
type CertificateProvider interface { | ||
// GetCA returns the active root CA. | ||
GetCA() ([]byte, error) | ||
|
||
// GetCertificate returns the current certificate matching the given client request. | ||
GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) | ||
|
||
// GetClientCertificate returns the current certificate to present to the server. | ||
GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) | ||
} | ||
|
||
// Generator describes an interface to sign the CSR. | ||
type Generator interface { | ||
Identity(csr *talosx509.CertificateSigningRequest) (ca, crt []byte, err error) | ||
} | ||
|
||
type certificateProvider struct { | ||
sync.RWMutex | ||
|
||
generator Generator | ||
|
||
ca []byte | ||
crt *tls.Certificate | ||
|
||
dnsNames []string | ||
ips []net.IP | ||
} | ||
|
||
// NewRenewingCertificateProvider returns a new CertificateProvider | ||
// which manages and updates its certificates using Generator. | ||
func NewRenewingCertificateProvider(generator Generator, dnsNames []string, ips []net.IP) (CertificateProvider, error) { | ||
provider := &certificateProvider{ | ||
generator: generator, | ||
dnsNames: dnsNames, | ||
ips: ips, | ||
} | ||
|
||
var ( | ||
ca []byte | ||
cert tls.Certificate | ||
err error | ||
) | ||
|
||
if ca, cert, err = provider.update(); err != nil { | ||
return nil, fmt.Errorf("failed to create initial certificate: %w", err) | ||
} | ||
|
||
provider.updateCertificates(ca, &cert) | ||
|
||
//nolint: errcheck | ||
go provider.manageUpdates(context.TODO()) | ||
|
||
return provider, nil | ||
} | ||
|
||
func (p *certificateProvider) update() (ca []byte, cert tls.Certificate, err error) { | ||
var ( | ||
crt []byte | ||
csr *talosx509.CertificateSigningRequest | ||
identity *talosx509.PEMEncodedCertificateAndKey | ||
) | ||
|
||
csr, identity, err = talosx509.NewCSRAndIdentity(p.dnsNames, p.ips) | ||
if err != nil { | ||
return nil, cert, err | ||
} | ||
|
||
if ca, crt, err = p.generator.Identity(csr); err != nil { | ||
return nil, cert, fmt.Errorf("failed to generate identity: %w", err) | ||
} | ||
|
||
identity.Crt = crt | ||
|
||
cert, err = tls.X509KeyPair(identity.Crt, identity.Key) | ||
if err != nil { | ||
return nil, cert, fmt.Errorf("failed to parse cert and key into a TLS Certificate: %w", err) | ||
} | ||
|
||
return ca, cert, nil | ||
} | ||
|
||
func (p *certificateProvider) GetCA() ([]byte, error) { | ||
if p == nil { | ||
return nil, errors.New("no provider") | ||
} | ||
|
||
p.RLock() | ||
defer p.RUnlock() | ||
|
||
return p.ca, nil | ||
} | ||
|
||
func (p *certificateProvider) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { | ||
if p == nil { | ||
return nil, errors.New("no provider") | ||
} | ||
|
||
p.RLock() | ||
defer p.RUnlock() | ||
|
||
return p.crt, nil | ||
} | ||
|
||
func (p *certificateProvider) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) { | ||
return p.GetCertificate(nil) | ||
} | ||
|
||
func (p *certificateProvider) updateCertificates(ca []byte, cert *tls.Certificate) { | ||
p.Lock() | ||
defer p.Unlock() | ||
|
||
p.ca = ca | ||
p.crt = cert | ||
} | ||
|
||
func (p *certificateProvider) manageUpdates(ctx context.Context) (err error) { | ||
nextRenewal := talosx509.DefaultCertificateValidityDuration | ||
|
||
for ctx.Err() == nil { | ||
//nolint: errcheck | ||
if c, _ := p.GetCertificate(nil); c != nil { | ||
if len(c.Certificate) > 0 { | ||
var crt *x509.Certificate | ||
crt, err = x509.ParseCertificate(c.Certificate[0]) | ||
|
||
if err == nil { | ||
nextRenewal = time.Until(crt.NotAfter) / 2 | ||
} else { | ||
log.Println("failed to parse current leaf certificate") | ||
} | ||
} else { | ||
log.Println("current leaf certificate not found") | ||
} | ||
} else { | ||
log.Println("certificate not found") | ||
} | ||
|
||
log.Println("next renewal in", nextRenewal) | ||
|
||
if nextRenewal > talosx509.DefaultCertificateValidityDuration { | ||
nextRenewal = talosx509.DefaultCertificateValidityDuration | ||
} | ||
|
||
select { | ||
case <-time.After(nextRenewal): | ||
case <-ctx.Done(): | ||
return nil | ||
} | ||
|
||
var ( | ||
ca []byte | ||
cert tls.Certificate | ||
) | ||
|
||
if ca, cert, err = p.update(); err != nil { | ||
log.Println("failed to renew certificate:", err) | ||
|
||
continue | ||
} | ||
|
||
p.updateCertificates(ca, &cert) | ||
} | ||
|
||
return errors.New("certificate update manager exited unexpectedly") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
// 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 provides tls.Config generation and certificate management. | ||
package tls | ||
|
||
import ( | ||
"crypto/tls" | ||
"crypto/x509" | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
// 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 fmt.Errorf("unhandled client auth type %+v", t) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
// WithServerCertificateProvider declares a dynamic provider for the server | ||
// certificate. | ||
// | ||
// NOTE: specifying this option will CLEAR any configured Certificates, since | ||
// they would otherwise override this option. | ||
func WithServerCertificateProvider(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 | ||
} | ||
} | ||
|
||
// WithClientCertificateProvider 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 WithClientCertificateProvider(p CertificateProvider) func(*tls.Config) error { | ||
return func(cfg *tls.Config) error { | ||
if p == nil { | ||
return errors.New("no provider") | ||
} | ||
|
||
cfg.Certificates = nil | ||
cfg.GetClientCertificate = p.GetClientCertificate | ||
|
||
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, | ||
} | ||
} | ||
|
||
// New returns a new TLS Configuration modified by any provided configuration options. | ||
func New(opts ...ConfigOptionFunc) (cfg *tls.Config, err error) { | ||
cfg = defaultConfig() | ||
|
||
for _, f := range opts { | ||
if err = f(cfg); err != nil { | ||
return | ||
} | ||
} | ||
|
||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// 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_test | ||
|
||
import "testing" | ||
|
||
func TestEmpty(t *testing.T) { | ||
// added for accurate coverage estimation | ||
// | ||
// please remove it once any unit-test is added | ||
// for this package | ||
} |