Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for tls certificate update #3272

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ type CommandLineFlags struct {
// --insecure-no-tls flag
DisableTLS bool

// --proxy-tls-reload-interval-sec flag
ProxyTLSReloadIntervalSeconds int

// --labels flag
Labels string
// --pid-file flag
Expand Down Expand Up @@ -916,6 +919,11 @@ func Configure(clf *CommandLineFlags, cfg *service.Config) error {
cfg.Proxy.DisableTLS = clf.DisableTLS
}

// apply --tls-reload-interval-sec flag:
if clf.ProxyTLSReloadIntervalSeconds != 0 {
cfg.Proxy.TLSReloadIntervalSeconds = time.Duration(clf.ProxyTLSReloadIntervalSeconds) * time.Second
}

// apply --debug flag to config:
if clf.Debug {
cfg.Console = ioutil.Discard
Expand Down
4 changes: 4 additions & 0 deletions lib/defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ var (
// ProxyQueueSize is proxy service queue size
ProxyQueueSize = 8192

// ProxyTLSReloadIntervalSeconds is interval seconds to reload TLS
// certificate for proxy
ProxyTLSReloadIntervalSeconds = 300

// NodeQueueSize is node service queue size
NodeQueueSize = 128
)
Expand Down
5 changes: 5 additions & 0 deletions lib/service/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,10 @@ type ProxyConfig struct {
// list of host principals on the TLS and SSH certificate.
SSHPublicAddrs []utils.NetAddr

// TLSReloadIntervalSeconds is interval seconds to reload TLS
// certificate for proxy
TLSReloadIntervalSeconds time.Duration

// TunnelPublicAddrs is a list of the public addresses the proxy advertises
// for the tunnel endpoint. The hosts in in PublicAddr are included in the
// list of host principals on the TLS and SSH certificate.
Expand Down Expand Up @@ -482,6 +486,7 @@ func ApplyDefaults(cfg *Config) {
cfg.Proxy.SSHAddr = *defaults.ProxyListenAddr()
cfg.Proxy.WebAddr = *defaults.ProxyWebListenAddr()
cfg.Proxy.ReverseTunnelListenAddr = *defaults.ReverseTunnelListenAddr()
cfg.Proxy.TLSReloadIntervalSeconds = time.Duration(defaults.ProxyTLSReloadIntervalSeconds) * time.Second
defaults.ConfigureLimiter(&cfg.Proxy.Limiter)

// Kubernetes proxy service defaults.
Expand Down
61 changes: 58 additions & 3 deletions lib/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package service
import (
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -81,6 +82,8 @@ var log = logrus.WithFields(logrus.Fields{
trace.Component: teleport.ComponentProcess,
})

var certificateCache atomic.Value

const (
// AuthIdentityEvent is generated when the Auth Servers identity has been
// initialized in the backend.
Expand Down Expand Up @@ -408,6 +411,30 @@ func Run(ctx context.Context, cfg Config, newTeleport NewProcess) error {
if err := srv.Start(); err != nil {
return trace.Wrap(err, "startup failed")
}

if !cfg.Proxy.DisableTLS {
go func() error {
ticker := time.NewTicker(cfg.Proxy.TLSReloadIntervalSeconds)
defer ticker.Stop()

for {
err := updateCertificate(cfg.Proxy.TLSCert, cfg.Proxy.TLSKey)
if err != nil {
if !trace.IsNotFound(err) {
ctx.Done()
}
log.Info("cannot find x509 key file or tls certificate file")
}

select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
}
}
}()
}

// Wait and reload until called exit.
for {
srv, err = waitAndReload(ctx, cfg, srv, newTeleport)
Expand Down Expand Up @@ -2102,10 +2129,14 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
proxyLimiter.WrapHandle(webHandler)
if !process.Config.Proxy.DisableTLS {
log.Infof("Using TLS cert %v, key %v", cfg.Proxy.TLSCert, cfg.Proxy.TLSKey)
tlsConfig, err := utils.CreateTLSConfiguration(cfg.Proxy.TLSCert, cfg.Proxy.TLSKey, cfg.CipherSuites)
if err != nil {
return trace.Wrap(err)
if _, err := os.Stat(cfg.Proxy.TLSCert); err != nil {
return trace.BadParameter("certificate is not accessible by '%v'", cfg.Proxy.TLSCert)
}
if _, err := os.Stat(cfg.Proxy.TLSKey); err != nil {
return trace.BadParameter("key is not accessible by '%v'", cfg.Proxy.TLSKey)
}
tlsConfig := utils.TLSConfig(cfg.CipherSuites)
tlsConfig.GetCertificate = getCertificate
listeners.web = tls.NewListener(listeners.web, tlsConfig)
}
webServer = &http.Server{
Expand Down Expand Up @@ -2291,6 +2322,30 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
return nil
}

func getCertificate(helloInfo *tls.ClientHelloInfo) (*tls.Certificate, error) {
c := certificateCache.Load()
if c == nil {
return nil, errors.New("certificate is not loaded")
}
return c.(*tls.Certificate), nil
}

func updateCertificate(certFile, keyFile string) error {
if _, err := os.Stat(certFile); err != nil {
return trace.Wrap(err)
}
if _, err := os.Stat(keyFile); err != nil {
return trace.Wrap(err)
}

c, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return fmt.Errorf("failed to load x509 key pair from (%s, %s): %v", certFile, keyFile, err)
}
certificateCache.Store(&c)
return nil
}

func warnOnErr(err error) {
if err != nil {
// don't warn on double close, happens sometimes when closing
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func CreateTLSConfiguration(certFile, keyFile string, cipherSuites []uint16) (*t
return nil, trace.BadParameter("certificate is not accessible by '%v'", certFile)
}
if _, err := os.Stat(keyFile); err != nil {
return nil, trace.BadParameter("certificate is not accessible by '%v'", certFile)
return nil, trace.BadParameter("key is not accessible by '%v'", keyFile)
}

cert, err := tls.LoadX509KeyPair(certFile, keyFile)
Expand Down
2 changes: 2 additions & 0 deletions tool/teleport/common/teleport.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func Run(options Options) (executedCommand string, conf *service.Config) {
BoolVar(&ccf.Debug)
start.Flag("insecure-no-tls", "Disable TLS for the web socket").
BoolVar(&ccf.DisableTLS)
start.Flag("proxy-tls-reload-interval-sec", "Reload interval seconds for proxy server TLS certificate").
IntVar(&ccf.ProxyTLSReloadIntervalSeconds)
start.Flag("roles",
fmt.Sprintf("Comma-separated list of roles to start with [%s]", strings.Join(defaults.StartRoles, ","))).
Short('r').
Expand Down