diff options
author | Mike Crute <mike@crute.us> | 2021-11-10 22:16:10 -0800 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2021-11-10 22:16:10 -0800 |
commit | 8c5b1d33cc3d4af0b7c0e0fc37a90ef80ba25fe5 (patch) | |
tree | d49f37a47aa3b853052a1f7a5a0e13f3e4a5d9e7 | |
parent | ef4444e6217592e2666b8a46e8c762eaf93fa8a6 (diff) | |
download | golib-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.go | 134 |
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 | ||
3 | import ( | 3 | import ( |
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 | ||
18 | type OcspError struct { | ||
19 | Err error | ||
20 | AtBoot bool | ||
21 | } | ||
22 | |||
23 | func (e OcspError) Error() string { | ||
24 | return e.Err.Error() | ||
25 | } | ||
26 | |||
27 | func (e OcspError) Unwrap() error { | ||
28 | return e.Err | ||
29 | } | ||
30 | |||
31 | type OcspLogger interface { | ||
32 | Info(...interface{}) | ||
33 | Errorf(string, ...interface{}) | ||
34 | } | ||
35 | |||
36 | func 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 | |||
53 | type OcspManager struct { | ||
54 | CertPath, KeyPath string | ||
55 | Errors chan<- OcspError | ||
56 | cert *tls.Certificate | ||
57 | ocspRes *ocsp.Response | ||
58 | sync.RWMutex | ||
59 | } | ||
60 | |||
61 | func (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 | |||
73 | func (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 | |||
91 | func (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 | |||
104 | func (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) | ||
129 | func (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 | |||
140 | func GetOcspResponse(chain *tls.Certificate) ([]byte, *ocsp.Response, error) { | 14 | func 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 @@ | |||
1 | package tls | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "crypto/tls" | ||
6 | "errors" | ||
7 | "fmt" | ||
8 | "sync" | ||
9 | "time" | ||
10 | |||
11 | "golang.org/x/crypto/ocsp" | ||
12 | ) | ||
13 | |||
14 | type OcspError struct { | ||
15 | Err error | ||
16 | AtBoot bool | ||
17 | } | ||
18 | |||
19 | func (e OcspError) Error() string { | ||
20 | return e.Err.Error() | ||
21 | } | ||
22 | |||
23 | func (e OcspError) Unwrap() error { | ||
24 | return e.Err | ||
25 | } | ||
26 | |||
27 | type OcspLogger interface { | ||
28 | Info(...interface{}) | ||
29 | Errorf(string, ...interface{}) | ||
30 | } | ||
31 | |||
32 | func 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 | |||
49 | type OcspManager struct { | ||
50 | CertPath, KeyPath string | ||
51 | Errors chan<- OcspError | ||
52 | cert *tls.Certificate | ||
53 | ocspRes *ocsp.Response | ||
54 | sync.RWMutex | ||
55 | } | ||
56 | |||
57 | func (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 | |||
69 | func (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 | |||
87 | func (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 | |||
100 | func (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) | ||
125 | func (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 | } | ||