-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
manager.go
132 lines (113 loc) · 4.53 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
package certificate
import (
"crypto/tls"
"fmt"
"log"
"time"
)
// Store provides functions to get and store certificates.
type Store interface {
GetCertificate(subject string, altNames []string) *Details
SaveCertificate(cert *Details) error
LockCertificate(subjectName string, altNames []string)
UnlockCertificate(subjectName string, altNames []string)
}
// Supplier provides new certificates and OCSP staples.
type Supplier interface {
GetCertificate(subject string, altNames []string) (*Details, error)
UpdateStaple(cert *Details) error
MinCertificateValidity() time.Duration
MinStapleValidity() time.Duration
}
// Manager is responsible for co-ordinating a certificate store and supplier, providing a means to obtain a valid
// certificate with an OCSP staple.
type Manager struct {
store Store
suppliers map[string]Supplier
supplierPreference []string
}
// NewManager returns a new certificate manager backed by the given store and supplier.
func NewManager(store Store, suppliers map[string]Supplier, supplierPreference []string) *Manager {
return &Manager{
store: store,
suppliers: suppliers,
supplierPreference: supplierPreference,
}
}
// GetCertificate returns a certificate for the given subject and alternate names. This may take some time if a new
// certificate needs to be obtained, or the OCSP staple needs to be updated.
func (m *Manager) GetCertificate(preferredSupplier string, subject string, altNames []string) (*tls.Certificate, error) {
supplier, err := m.supplier(preferredSupplier)
if err != nil {
return nil, err
}
m.store.LockCertificate(subject, altNames)
defer m.store.UnlockCertificate(subject, altNames)
if cert := m.store.GetCertificate(subject, altNames); cert == nil {
log.Printf("Obtaining new certificate for '%s'", subject)
return m.obtain(supplier, subject, altNames)
} else if !cert.ValidFor(supplier.MinCertificateValidity()) {
log.Printf("Renewing certificate for '%s'", subject)
return m.obtain(supplier, subject, altNames)
} else if !cert.HasStapleFor(supplier.MinStapleValidity()) {
log.Printf("Obtaining new OCSP staple for '%s'", subject)
return m.staple(supplier, cert)
} else {
return cert.keyPair()
}
}
// GetExistingCertificate returns a previously saved certificate with the given subject and alternate names if it is
// still valid. It also indicates whether the certificate is in need of renewal or not. Certificates should be renewed
// by calling GetCertificate which will block and return the new certificate.
func (m *Manager) GetExistingCertificate(preferredSupplier string, subject string, altNames []string) (*tls.Certificate, bool, error) {
supplier, err := m.supplier(preferredSupplier)
if err != nil {
return nil, false, err
}
if cert := m.store.GetCertificate(subject, altNames); cert == nil {
return nil, true, fmt.Errorf("no stored certificate found")
} else if !cert.ValidFor(0) || !cert.HasStapleFor(0) {
return nil, true, fmt.Errorf("certificate has expired")
} else {
key, err := cert.keyPair()
needRenewal := !cert.ValidFor(supplier.MinCertificateValidity()) || !cert.HasStapleFor(supplier.MinStapleValidity())
return key, needRenewal, err
}
}
func (m *Manager) supplier(preferred string) (Supplier, error) {
if preferred != "" {
s, ok := m.suppliers[preferred]
if !ok {
return nil, fmt.Errorf("requested supplier not found: %v", preferred)
}
return s, nil
}
for i := range m.supplierPreference {
s, ok := m.suppliers[m.supplierPreference[i]]
if ok {
return s, nil
}
}
return nil, fmt.Errorf("no suppliers found for preference: %v", m.supplierPreference)
}
// obtain gets a new certificate and saves it to the store.
func (m *Manager) obtain(supplier Supplier, subject string, altNames []string) (*tls.Certificate, error) {
cert, err := supplier.GetCertificate(subject, altNames)
if err != nil {
return nil, fmt.Errorf("failed to obtain certificate for %s: %w", subject, err)
}
if err := m.store.SaveCertificate(cert); err != nil {
return nil, fmt.Errorf("failed to save certificate for %s: %s", subject, err)
}
return cert.keyPair()
}
// staple updates the OCSP staple for the cert and saves it in the store.
func (m *Manager) staple(supplier Supplier, cert *Details) (*tls.Certificate, error) {
if err := supplier.UpdateStaple(cert); err != nil {
return nil, fmt.Errorf("failed to obtain OCSP staple for %s: %w", cert.Subject, err)
}
if err := m.store.SaveCertificate(cert); err != nil {
return nil, fmt.Errorf("failed to save certificate for %s: %s", cert.Subject, err)
}
return cert.keyPair()
}