Skip to content

Commit 67f4051

Browse files
authored
Merge pull request #3 from bojand/master
refactor server buildServerTLSConfig function into tls package
2 parents 12f54e2 + 1c82246 commit 67f4051

9 files changed

Lines changed: 508 additions & 94 deletions

File tree

go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@ module github.com/cloudhut/common
33
go 1.19
44

55
require (
6+
github.com/fsnotify/fsnotify v1.6.0
67
github.com/go-chi/chi/v5 v5.0.8
78
github.com/google/go-cmp v0.5.9
89
github.com/prometheus/client_golang v1.15.1
10+
github.com/stretchr/testify v1.8.0
911
go.uber.org/zap v1.24.0
1012
)
1113

1214
require (
1315
github.com/beorn7/perks v1.0.1 // indirect
1416
github.com/cespare/xxhash/v2 v2.2.0 // indirect
15-
github.com/fsnotify/fsnotify v1.6.0 // indirect
17+
github.com/davecgh/go-spew v1.1.1 // indirect
1618
github.com/golang/protobuf v1.5.3 // indirect
19+
github.com/kr/text v0.2.0 // indirect
1720
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
21+
github.com/pmezard/go-difflib v1.0.0 // indirect
1822
github.com/prometheus/client_model v0.4.0 // indirect
1923
github.com/prometheus/common v0.43.0 // indirect
2024
github.com/prometheus/procfs v0.9.0 // indirect
2125
go.uber.org/atomic v1.11.0 // indirect
2226
go.uber.org/multierr v1.11.0 // indirect
2327
golang.org/x/sys v0.8.0 // indirect
2428
google.golang.org/protobuf v1.30.0 // indirect
29+
gopkg.in/yaml.v3 v3.0.1 // indirect
2530
)

go.sum

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
33
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
44
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
55
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
6+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
7+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
68
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
710
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
811
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
912
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
@@ -15,10 +18,14 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
1518
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
1619
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
1720
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
21+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
22+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
23+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
1824
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
1925
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
2026
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
2127
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
28+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2229
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
2330
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
2431
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
@@ -27,7 +34,12 @@ github.com/prometheus/common v0.43.0 h1:iq+BVjvYLei5f27wiuNiB1DN6DYQkp1c8Bx0Vykh
2734
github.com/prometheus/common v0.43.0/go.mod h1:NCvr5cQIh3Y/gy73/RdVtC9r8xxrxwJnB+2lB3BxrFc=
2835
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
2936
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
37+
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
38+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
39+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
40+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
3041
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
42+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
3143
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
3244
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
3345
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
@@ -44,4 +56,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
4456
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
4557
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
4658
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
59+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
60+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
61+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
4762
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
63+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

rest/server.go

Lines changed: 2 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package rest
22

