forked from knative/serving
-
Notifications
You must be signed in to change notification settings - Fork 0
/
resolver.go
121 lines (100 loc) · 3.9 KB
/
resolver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package certificate
import (
"context"
"crypto/tls"
"sync"
"go.uber.org/zap"
v1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/tools/cache"
"knative.dev/control-protocol/pkg/certificates"
"knative.dev/networking/pkg/apis/networking"
"knative.dev/networking/pkg/apis/networking/v1alpha1"
kcertinformer "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/certificate"
secretfilteredinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/secret/filtered"
filteredFactory "knative.dev/pkg/client/injection/kube/informers/factory/filtered"
"knative.dev/pkg/logging"
pkgreconciler "knative.dev/pkg/reconciler"
)
const (
defaultCertificateDomain = "knative"
)
type CertResolver struct {
secretInformer v1.SecretInformer
logger *zap.SugaredLogger
certificates map[string]*tls.Certificate
certificatesMux sync.RWMutex
}
func NewCertResolver(ctx context.Context) *CertResolver {
knCertificateInformer := kcertinformer.Get(ctx)
secretInformer := getSecretInformer(ctx)
cr := &CertResolver{
secretInformer: secretInformer,
certificates: make(map[string]*tls.Certificate),
logger: logging.FromContext(ctx),
}
knCertificateInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
// TODO: update the filter if we have a custom label for internal certs
FilterFunc: pkgreconciler.LabelExistsFilterFunc(networking.VisibilityLabelKey),
Handler: cache.ResourceEventHandlerFuncs{
AddFunc: cr.handleCertificateAdd,
UpdateFunc: cr.handleCertificateUpdate,
DeleteFunc: cr.handleCertificateDelete,
},
})
return cr
}
// GetCertificate returns a certificate based on SNIs ClientHello.
// If we return (nil, error) the client sees - 'tls: internal error'
// If we return (nil, nil) the client sees - 'tls: no certificates configured'
// We'll return (nil, nil) when we don't find a certificate
func (cr *CertResolver) GetCertificate(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
cr.certificatesMux.RLock()
defer cr.certificatesMux.RUnlock()
if crt, ok := cr.certificates[info.ServerName]; ok {
cr.logger.Warnw("SNI request, returning domain cert", zap.String("server-name", info.ServerName))
return crt, nil
}
// Fallback to default certificate with SAN = defaultCertificateDomain
if crt, ok := cr.certificates[defaultCertificateDomain]; ok {
cr.logger.Warnw("SNI request, returning DEFAULT", zap.String("server-name", info.ServerName))
return crt, nil
}
cr.logger.Warnw("SNI request, returning NO CERT", zap.String("server-name", info.ServerName))
return nil, nil
}
func (cr *CertResolver) handleCertificateAdd(added interface{}) {
if kCert, ok := added.(*v1alpha1.Certificate); ok {
secret, err := cr.secretInformer.Lister().Secrets(kCert.Namespace).Get(kCert.Spec.SecretName)
if err != nil {
cr.logger.Warnw("failed to get secret", zap.Namespace(kCert.Namespace), zap.Error(err))
return
}
cr.certificatesMux.Lock()
defer cr.certificatesMux.Unlock()
cert, err := tls.X509KeyPair(secret.Data[certificates.CertName], secret.Data[certificates.PrivateKeyName])
if err != nil {
cr.logger.Warnw("failed to parse secret", zap.Namespace(kCert.Namespace), zap.Error(err))
return
}
for _, domain := range kCert.Spec.DNSNames {
cr.certificates[domain] = &cert
}
}
}
func (cr *CertResolver) handleCertificateUpdate(old, new interface{}) {
cr.handleCertificateDelete(old)
cr.handleCertificateAdd(new)
}
func (cr *CertResolver) handleCertificateDelete(del interface{}) {
if kCert, ok := del.(*v1alpha1.Certificate); ok {
cr.certificatesMux.Lock()
defer cr.certificatesMux.Unlock()
for _, domain := range kCert.Spec.DNSNames {
delete(cr.certificates, domain)
}
}
}
func getSecretInformer(ctx context.Context) v1.SecretInformer {
untyped := ctx.Value(filteredFactory.LabelKey{}) // This should always be not nil and have exactly one selector
return secretfilteredinformer.Get(ctx, untyped.([]string)[0])
}