2019-10-27 06:18:58 +00:00
package dynamiclistener
import (
2020-01-31 05:18:44 +00:00
"context"
2019-10-27 06:18:58 +00:00
"crypto"
"crypto/tls"
"crypto/x509"
"net"
"net/http"
2019-11-13 06:03:51 +00:00
"strings"
2019-10-27 06:18:58 +00:00
"sync"
2020-01-31 05:18:44 +00:00
"time"
2019-10-27 06:18:58 +00:00
2020-08-11 00:06:11 +00:00
"github.com/rancher/dynamiclistener/cert"
2019-10-27 06:18:58 +00:00
"github.com/rancher/dynamiclistener/factory"
"github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
)
type TLSStorage interface {
Get ( ) ( * v1 . Secret , error )
Update ( secret * v1 . Secret ) error
}
2019-12-04 18:32:00 +00:00
type TLSFactory interface {
2020-08-11 00:06:11 +00:00
Renew ( secret * v1 . Secret ) ( * v1 . Secret , error )
2019-12-04 18:32:00 +00:00
AddCN ( secret * v1 . Secret , cn ... string ) ( * v1 . Secret , bool , error )
2020-02-04 19:47:53 +00:00
Merge ( target * v1 . Secret , additional * v1 . Secret ) ( * v1 . Secret , bool , error )
2020-04-03 05:08:36 +00:00
Filter ( cn ... string ) [ ] string
2021-11-15 20:50:26 +00:00
Regenerate ( secret * v1 . Secret ) ( * v1 . Secret , error )
2019-12-04 18:32:00 +00:00
}
2019-11-13 06:03:51 +00:00
type SetFactory interface {
2019-12-04 18:32:00 +00:00
SetFactory ( tls TLSFactory )
2019-11-13 06:03:51 +00:00
}
2019-10-27 06:18:58 +00:00
func NewListener ( l net . Listener , storage TLSStorage , caCert * x509 . Certificate , caKey crypto . Signer , config Config ) ( net . Listener , http . Handler , error ) {
if config . CN == "" {
config . CN = "dynamic"
}
if len ( config . Organization ) == 0 {
config . Organization = [ ] string { "dynamic" }
}
2020-01-31 05:27:52 +00:00
if config . TLSConfig == nil {
config . TLSConfig = & tls . Config { }
}
2022-07-20 19:50:19 +00:00
if config . ExpirationDaysCheck == 0 {
config . ExpirationDaysCheck = 90
}
2019-10-27 06:18:58 +00:00
dynamicListener := & listener {
factory : & factory . TLS {
2022-07-20 19:50:19 +00:00
CACert : caCert ,
CAKey : caKey ,
CN : config . CN ,
Organization : config . Organization ,
FilterCN : allowDefaultSANs ( config . SANs , config . FilterCN ) ,
ExpirationDaysCheck : config . ExpirationDaysCheck ,
2019-10-27 06:18:58 +00:00
} ,
Listener : l ,
storage : & nonNil { storage : storage } ,
sans : config . SANs ,
2020-03-19 06:15:44 +00:00
maxSANs : config . MaxSANs ,
2019-10-27 06:18:58 +00:00
tlsConfig : config . TLSConfig ,
}
2020-01-31 05:27:52 +00:00
if dynamicListener . tlsConfig == nil {
dynamicListener . tlsConfig = & tls . Config { }
}
2019-10-27 06:18:58 +00:00
dynamicListener . tlsConfig . GetCertificate = dynamicListener . getCertificate
2020-02-12 19:00:20 +00:00
if config . CloseConnOnCertChange {
2020-03-19 06:16:11 +00:00
if len ( dynamicListener . tlsConfig . Certificates ) == 0 {
dynamicListener . tlsConfig . NextProtos = [ ] string { "http/1.1" }
}
2020-02-12 19:00:20 +00:00
dynamicListener . conns = map [ int ] * closeWrapper { }
}
2019-11-13 06:03:51 +00:00
if setter , ok := storage . ( SetFactory ) ; ok {
setter . SetFactory ( dynamicListener . factory )
}
2021-11-15 20:50:26 +00:00
if config . RegenerateCerts != nil && config . RegenerateCerts ( ) {
if err := dynamicListener . regenerateCerts ( ) ; err != nil {
return nil , nil , err
}
}
2020-01-31 05:27:52 +00:00
tlsListener := tls . NewListener ( dynamicListener . WrapExpiration ( config . ExpirationDaysCheck ) , dynamicListener . tlsConfig )
2021-11-15 20:50:26 +00:00
2020-01-31 05:18:44 +00:00
return tlsListener , dynamicListener . cacheHandler ( ) , nil
}
2020-04-18 02:29:23 +00:00
func allowDefaultSANs ( sans [ ] string , next func ( ... string ) [ ] string ) func ( ... string ) [ ] string {
if next == nil {
return nil
} else if len ( sans ) == 0 {
return next
}
sanMap := map [ string ] bool { }
for _ , san := range sans {
sanMap [ san ] = true
}
return func ( s ... string ) [ ] string {
var (
good [ ] string
unknown [ ] string
)
for _ , s := range s {
if sanMap [ s ] {
good = append ( good , s )
} else {
unknown = append ( unknown , s )
}
}
return append ( good , next ( unknown ... ) ... )
}
}
2020-01-31 05:18:44 +00:00
type cancelClose struct {
cancel func ( )
net . Listener
}
func ( c * cancelClose ) Close ( ) error {
c . cancel ( )
return c . Listener . Close ( )
}
type Config struct {
2020-02-12 19:00:20 +00:00
CN string
Organization [ ] string
TLSConfig * tls . Config
SANs [ ] string
2020-03-19 06:15:44 +00:00
MaxSANs int
2020-02-12 19:00:20 +00:00
ExpirationDaysCheck int
CloseConnOnCertChange bool
2021-11-15 20:50:26 +00:00
RegenerateCerts func ( ) bool
2020-04-03 05:08:36 +00:00
FilterCN func ( ... string ) [ ] string
2019-10-27 06:18:58 +00:00
}
type listener struct {
sync . RWMutex
net . Listener
2020-02-12 19:00:20 +00:00
conns map [ int ] * closeWrapper
connID int
connLock sync . Mutex
2019-12-04 18:32:00 +00:00
factory TLSFactory
2019-10-27 06:18:58 +00:00
storage TLSStorage
version string
2020-01-31 05:27:52 +00:00
tlsConfig * tls . Config
2019-10-27 06:18:58 +00:00
cert * tls . Certificate
sans [ ] string
2020-03-19 06:15:44 +00:00
maxSANs int
2019-11-13 06:03:51 +00:00
init sync . Once
2019-10-27 06:18:58 +00:00
}
2020-01-31 05:18:44 +00:00
func ( l * listener ) WrapExpiration ( days int ) net . Listener {
ctx , cancel := context . WithCancel ( context . Background ( ) )
go func ( ) {
2022-07-25 12:33:03 +00:00
// loop on short sleeps until certificate preload completes
2021-11-18 01:43:37 +00:00
for l . cert == nil {
2022-07-25 12:33:03 +00:00
time . Sleep ( time . Millisecond )
2021-11-18 01:43:37 +00:00
}
2020-01-31 05:18:44 +00:00
for {
wait := 6 * time . Hour
if err := l . checkExpiration ( days ) ; err != nil {
2022-05-11 18:41:00 +00:00
logrus . Errorf ( "dynamiclistener %s: failed to check and renew dynamic cert: %v" , l . Addr ( ) , err )
2020-08-11 00:06:11 +00:00
// Don't go into short retry loop if we're using a static (user-provided) cert.
// We will still check and print an error every six hours until the user updates the secret with
// a cert that is not about to expire. Hopefully this will prompt them to take action.
if err != cert . ErrStaticCert {
wait = 5 * time . Minute
}
2020-01-31 05:18:44 +00:00
}
select {
case <- ctx . Done ( ) :
return
case <- time . After ( wait ) :
}
}
} ( )
return & cancelClose {
cancel : cancel ,
Listener : l ,
}
}
2021-11-15 20:50:26 +00:00
// regenerateCerts regenerates the used certificates and
// updates the secret.
func ( l * listener ) regenerateCerts ( ) error {
l . Lock ( )
defer l . Unlock ( )
secret , err := l . storage . Get ( )
if err != nil {
return err
}
newSecret , err := l . factory . Regenerate ( secret )
if err != nil {
return err
}
if err := l . storage . Update ( newSecret ) ; err != nil {
return err
}
// clear version to force cert reload
l . version = ""
return nil
}
2020-01-31 05:18:44 +00:00
func ( l * listener ) checkExpiration ( days int ) error {
l . Lock ( )
defer l . Unlock ( )
if days == 0 {
return nil
}
if l . cert == nil {
return nil
}
secret , err := l . storage . Get ( )
if err != nil {
return err
}
2020-08-11 00:06:11 +00:00
keyPair , err := tls . X509KeyPair ( secret . Data [ v1 . TLSCertKey ] , secret . Data [ v1 . TLSPrivateKeyKey ] )
2020-01-31 05:18:44 +00:00
if err != nil {
return err
}
2020-08-11 00:06:11 +00:00
certParsed , err := x509 . ParseCertificate ( keyPair . Certificate [ 0 ] )
2020-01-31 05:18:44 +00:00
if err != nil {
return err
}
2020-08-11 00:06:11 +00:00
if cert . IsCertExpired ( certParsed , days ) {
secret , err := l . factory . Renew ( secret )
2020-01-31 05:18:44 +00:00
if err != nil {
return err
}
2020-08-11 00:06:11 +00:00
if err := l . storage . Update ( secret ) ; err != nil {
return err
}
// clear version to force cert reload
l . version = ""
2020-01-31 05:18:44 +00:00
}
return nil
}
2019-10-27 06:18:58 +00:00
func ( l * listener ) Accept ( ) ( net . Conn , error ) {
2019-11-13 06:03:51 +00:00
l . init . Do ( func ( ) {
if len ( l . sans ) > 0 {
2021-11-18 01:43:37 +00:00
if err := l . updateCert ( l . sans ... ) ; err != nil {
2022-05-11 18:41:00 +00:00
logrus . Errorf ( "dynamiclistener %s: failed to update cert with configured SANs: %v" , l . Addr ( ) , err )
2021-11-18 01:43:37 +00:00
return
}
2022-05-11 18:41:00 +00:00
}
if cert , err := l . loadCert ( nil ) ; err != nil {
logrus . Errorf ( "dynamiclistener %s: failed to preload certificate: %v" , l . Addr ( ) , err )
} else if cert == nil {
// This should only occur on the first startup when no SANs are configured in the listener config, in which
// case no certificate can be created, as dynamiclistener will not create certificates until at least one IP
// or DNS SAN is set. It will also occur when using the Kubernetes storage without a local File cache.
// For reliable serving of requests, callers should configure a local cache and/or a default set of SANs.
logrus . Warnf ( "dynamiclistener %s: no cached certificate available for preload - deferring certificate load until storage initialization or first client request" , l . Addr ( ) )
2019-11-13 06:03:51 +00:00
}
} )
2019-10-27 06:18:58 +00:00
conn , err := l . Listener . Accept ( )
if err != nil {
return conn , err
}
2019-11-09 04:18:56 +00:00
addr := conn . LocalAddr ( )
2019-10-27 06:18:58 +00:00
if addr == nil {
return conn , nil
}
host , _ , err := net . SplitHostPort ( addr . String ( ) )
if err != nil {
2022-05-11 20:07:41 +00:00
logrus . Errorf ( "dynamiclistener %s: failed to parse connection local address %s: %v" , l . Addr ( ) , addr , err )
2019-10-27 06:18:58 +00:00
return conn , nil
}
2022-05-11 20:07:41 +00:00
if err := l . updateCert ( host ) ; err != nil {
logrus . Errorf ( "dynamiclistener %s: failed to update cert with connection local address: %v" , l . Addr ( ) , err )
2019-11-09 04:18:56 +00:00
}
2020-02-12 19:00:20 +00:00
if l . conns != nil {
conn = l . wrap ( conn )
}
2019-11-09 04:18:56 +00:00
return conn , nil
2019-10-27 06:18:58 +00:00
}
2020-02-12 19:00:20 +00:00
func ( l * listener ) wrap ( conn net . Conn ) net . Conn {
l . connLock . Lock ( )
defer l . connLock . Unlock ( )
l . connID ++
wrapper := & closeWrapper {
Conn : conn ,
id : l . connID ,
l : l ,
}
l . conns [ l . connID ] = wrapper
return wrapper
}
type closeWrapper struct {
net . Conn
2021-10-22 22:54:20 +00:00
id int
l * listener
ready bool
2020-02-12 19:00:20 +00:00
}
func ( c * closeWrapper ) close ( ) error {
delete ( c . l . conns , c . id )
return c . Conn . Close ( )
}
func ( c * closeWrapper ) Close ( ) error {
2020-02-13 16:52:25 +00:00
c . l . connLock . Lock ( )
defer c . l . connLock . Unlock ( )
2020-02-12 19:00:20 +00:00
return c . close ( )
}
2019-10-27 06:18:58 +00:00
func ( l * listener ) getCertificate ( hello * tls . ClientHelloInfo ) ( * tls . Certificate , error ) {
2021-10-22 22:54:20 +00:00
newConn := hello . Conn
2019-10-27 06:18:58 +00:00
if hello . ServerName != "" {
if err := l . updateCert ( hello . ServerName ) ; err != nil {
2022-05-11 18:41:00 +00:00
logrus . Errorf ( "dynamiclistener %s: failed to update cert with TLS ServerName: %v" , l . Addr ( ) , err )
2019-10-27 06:18:58 +00:00
return nil , err
}
}
2021-10-29 18:03:02 +00:00
return l . loadCert ( newConn )
2019-10-27 06:18:58 +00:00
}
2019-11-13 06:03:51 +00:00
func ( l * listener ) updateCert ( cn ... string ) error {
2020-04-03 05:08:36 +00:00
cn = l . factory . Filter ( cn ... )
if len ( cn ) == 0 {
return nil
}
2019-10-27 06:18:58 +00:00
l . RLock ( )
defer l . RUnlock ( )
secret , err := l . storage . Get ( )
if err != nil {
return err
}
2021-11-18 01:43:37 +00:00
if factory . IsStatic ( secret ) || ! factory . NeedsUpdate ( l . maxSANs , secret , cn ... ) {
2019-10-27 06:18:58 +00:00
return nil
}
l . RUnlock ( )
l . Lock ( )
defer l . RLock ( )
defer l . Unlock ( )
2019-11-13 06:03:51 +00:00
secret , updated , err := l . factory . AddCN ( secret , append ( l . sans , cn ... ) ... )
2019-10-27 06:18:58 +00:00
if err != nil {
return err
}
if updated {
if err := l . storage . Update ( secret ) ; err != nil {
return err
}
2021-11-18 01:43:37 +00:00
// Clear version to force cert reload next time loadCert is called by TLSConfig's
// GetCertificate hook to provide a certificate for a new connection. Note that this
// means the old certificate stays in l.cert until a new connection is made.
2019-10-27 06:18:58 +00:00
l . version = ""
2020-02-12 19:00:20 +00:00
}
2019-10-27 06:18:58 +00:00
return nil
}
2021-10-29 18:03:02 +00:00
func ( l * listener ) loadCert ( currentConn net . Conn ) ( * tls . Certificate , error ) {
2019-10-27 06:18:58 +00:00
l . RLock ( )
defer l . RUnlock ( )
secret , err := l . storage . Get ( )
if err != nil {
return nil , err
}
2021-03-05 20:39:13 +00:00
if l . cert != nil && l . version == secret . ResourceVersion && secret . ResourceVersion != "" {
2019-10-27 06:18:58 +00:00
return l . cert , nil
}
defer l . RLock ( )
l . RUnlock ( )
l . Lock ( )
defer l . Unlock ( )
secret , err = l . storage . Get ( )
if err != nil {
return nil , err
}
2022-04-30 00:38:08 +00:00
if ! cert . IsValidTLSSecret ( secret ) {
return l . cert , nil
}
2021-03-05 20:39:13 +00:00
if l . cert != nil && l . version == secret . ResourceVersion && secret . ResourceVersion != "" {
2019-10-27 06:18:58 +00:00
return l . cert , nil
}
cert , err := tls . X509KeyPair ( secret . Data [ v1 . TLSCertKey ] , secret . Data [ v1 . TLSPrivateKeyKey ] )
if err != nil {
return nil , err
}
2021-06-16 07:23:14 +00:00
// cert has changed, close closeWrapper wrapped connections if this isn't the first load
2021-11-18 01:43:37 +00:00
if currentConn != nil && l . conns != nil && l . cert != nil {
2020-08-11 00:06:11 +00:00
l . connLock . Lock ( )
for _ , conn := range l . conns {
2021-10-22 22:54:20 +00:00
// Don't close a connection that's in the middle of completing a TLS handshake
if ! conn . ready {
continue
}
2020-08-11 00:06:11 +00:00
_ = conn . close ( )
}
2021-10-29 18:03:02 +00:00
l . conns [ currentConn . ( * closeWrapper ) . id ] . ready = true
2020-08-11 00:06:11 +00:00
l . connLock . Unlock ( )
}
2019-10-27 06:18:58 +00:00
l . cert = & cert
2020-04-18 02:29:52 +00:00
l . version = secret . ResourceVersion
2019-10-27 06:18:58 +00:00
return l . cert , nil
}
func ( l * listener ) cacheHandler ( ) http . Handler {
return http . HandlerFunc ( func ( resp http . ResponseWriter , req * http . Request ) {
h , _ , err := net . SplitHostPort ( req . Host )
if err != nil {
h = req . Host
}
ip := net . ParseIP ( h )
if len ( ip ) > 0 {
2020-03-14 17:16:11 +00:00
for _ , v := range req . Header [ "User-Agent" ] {
if strings . Contains ( strings . ToLower ( v ) , "mozilla" ) {
return
}
}
2021-11-18 01:43:37 +00:00
if err := l . updateCert ( h ) ; err != nil {
2022-05-11 18:41:00 +00:00
logrus . Errorf ( "dynamiclistener %s: failed to update cert with HTTP request Host header: %v" , l . Addr ( ) , err )
2021-11-18 01:43:37 +00:00
}
2019-10-27 06:18:58 +00:00
}
} )
}
type nonNil struct {
sync . Mutex
storage TLSStorage
}
func ( n * nonNil ) Get ( ) ( * v1 . Secret , error ) {
n . Lock ( )
defer n . Unlock ( )
s , err := n . storage . Get ( )
if err != nil || s == nil {
return & v1 . Secret { } , err
}
return s , nil
}
func ( n * nonNil ) Update ( secret * v1 . Secret ) error {
n . Lock ( )
defer n . Unlock ( )
return n . storage . Update ( secret )
}