-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtls.go
208 lines (176 loc) · 5.27 KB
/
tls.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package main
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"fmt"
"math/big"
mrand "math/rand"
"net"
"net/http"
"time"
"github.com/caddyserver/certmagic"
"github.com/go-chi/chi"
)
// defaults for LetsEncrypt services
var (
DefaultHostPort = ":9090"
DefaultACMEEmail = "[email protected]"
)
// useTLS configures TLS for a service based on the passed in
// ConfigHTTP data. The passed in mux, allows using Middleware to
// send back a http pinning header
func useTLS(mw *chi.Mux, server ConfigHTTP) *tls.Config {
if server.SSL == nil {
log.Printf("[tls] %q no certs loaded (using HTTP) ...", server.Name)
return nil
}
var err error
var tlsConfig *tls.Config
switch {
case server.SSL.LetsEnc != nil:
log.Printf("[tls] %q loading lets encrypt certs ...", server.Name)
// provide an email address
certmagic.DefaultACME.Email = DefaultACMEEmail
if server.SSL.LetsEnc.Email != nil {
val, err := server.SSL.LetsEnc.Email.Expr.Value(&fileEvalCtx)
if err != nil {
panic(fmt.Errorf("lets encrypt email: %v", err))
}
certmagic.DefaultACME.Email = val.AsString()
}
// use the staging endpoint while we're developing
certmagic.DefaultACME.CA = certmagic.LetsEncryptProductionCA
tlsConfig, err = certmagic.TLS(server.SSL.LetsEnc.Hosts)
if err != nil {
panic(fmt.Errorf("lets encrypt SSL certs: %v", err))
}
case server.SSL.Crt == "" && server.SSL.Key == "":
log.Printf("[tls] %q loading self-signed certs ...", server.Name)
var pin []byte
tlsConfig, pin, err = cert(server.SSL.CACrt, server.SSL.CAKey)
if err != nil {
panic(fmt.Errorf("gen SSL certs: %v", err))
}
// add Pinning Key to output ...
mw.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Pinned-Key", fmt.Sprintf("sha256//%s", string(pin)))
next.ServeHTTP(w, r)
})
})
default:
log.Printf("[tls] %q loading external SSL certs ...", server.Name)
cer, err := tls.LoadX509KeyPair(server.SSL.Crt, server.SSL.Key)
if err != nil {
panic(fmt.Errorf("load SSL certs: %v", err)) // will stop the startup sequence...
}
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cer}}
}
return tlsConfig
}
// cert builds a x509 cert to use in HTTPS services.
func cert(caCrtFile, caKeyFile string) (serverTLSConf *tls.Config, pin []byte, err error) {
var caCrt *x509.Certificate
var caKey crypto.PrivateKey
rnd := mrand.New(mrand.NewSource(time.Now().UnixNano()))
max, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFF", 16)
if max == nil {
return nil, nil, ErrBigIntCreation
}
nets, err := net.Interfaces()
if err != nil {
return nil, nil, ErrGetNetInterface.F(err)
}
var ipAddrs []net.IP
for _, i := range nets {
addrs, err := i.Addrs()
if err != nil {
return nil, nil, ErrGetNetAddr.F(err)
}
for _, addr := range addrs {
switch v := addr.(type) {
case *net.IPNet:
ipAddrs = append(ipAddrs, v.IP)
case *net.IPAddr:
ipAddrs = append(ipAddrs, v.IP)
}
}
}
if caCrtFile != "" {
caCrtTLS, err := tls.LoadX509KeyPair(caCrtFile, caKeyFile)
if err != nil {
return nil, nil, ErrLoadX509.F(err)
}
if len(caCrtTLS.Certificate) > 0 {
caCrt, err = x509.ParseCertificate(caCrtTLS.Certificate[0])
if err != nil {
return nil, nil, ErrParseCACert.F(err)
}
caKey = caCrtTLS.PrivateKey
}
}
svrCrtPrvKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, nil, ErrGenKey.F(err)
}
svrCrt := x509.Certificate{
SerialNumber: new(big.Int).Rand(rnd, max),
Subject: pkix.Name{
Organization: []string{"Not a Organization Inc."},
CommonName: "localhost",
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24 * 180),
DNSNames: []string{"localhost"},
IPAddresses: ipAddrs,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
if caCrt == nil {
caCrt = &svrCrt
caKey = svrCrtPrvKey
}
svrCrtBytes, err := x509.CreateCertificate(rand.Reader, &svrCrt, caCrt, &svrCrtPrvKey.PublicKey, caKey)
if err != nil {
return nil, nil, ErrCreateX590Cert.F(err)
}
svrCrtPEM := new(bytes.Buffer)
pem.Encode(svrCrtPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: svrCrtBytes,
})
svrCrtPrvKeyDER, err := x509.MarshalECPrivateKey(svrCrtPrvKey)
if err != nil {
return nil, nil, ErrMarshalPrivKey.F(err)
}
svrCrtPrvKeyPEM := new(bytes.Buffer)
pem.Encode(svrCrtPrvKeyPEM, &pem.Block{
Type: "PRIVATE KEY",
Bytes: svrCrtPrvKeyDER,
})
serverCert, err := tls.X509KeyPair(svrCrtPEM.Bytes(), svrCrtPrvKeyPEM.Bytes())
if err != nil {
return nil, nil, ErrCreateTLSCert.F(err)
}
serverTLSConf = &tls.Config{
Certificates: []tls.Certificate{serverCert},
}
svrCrtPubKeyDER, err := x509.MarshalPKIXPublicKey(&svrCrtPrvKey.PublicKey)
if err != nil {
return nil, nil, ErrMarshalPubKey.F(err)
}
sum := sha256.Sum256(svrCrtPubKeyDER)
pin = make([]byte, base64.StdEncoding.EncodedLen(len(sum)))
base64.StdEncoding.Encode(pin, sum[:])
return serverTLSConf, pin, err
}