-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
manager.go
134 lines (114 loc) · 4.3 KB
/
manager.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
122
123
124
125
126
127
128
129
130
131
132
133
134
package proxy
import (
"crypto/tls"
"fmt"
"log"
)
// CertificateProvider defines the interface for providing certificates to a Manager.
type CertificateProvider interface {
GetCertificate(preferredSupplier string, subject string, altNames []string) (*tls.Certificate, error)
GetExistingCertificate(preferredSupplier string, subject string, altNames []string) (*tls.Certificate, bool, error)
}
// Manager is responsible for maintaining a set of routes, mapping domains to those routes, and refreshing the
// certificates for those routes.
type Manager struct {
provider CertificateProvider
routes []*Route
domains map[string]*Route
}
// NewManager creates a new route provider. Routes should be set using the SetRoutes method after creation.
// If the provider is nil, then the manager will not obtain certificates and CertificateForClient will always return
// an error.
func NewManager(provider CertificateProvider) *Manager {
return &Manager{
provider: provider,
domains: make(map[string]*Route),
}
}
// SetRoutes replaces all previously registered routes with the given new routes. This func may block while new
// certificates are obtained; during this time the old routes will continue to be served to avoid too much disruption.
func (m *Manager) SetRoutes(newRoutes []*Route) error {
newDomains := make(map[string]*Route)
for i := range newRoutes {
route := newRoutes[i]
for j := range route.Domains {
if !isDomainName(route.Domains[j]) {
return fmt.Errorf("invalid domain name: %s", route.Domains[j])
}
newDomains[route.Domains[j]] = route
m.loadCertificate(route)
}
}
m.domains = newDomains
m.routes = newRoutes
m.CheckCertificates()
return nil
}
// loadCertificate attempts to load an existing certificate for use with the given route, to enable it to be served
// immediately without waiting for certificate renewals.
func (m *Manager) loadCertificate(route *Route) {
if m.provider == nil {
route.certificateStatus = CertificateNotRequired
return
}
cert, needsRenewal, err := m.provider.GetExistingCertificate(route.Provider, route.Domains[0], route.Domains[1:])
if err == nil {
route.certificate = cert
if needsRenewal {
log.Printf("Existing certificate found for %#v but it expires soon", route.Domains)
route.certificateStatus = CertificateExpiringSoon
} else {
log.Printf("Existing certificate found for %#v", route.Domains)
route.certificateStatus = CertificateGood
}
} else {
log.Printf("No existing certificate found for %#v, route will not be served until cert is obtained", route.Domains)
route.certificate = nil
route.certificateStatus = CertificateMissing
}
}
// RouteForDomain returns the previously-registered route for the given domain. If no routes match the domain,
// nil is returned.
func (m *Manager) RouteForDomain(domain string) *Route {
route := m.domains[domain]
if route == nil || route.certificateStatus <= CertificateMissing {
return nil
}
return m.domains[domain]
}
// CertificateForClient returns a certificate (if one exists) for the domain specified in the provided
// client hello. If no certificate is available, nil is returned. The error return value is unused, but
// is kept to maintain compatibility with the tls.Config.GetCertificate func signature.
func (m *Manager) CertificateForClient(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
if m.provider == nil {
return nil, fmt.Errorf("this manager does not support obtaining certificates")
}
route := m.domains[hello.ServerName]
if route == nil {
return nil, nil
}
return route.certificate, nil
}
// CheckCertificates checks and updates the certificates required for registered routes.
// It should be called periodically to renew certificates and obtain new OCSP staples.
func (m *Manager) CheckCertificates() {
for i := range m.routes {
route := m.routes[i]
if m.provider == nil {
route.certificateStatus = CertificateNotRequired
} else {
m.updateCert(route)
}
}
}
// updateCert updates the certificate for the given route.
func (m *Manager) updateCert(route *Route) {
cert, err := m.provider.GetCertificate(route.Provider, route.Domains[0], route.Domains[1:])
if err != nil {
log.Printf("Failed to update certificate for %#v: %v", route.Domains, err)
m.loadCertificate(route)
return
}
route.certificate = cert
route.certificateStatus = CertificateGood
}