33
import (
44
"context"
5-
"crypto/tls"
65
"fmt"
76
"net"
87
"net/http"
@@ -12,7 +11,7 @@ import (
1211
"sync"
1312
"syscall"
1413

15-
"github.com/fsnotify/fsnotify"
14+
"github.com/cloudhut/common/tls"
1615
"github.com/go-chi/chi/v5"
1716
"go.uber.org/zap"
1817
)
@@ -42,7 +41,7 @@ func NewServer(cfg *Config, logger *zap.Logger, router *chi.Mux) (*Server, error
4241
}
4342

4443
if cfg.TLS.Enabled {
45-
tlsCfg, err := buildServerTLSConfig(logger, cfg.TLS)
44+
tlsCfg, err := tls.BuildWatchedTLSConfig(logger, cfg.TLS.CertFilepath, cfg.TLS.KeyFilepath, nil)
4645
if err != nil {
4746
return nil, fmt.Errorf("failed to create TLS config: %w", err)
4847
}
@@ -109,93 +108,3 @@ func (s *Server) Start() error {
109108

110109
return nil
111110
}
112-
113-
func buildServerTLSConfig(logger *zap.Logger, cfg TLSConfig) (*tls.Config, error) {
114-
cert, err := tls.LoadX509KeyPair(cfg.CertFilepath, cfg.KeyFilepath)
115-
if err != nil {
116-
return nil, fmt.Errorf("failed loading TLS cert: %w", err)
117-
}
118-
119-
signalCh := make(chan os.Signal, 1)
120-
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)
121-
122-
watcher, err := fsnotify.NewWatcher()
123-
if err != nil {
124-
return nil, fmt.Errorf("failed to setup file watcher for hot reloading tls certificates: %w", err)
125-
}
126-
127-
var lock sync.RWMutex
128-
go func() {
129-
defer watcher.Close()
130-
for {
131-
select {
132-
case event, ok := <-watcher.Events:
133-
if !ok {
134-
return
135-
}
136-
137-
// Ignore all events that are neither remove nor write
138-
isRelevantEvent := event.Has(fsnotify.Remove) || event.Has(fsnotify.Write)
139-
if !isRelevantEvent {
140-
continue
141-
}
142-
143-
if event.Has(fsnotify.Remove) {
144-
// Kubernetes uses symbolic links to create the illusion of atomic writes.
145-
// Thus, we have to watch for the remove event and reconfigure our watcher.
146-
// See: https://ahmet.im/blog/kubernetes-inotify/
147-
_ = watcher.Remove(event.Name)
148-
err = watcher.Add(cfg.CertFilepath)
149-
if err != nil {
150-
logger.Error("failed to re-add file watcher",
151-
zap.String("file_path", cfg.CertFilepath),
152-
zap.Error(err))
153-
}
154-
155-
err = watcher.Add(cfg.KeyFilepath)
156-
if err != nil {
157-
logger.Warn("failed to re-add file watcher",
158-
zap.String("file_path", cfg.KeyFilepath),
159-
zap.Error(err))
160-
}
161-
}
162-
163-
logger.Info("hot reloading the TLS certificate")
164-
165-
newCert, err := tls.LoadX509KeyPair(cfg.CertFilepath, cfg.KeyFilepath)
166-
if err != nil {
167-
logger.Error("failed to load certificates", zap.Error(err))
168-
continue
169-
}
170-
lock.Lock()
171-
cert = newCert
172-
lock.Unlock()
173-
174-
logger.Info("successfully hot reloaded the TLS certificate")
175-
case err, ok := <-watcher.Errors:
176-
if !ok {
177-
return
178-
}
179-
logger.Error("tls certificate watcher error", zap.Error(err))
180-
case <-signalCh:
181-
return
182-
}
183-
}
184-
}()
185-
if err := watcher.Add(cfg.CertFilepath); err != nil {
186-
return nil, fmt.Errorf("failed to setup watcher for cert file: %w", err)
187-
}
188-
if err = watcher.Add(cfg.KeyFilepath); err != nil {
189-
return nil, fmt.Errorf("failed to setup watcher for key file: %w", err)
190-
}
191-
192-
return &tls.Config{
193-
MinVersion: tls.VersionTLS12,
194-
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
195-
lock.RLock()
196-
defer lock.RUnlock()
197-
198-
return &cert, nil
199-
},
200-
}, nil
201-
}

