aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2021-11-10 22:16:10 -0800
committerMike Crute <mike@crute.us>2021-11-10 22:16:10 -0800
commit8c5b1d33cc3d4af0b7c0e0fc37a90ef80ba25fe5 (patch)
treed49f37a47aa3b853052a1f7a5a0e13f3e4a5d9e7
parentef4444e6217592e2666b8a46e8c762eaf93fa8a6 (diff)
downloadgolib-8c5b1d33cc3d4af0b7c0e0fc37a90ef80ba25fe5.tar.bz2
golib-8c5b1d33cc3d4af0b7c0e0fc37a90ef80ba25fe5.tar.xz
golib-8c5b1d33cc3d4af0b7c0e0fc37a90ef80ba25fe5.zip
Nest tls package in crypto, split ocsp
-rw-r--r--crypto/tls/ocsp.go (renamed from tls/ocsp.go)126
-rw-r--r--crypto/tls/ocsp_manager.go134
2 files changed, 134 insertions, 126 deletions
diff --git a/tls/ocsp.go b/crypto/tls/ocsp.go
index b80764f..9ae9828 100644
--- a/tls/ocsp.go
+++ b/crypto/tls/ocsp.go
@@ -2,141 +2,15 @@ package tls
2 2
3import ( 3import (
4 "bytes" 4 "bytes"
5 "context"
6 "crypto/tls" 5 "crypto/tls"
7 "crypto/x509" 6 "crypto/x509"
8 "errors"
9 "fmt" 7 "fmt"
10 "io" 8 "io"
11 "net/http" 9 "net/http"
12 "sync"
13 "time"
14 10
15 "golang.org/x/crypto/ocsp" 11 "golang.org/x/crypto/ocsp"
16) 12)
17 13
18type OcspError struct {
19 Err error
20 AtBoot bool
21}
22
23func (e OcspError) Error() string {
24 return e.Err.Error()
25}
26
27func (e OcspError) Unwrap() error {
28 return e.Err
29}
30
31type OcspLogger interface {
32 Info(...interface{})
33 Errorf(string, ...interface{})
34}
35
36func OcspErrorLogger(l OcspLogger, c <-chan OcspError) func(context.Context, *sync.WaitGroup) error {
37 return func(ctx context.Context, wg *sync.WaitGroup) error {
38 wg.Add(1)
39 defer wg.Done()
40
41 for {
42 select {
43 case err := <-c:
44 l.Errorf("Error in OCSP stapling: %w", errors.Unwrap(err))
45 case <-ctx.Done():
46 l.Info("Shutting down OCSP logger")
47 return nil
48 }
49 }
50 }
51}
52
53type OcspManager struct {
54 CertPath, KeyPath string
55 Errors chan<- OcspError
56 cert *tls.Certificate
57 ocspRes *ocsp.Response
58 sync.RWMutex
59}
60
61func (m *OcspManager) loadCert() error {
62 cert, err := tls.LoadX509KeyPair(m.CertPath, m.KeyPath)
63 if err != nil {
64 return err
65 }
66 m.Lock()
67 m.cert = &cert
68 m.Unlock()
69
70 return nil
71}
72
73func (m *OcspManager) stapleCert() error {
74 // This makes a network request to an unknown server so don't hold the full
75 // lock while this is happening
76 m.RLock()
77 raw, ocspRes, err := GetOcspResponse(m.cert)
78 if err != nil {
79 return err
80 }
81 m.RUnlock()
82
83 m.Lock()
84 m.cert.OCSPStaple = raw
85 m.ocspRes = ocspRes
86 m.Unlock()
87
88 return nil
89}
90
91func (m *OcspManager) Init() error {
92 // All functions called here will handle locking themselves
93 if err := m.loadCert(); err != nil {
94 return err
95 }
96
97 if err := m.stapleCert(); err != nil {
98 return err
99 }
100
101 return nil
102}
103
104func (m *OcspManager) Run(ctx context.Context, wg *sync.WaitGroup) error {
105 wg.Add(1)
106 defer wg.Done()
107
108 t := time.NewTimer(m.ocspRes.NextUpdate.Sub(time.Now()) - time.Hour)
109
110 for {
111 select {
112 case <-t.C:
113 if err := m.stapleCert(); err != nil {
114 if m.Errors != nil {
115 m.Errors <- OcspError{err, false}
116 }
117 t.Reset(time.Hour)
118 continue
119 }
120 // We own this object and only we write it, no need to lock
121 t.Reset(m.ocspRes.NextUpdate.Sub(time.Now()) - time.Hour)
122 case <-ctx.Done():
123 return nil
124 }
125 }
126}
127
128// TODO: TLS.GetCertificate for dyanmic certs for LE (cache these)
129func (m *OcspManager) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) {
130 m.RLock()
131 defer m.RUnlock()
132
133 if m.cert != nil {
134 return m.cert, nil
135 }
136
137 return nil, fmt.Errorf("OCSP manager has no certificate stored")
138}
139
140func GetOcspResponse(chain *tls.Certificate) ([]byte, *ocsp.Response, error) { 14func GetOcspResponse(chain *tls.Certificate) ([]byte, *ocsp.Response, error) {
141 var certs []*x509.Certificate 15 var certs []*x509.Certificate
142 for _, c := range chain.Certificate { 16 for _, c := range chain.Certificate {
diff --git a/crypto/tls/ocsp_manager.go b/crypto/tls/ocsp_manager.go
new file mode 100644
index 0000000..dac4c5e
--- /dev/null
+++ b/crypto/tls/ocsp_manager.go
@@ -0,0 +1,134 @@
1package tls
2
3import (
4 "context"
5 "crypto/tls"
6 "errors"
7 "fmt"
8 "sync"
9 "time"
10
11 "golang.org/x/crypto/ocsp"
12)
13
14type OcspError struct {
15 Err error
16 AtBoot bool
17}
18
19func (e OcspError) Error() string {
20 return e.Err.Error()
21}
22
23func (e OcspError) Unwrap() error {
24 return e.Err
25}
26
27type OcspLogger interface {
28 Info(...interface{})
29 Errorf(string, ...interface{})
30}
31
32func OcspErrorLogger(l OcspLogger, c <-chan OcspError) func(context.Context, *sync.WaitGroup) error {
33 return func(ctx context.Context, wg *sync.WaitGroup) error {
34 wg.Add(1)
35 defer wg.Done()
36
37 for {
38 select {
39 case err := <-c:
40 l.Errorf("Error in OCSP stapling: %w", errors.Unwrap(err))
41 case <-ctx.Done():
42 l.Info("Shutting down OCSP logger")
43 return nil
44 }
45 }
46 }
47}
48
49type OcspManager struct {
50 CertPath, KeyPath string
51 Errors chan<- OcspError
52 cert *tls.Certificate
53 ocspRes *ocsp.Response
54 sync.RWMutex
55}
56
57func (m *OcspManager) loadCert() error {
58 cert, err := tls.LoadX509KeyPair(m.CertPath, m.KeyPath)
59 if err != nil {
60 return err
61 }
62 m.Lock()
63 m.cert = &cert
64 m.Unlock()
65
66 return nil
67}
68
69func (m *OcspManager) stapleCert() error {
70 // This makes a network request to an unknown server so don't hold the full
71 // lock while this is happening
72 m.RLock()
73 raw, ocspRes, err := GetOcspResponse(m.cert)
74 if err != nil {
75 return err
76 }
77 m.RUnlock()
78
79 m.Lock()
80 m.cert.OCSPStaple = raw
81 m.ocspRes = ocspRes
82 m.Unlock()
83
84 return nil
85}
86
87func (m *OcspManager) Init() error {
88 // All functions called here will handle locking themselves
89 if err := m.loadCert(); err != nil {
90 return err
91 }
92
93 if err := m.stapleCert(); err != nil {
94 return err
95 }
96
97 return nil
98}
99
100func (m *OcspManager) Run(ctx context.Context, wg *sync.WaitGroup) error {
101 wg.Add(1)
102 defer wg.Done()
103
104 t := time.NewTimer(m.ocspRes.NextUpdate.Sub(time.Now()) - time.Hour)
105
106 for {
107 select {
108 case <-t.C:
109 if err := m.stapleCert(); err != nil {
110 if m.Errors != nil {
111 m.Errors <- OcspError{err, false}
112 }
113 t.Reset(time.Hour)
114 continue
115 }
116 // We own this object and only we write it, no need to lock
117 t.Reset(m.ocspRes.NextUpdate.Sub(time.Now()) - time.Hour)
118 case <-ctx.Done():
119 return nil
120 }
121 }
122}
123
124// TODO: TLS.GetCertificate for dyanmic certs for LE (cache these)
125func (m *OcspManager) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) {
126 m.RLock()
127 defer m.RUnlock()
128
129 if m.cert != nil {
130 return m.cert, nil
131 }
132
133 return nil, fmt.Errorf("OCSP manager has no certificate stored")
134}