Skip to content

Commit

Permalink
feat: move pkg/grpc/tls from github.com/talos-systems/talos as `.…
Browse files Browse the repository at this point in the history
…/tls`

Just moving code without any changes.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
  • Loading branch information
smira authored and talos-bot committed Aug 17, 2020
1 parent 1ff6242 commit 196679e
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2020-08-13T17:40:42Z by kres f4c4987-dirty.
# Generated on 2020-08-17T13:49:58Z by kres 3d35a96-dirty.

**
!tls
!x509
!go.mod
!go.sum
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2020-08-13T17:38:42Z by kres f4c4987-dirty.
# Generated on 2020-08-17T13:49:58Z by kres 3d35a96-dirty.

ARG TOOLCHAIN

Expand All @@ -28,6 +28,7 @@ COPY ./go.mod .
COPY ./go.sum .
RUN go mod download
RUN go mod verify
COPY ./tls ./tls
COPY ./x509 ./x509
RUN go list -mod=readonly all >/dev/null

Expand Down
186 changes: 186 additions & 0 deletions tls/provider.go
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")
}
138 changes: 138 additions & 0 deletions tls/tls.go
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
}
14 changes: 14 additions & 0 deletions tls/tls_test.go
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
}

0 comments on commit 196679e

Please sign in to comment.