tls/testdata/certs/localhost.crt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIC5TCCAc2gAwIBAgIJAJ6N+ougLqO+MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
3+
BAMMCWxvY2FsaG9zdDAeFw0yMzA2MDUxMzIxMzVaFw0yNDA1MzAxMzIxMzVaMBQx
4+
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
5+
ggEBAMau0ywozyQbvIrf4HlOuBahwHOrJ37Ho7gTDiYfh8/WecipEbQzVFl85iK5
6+
iFvMPQTeN+ld0FsXzTlDwAU7j2L1rF9FnNJeUUnBsT5Fd0uhWixrUbM0jfBhg+A8
7+
9uNJ3M084YxmRmuZ/MMSbu3RLMQP8YCJLCWphfDnr6EK04ggIV2C/aTZ+D7eUuQF
8+
aUD3OmfGx0mXFYdegwwQnPdqeOvq8V0//F1KCPllwiOuK3pjbBC/mRJXCbAkjTZs
9+
kq6qeHURCTWscTp3fv/5UBJBVlZxUwI96IJlDWJg4aYrAvuofrry8nE+Tko3h6ZN
10+
2z0Jm/CrV+39fmPUruUVea2DVpUCAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
11+
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
12+
AQsFAAOCAQEArSGyWFWA6fSG9blCDzs/E0HOQayBuyuTnYMcSH1WVCX34eqGr4Kd
13+
oJIjXc1sf9XZ4oRB3NYWKV0HFPnR7EtuY9K1QkcoLlpFvJgYcIu9zW9bl6yVehBt
14+
gDN7uZ6ly9URxbM3yRLTT+Hy5tO+AoOkKvfjf5dp7ieIRPyyn4AjpLuJID9BqP/s
15+
L7bATFt7RiYp4BTmKJ19R7X2lABzGa2JvyHV0Y55JuYaABsRl7rarIPu6PFfVnvs
16+
z/l5vB95FBPIQpBcxXA77hLzwj1RczFXzlI8uCyXn3EfqYn7UaoOqw6/5W3F8g+Y
17+
VwwxpeIhldfeahJNAah+1V6tO26hr8gJQw==
18+
-----END CERTIFICATE-----

tls/testdata/certs/localhost.key

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDGrtMsKM8kG7yK
3+
3+B5TrgWocBzqyd+x6O4Ew4mH4fP1nnIqRG0M1RZfOYiuYhbzD0E3jfpXdBbF805
4+
Q8AFO49i9axfRZzSXlFJwbE+RXdLoVosa1GzNI3wYYPgPPbjSdzNPOGMZkZrmfzD
5+
Em7t0SzED/GAiSwlqYXw56+hCtOIICFdgv2k2fg+3lLkBWlA9zpnxsdJlxWHXoMM
6+
EJz3anjr6vFdP/xdSgj5ZcIjrit6Y2wQv5kSVwmwJI02bJKuqnh1EQk1rHE6d37/
7+
+VASQVZWcVMCPeiCZQ1iYOGmKwL7qH668vJxPk5KN4emTds9CZvwq1ft/X5j1K7l
8+
FXmtg1aVAgMBAAECggEBAKHPM9CdE8Y2iKEZn3lsMOTNqy0I0UuhT6bUbguCVltg
9+
MyLG/tIhk6ql28+gBnuspG1YhXSboNrvUYY3tSUN0sMnjdCxovx5L/6/rpgmfver
10+
WwMeDBXE0WxaHsr7G58UQq0rzg1IJkXvzTkZxBoO50RuL6MdFEVAAQOnzRN8+7W5
11+
9dX0V2KKYWm8DoouxpgCFkpfCviwgaHUQWha85zbLgGRKN7lVcM5uUFMcl5ypxGz
12+
g3+s1TBw+g6Al/o5x1QSTrhydvnAKsWgUri9GHBSt0NoA4BSNSHLmhv5q7abJE7L
13+
St+TvbghhsbKGReP2zgikQ7UVtZGqhtf99irP4Gu4OkCgYEA400BbglVF63mAwjs
14+
ge9VHSHAOZcFtBzwWt4+nC3wYEE1sutnLuuHEh1AR18RmXWsnQ3I//i2S6KXoaKp
15+
qKBTDa/N0sLEN5mT9pLhnzNJF0Seo4MHkCig2i+zHW7VlLJJD/9/kedwlmtmhfTe
16+
PlVI1fTbfhDVqqLchEHo6AxYGGcCgYEA38TPMp8RyM392HlGPCI2ngY8yLtxkdQT
17+
ONHB8fA/LIeaTjlzU/NI0/J/sNZy+B8LEKYMA62m3qbAi7army+IrmogWJaTrDRa
18+
FlXSUg9F31MOhTX0T8tBAskkH11RD879wHejFbwylLsZQmDS+F8gG/6bNT2pDtgz
19+
2Kyn8IaJq6MCgYEArlYv1I//3guZMZa0n+xLYe6zGvjEfSL9DxUK/IsXpRwe7b40
20+
A/7OOIyK8rLuMr/YxxT9p6bBWz24A1dZvWZKjWLcAN011ldK74I03wBc/SW6bzte
21+
n6kpxm9zeA28bzJXa5fR5ryW1ChIGFJ562FKXiBSAV00JI6JiD9tPh3Jq90CgYAZ
22+
0PH6rCF4IlPcCrnQrD3S43NV0VJb+bSyBHk0uXwAXjCuP7CPiezoDv0uYL9o4uP6
23+
6r1OG1W6MFDcjZmk0MobHUFYFx84ad3O393g+8Qa7NErCzuBjTiV4rDZMYHtqfra
24+
nrLhChJn2GIkp1kPsKHauPgdH10GymjI4bqKZGszswKBgQCB2eT638rKIJobA2MA
25+
kCYwhMldehXIlmBGy/QLhDEdJ2qridOb79pDbnKTd+vyBEmU+V1W/UK1kG/NtA9S
26+
yXkWIRWxzC8qf9/D9tifnybCLIpdn62YBGLcAEJKCsP1o0U9VR8v2HjkDRghjUkI
27+
vvlr90zAjfyTcXmG3kWplyeglQ==
28+
-----END PRIVATE KEY-----

