-
Notifications
You must be signed in to change notification settings - Fork 261
/
ssl.go
287 lines (241 loc) · 9.17 KB
/
ssl.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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
package db_local
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"log"
"math/big"
"os"
"time"
filehelpers "github.com/turbot/go-kit/files"
"github.com/turbot/steampipe/pkg/utils"
)
const CertIssuer = "steampipe.io"
var (
CertExpiryTolerance = 180 * (24 * time.Hour) // 180 days
RootCertValidityPeriod = 5 * 365 * (24 * time.Hour) // 5 years
ServerCertValidityPeriod = 365 * (24 * time.Hour) // 1 year
)
// CertificatesExist checks if the root and server certificate and key files exist
func CertificatesExist() bool {
return filehelpers.FileExists(getRootCertLocation()) && filehelpers.FileExists(getServerCertLocation())
}
// RemoveServerCertificate removes the server certificate certificates so it will be regenerated
func RemoveServerCertificate() error {
utils.LogTime("db_local.RemoveServerCertificate start")
defer utils.LogTime("db_local.RemoveServerCertificate end")
if err := os.Remove(getServerCertLocation()); err != nil {
return err
}
return os.Remove(getServerCertKeyLocation())
}
// RemoveAllCertificates removes root and server certificates so that they can be regenerated
func RemoveAllCertificates() error {
utils.LogTime("db_local.RemoveAllCertificates start")
defer utils.LogTime("db_local.RemoveAllCertificates end")
// remove the root cert (but not key)
if err := os.Remove(getRootCertLocation()); err != nil {
return err
}
// remove the server cert and key
return RemoveServerCertificate()
}
// ValidateRootCertificate checks the root certificate exists, is not expired and has correct Subject
func ValidateRootCertificate() bool {
utils.LogTime("db_local.ValidateRootCertificates start")
defer utils.LogTime("db_local.ValidateRootCertificates end")
rootCertificate, err := utils.ParseCertificateInLocation(getRootCertLocation())
if err != nil {
return false
}
return (rootCertificate.Subject.CommonName == CertIssuer) && isCerticateExpiring(rootCertificate)
}
// ValidateServerCertificate checks the server certificate exists, is not expired and has correct issuer
func ValidateServerCertificate() bool {
utils.LogTime("db_local.ValidateServerCertificates start")
defer utils.LogTime("db_local.ValidateServerCertificates end")
serverCertificate, err := utils.ParseCertificateInLocation(getServerCertLocation())
if err != nil {
return false
}
return (serverCertificate.Issuer.CommonName == CertIssuer) && isCerticateExpiring(serverCertificate)
}
// if certificate or private key files do not exist, generate them
func ensureSelfSignedCertificate() (err error) {
if serverCertificateAndKeyExist() && rootCertificateAndKeyExists() {
return nil
}
// so one or both of the root and server certificate need creating
var rootPrivateKey *rsa.PrivateKey
var rootCertificate *x509.Certificate
if rootCertificateAndKeyExists() {
// if the root cert and key exist, load them
rootPrivateKey, err = loadRootPrivateKey()
if err != nil {
return err
}
rootCertificate, err = utils.ParseCertificateInLocation(getRootCertLocation())
} else {
// otherwise generate them
rootCertificate, rootPrivateKey, err = generateRootCertificate()
}
if err != nil {
return err
}
// now generate new server cert
return generateServerCertificates(rootCertificate, rootPrivateKey)
}
// rootCertificateAndKeyExists checks if the root certificate ands private key files exist
func rootCertificateAndKeyExists() bool {
return filehelpers.FileExists(getRootCertLocation()) && filehelpers.FileExists(getRootCertKeyLocation())
}
// serverCertificateAndKeyExist checks if the server certificate ands private key files exist
func serverCertificateAndKeyExist() bool {
return filehelpers.FileExists(getServerCertLocation()) && filehelpers.FileExists(getServerCertKeyLocation())
}
// isCerticateExpiring checks whether the certificate expires within a predefined CertExpiryTolerance period (defined above)
func isCerticateExpiring(certificate *x509.Certificate) bool {
return certificate.NotAfter.Add(-CertExpiryTolerance).After(time.Now())
}
func generateRootCertificate() (*x509.Certificate, *rsa.PrivateKey, error) {
utils.LogTime("db_local.generateServiceCertificates start")
defer utils.LogTime("db_local.generateServiceCertificates end")
// Load or create our own certificate authority
caPrivateKey, err := ensureRootPrivateKey()
if err != nil {
return nil, nil, err
}
now := time.Now()
// Certificate authority input
caCertificateData := &x509.Certificate{
SerialNumber: big.NewInt(2020),
NotBefore: now,
NotAfter: now.Add(RootCertValidityPeriod),
Subject: pkix.Name{CommonName: CertIssuer},
IsCA: true,
BasicConstraintsValid: true,
}
caCertificate, err := x509.CreateCertificate(rand.Reader, caCertificateData, caCertificateData, &caPrivateKey.PublicKey, caPrivateKey)
if err != nil {
log.Println("[WARN] failed to create certificate")
return nil, nil, err
}
if err := utils.WriteCertificate(getRootCertLocation(), caCertificate); err != nil {
log.Println("[WARN] failed to save the certificate")
return nil, nil, err
}
return caCertificateData, caPrivateKey, nil
}
func generateServerCertificates(caCertificateData *x509.Certificate, caPrivateKey *rsa.PrivateKey) error {
utils.LogTime("db_local.generateServerCertificates start")
defer utils.LogTime("db_local.generateServerCertificates end")
now := time.Now()
// set up for server certificate
serverCertificateData := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: caCertificateData.Subject,
Issuer: caCertificateData.Subject,
NotBefore: now,
NotAfter: now.Add(ServerCertValidityPeriod),
}
// Generate the server private key
serverPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
serverCertBytes, err := x509.CreateCertificate(rand.Reader, serverCertificateData, caCertificateData, &serverPrivKey.PublicKey, caPrivateKey)
if err != nil {
log.Println("[INFO] Failed to create server certificate")
return err
}
if err := utils.WriteCertificate(getServerCertLocation(), serverCertBytes); err != nil {
log.Println("[INFO] Failed to save server certificate")
return err
}
if err := utils.WritePrivateKey(getServerCertKeyLocation(), serverPrivKey); err != nil {
log.Println("[INFO] Failed to save server private key")
return err
}
return nil
}
// derive ssl status from out ssl mode
func sslStatus() string {
if serverCertificateAndKeyExist() {
return "on"
}
return "off"
}
// derive ssl parameters from the presence of the server certificate and key file
func dsnSSLParams() map[string]string {
if serverCertificateAndKeyExist() && rootCertificateAndKeyExists() {
// as per https://www.postgresql.org/docs/current/libpq-ssl.html#LIBQ-SSL-CERTIFICATES :
//
// For backwards compatibility with earlier versions of PostgreSQL, if a root CA file exists, the
// behavior of sslmode=require will be the same as that of verify-ca, meaning the
// server certificate is validated against the CA. Relying on this behavior is discouraged, and
// applications that need certificate validation should always use verify-ca or verify-full.
//
// Since we are using the Root Certificate, 'require' is overridden with 'verify-ca' anyway
return map[string]string{
"sslmode": "verify-ca",
"sslrootcert": getRootCertLocation(),
"sslcert": getServerCertLocation(),
"sslkey": getServerCertKeyLocation(),
}
}
return map[string]string{"sslmode": "disable"}
}
func ensureRootPrivateKey() (*rsa.PrivateKey, error) {
// first try to load the key
// if any errors are encountered this will just return nil
caPrivateKey, _ := loadRootPrivateKey()
if caPrivateKey != nil {
// we loaded one
return caPrivateKey, nil
}
// so we failed to load the key - generate instead
var err error
caPrivateKey, err = rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Println("[WARN] private key creation failed for ca failed")
return nil, err
}
if err := utils.WritePrivateKey(getRootCertKeyLocation(), caPrivateKey); err != nil {
log.Println("[WARN] failed to save root private key")
return nil, err
}
return caPrivateKey, nil
}
func loadRootPrivateKey() (*rsa.PrivateKey, error) {
location := getRootCertKeyLocation()
priv, err := os.ReadFile(location)
if err != nil {
log.Printf("[TRACE] loadRootPrivateKey - failed to load key from %s: %s", location, err.Error())
return nil, err
}
privPem, _ := pem.Decode(priv)
if privPem.Type != "RSA PRIVATE KEY" {
log.Printf("[TRACE] RSA private key is of the wrong type: %v", privPem.Type)
return nil, fmt.Errorf("RSA private key is of the wrong type: %v", privPem.Type)
}
privPemBytes := privPem.Bytes
var parsedKey interface{}
if parsedKey, err = x509.ParsePKCS1PrivateKey(privPemBytes); err != nil {
if parsedKey, err = x509.ParsePKCS8PrivateKey(privPemBytes); err != nil {
// note this returns type `interface{}`
log.Printf("[TRACE] failed to parse RSA private key: %s", err.Error())
return nil, err
}
}
var privateKey *rsa.PrivateKey
var ok bool
privateKey, ok = parsedKey.(*rsa.PrivateKey)
if !ok {
log.Printf("[TRACE] failed to parse RSA private key")
return nil, fmt.Errorf("failed to parse RSA private key")
}
return privateKey, nil
}