tls/testdata/certs/localhost2.crt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIC5TCCAc2gAwIBAgIJAK/t3663m69zMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
3+
BAMMCWxvY2FsaG9zdDAeFw0yMzA2MDUxMzIxMzVaFw0yNDA1MjAxMzIxMzVaMBQx
4+
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
5+
ggEBAMHf7WSZoHsDZ1UBVImL/cw77N+yJ2kVufaYz7Uc3knuVProXXw98RhUG5cl
6+
JXiAgw35+QF4+TyEzeifF6RplrX90AGStmwgDUBveCjqhC5QPVsJ8tbIXX0X50DA
7+
dNDo/2mT3Kb4Tlzhq5D7cVRrJXrCdB3WNX5cNwiYN1MpbnXbydARlHlcdzs9u0yk
8+
BFoiYajmkseYtcBAr91CxCAn8Mxo2Un0CVaVW5VGIIbA4qh0i8cnzW4GLfpCIxsQ
9+
dIbt4wk3j10v2eLq3U+dtHWbTQQyMerB4hDwb95AyUhjdvO4xNXEyHki69LBwV8a
10+
+4+nJddvLw9hAfdWWyiEz7B0me0CAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
11+
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
12+
AQsFAAOCAQEAO7aNbohyfAU3+8U5ODF38GUJICxwpGzJF8okUSaxa8C6A28NsPep
13+
3GOaoVi39yekq1YrqVfCGnVFqisYV/VOBdNZVORHtJpB40700IKrMaBmERkiQpT1
14+
VxzgSr1piXPVXHFJZHnrnvA1hBXxSSSXguNZRavFrj0i89OQouHy13jFSTFr91K3
15+
ktajwLt4YEKelsEqTurQcjX75i+Q0YlsD+UjOHp9F4P82mgFPtE0WWeU05+5n1G4
16+
/T5xKUH9NrSJdcDgFSV4yPNfdsPdJfA5Ohfks/NJoo1F84MajPvksAoitOUcSlgc
17+
QA8a9lT9fnAC3piDSXQybCFZV9cNVx4E1A==
18+
-----END CERTIFICATE-----

tls/testdata/certs/localhost2.key

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDB3+1kmaB7A2dV
3+
AVSJi/3MO+zfsidpFbn2mM+1HN5J7lT66F18PfEYVBuXJSV4gIMN+fkBePk8hM3o
4+
nxekaZa1/dABkrZsIA1Ab3go6oQuUD1bCfLWyF19F+dAwHTQ6P9pk9ym+E5c4auQ
5+
+3FUayV6wnQd1jV+XDcImDdTKW5128nQEZR5XHc7PbtMpARaImGo5pLHmLXAQK/d
6+
QsQgJ/DMaNlJ9AlWlVuVRiCGwOKodIvHJ81uBi36QiMbEHSG7eMJN49dL9ni6t1P
7+
nbR1m00EMjHqweIQ8G/eQMlIY3bzuMTVxMh5IuvSwcFfGvuPpyXXby8PYQH3Vlso
8+
hM+wdJntAgMBAAECggEBAIWV6vfnVwmL1dZfnUVNPWpNXDDii38/5iwBLRVJN+1P
9+
GCTumQOzln1B7uTdRo1aV3L469dU6L8Hbu27OUojKyJpKbr7wVCNYTQl2nCu7rcO
10+
uMgS+c1+r9Qy9TfLpHISKXMw29f8vdoH8PRsHLGjRmbot6ObZq6TkaQNZgmaQa9Y
11+
tsbXRK7L2hT4gNoNNlonTnD/7xw2eqJU5btmEZmQ0FVeizvxjbpM0/QAYjffC50o
12+
qKokUz0ARzuUC84ke0K5HbNYyygDARkekrLAJIIJFHNBjsDrUd1Hd8F1tveI8fUc
13+
jHfvUszdX08I6Lg4tFQ0ESSUcOPbM9gOmMRKUGE+v20CgYEA9HTW5/Qbu8Icr5YO
14+
uuQPkWvtcG6oGmoag7NUcV3WXKoAFLrV2Yi93XEY3JtRoX7kFGGYTId7aqFYaFnq
15+
D8uR/NWJwzzSgA/3CTJ6Bx0HsIO84hjBbddFOMy79koPVHXV9gZyFVZDoIuu5PRX
16+
MPQJfLAlnxxgdHbKRMp5J/4uzQsCgYEAywee86rgLrFyyLOBJG8nR/2f2cmjcJHg
17+
NyBM3fXgewCQu0r/xKW6y1U3560XAxiFfIADP2F8L7xgPulmJtRnKU3z0cHHwmsc
18+
/MJvO8rvrMgU7O/fNLX5N1i8uZDrxmQcf8S0u0PAicQTiXorycOB1EH5Zf8CQVMo
19+
ZxoPpG733+cCgYBgEUafcywu9lLFoif5xERl9s8h3yrK7qWq2h+2SZVDZz+O5fnC
20+
el17F8YYdCV5XN+PLudmM9wJhIy0vZkhSfP+M4DnLBDhaOTBRYf1IbBy6uKgy+/A
21+
FdhLQRIg8OvjWkeSXugYgIUlI5/AtFFLmKvdx2+RftpdCo3kyNkiIV8NDwKBgAQa
22+
B1AM57KJyzPazIUb6cM+kHgp5q9jgxAaCvOBACP8AvCFt10VrAxnkFWR3aEmYav+
23+
OhKRuZyNRbR/qpymNd9Tv9VBAPQgjdldZDnlA6qN8D5JKk06T+qaVFW7Y8gCRcEf
24+
DDesSrt9xpdEbJYK6RiMrKku2bDQKUTL9fzwcPmJAoGAbUEi8TemjMGkThiu9do+
25+
yuZx7DlBG/+NrE/xkHO7RSK6IxX6KorYp2vqGSmz6dm6zp1GwoFOqC+ArD40de4V
26+
gDHZV9mI1IllXAlbTY05qiIeJlyTvert3TTW/PGDrqnr3+EDdx4fecU+FdJ0b3Yk
27+
7F3iknvaFE055+75cb/fm8k=
28+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)