-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
database_certificates.go
133 lines (112 loc) · 4.25 KB
/
database_certificates.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
/*
Copyright 2022 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package db
import (
"context"
"crypto/x509/pkix"
"time"
"github.com/gravitational/trace"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/client/identityfile"
"github.com/gravitational/teleport/lib/tlsca"
)
// GenerateDatabaseCertificatesRequest contains the required fields used to generate database certificates
// Those certificates will be used by databases to set up mTLS authentication against Teleport
type GenerateDatabaseCertificatesRequest struct {
ClusterAPI auth.ClientI
Principals []string
OutputFormat identityfile.Format
OutputCanOverwrite bool
OutputLocation string
IdentityFileWriter identityfile.ConfigWriter
TTL time.Duration
Key *client.Key
// Password is used to generate JKS keystore used for cassandra format or Oracle wallet.
Password string
}
// GenerateDatabaseCertificates to be used by databases to set up mTLS authentication
func GenerateDatabaseCertificates(ctx context.Context, req GenerateDatabaseCertificatesRequest) ([]string, error) {
if len(req.Principals) == 0 ||
(len(req.Principals) == 1 && req.Principals[0] == "" && req.OutputFormat != identityfile.FormatSnowflake) {
return nil, trace.BadParameter("at least one hostname must be specified")
}
// For CockroachDB node certificates, CommonName must be "node":
//
// https://www.cockroachlabs.com/docs/v21.1/cockroach-cert#node-key-and-certificates
if req.OutputFormat == identityfile.FormatCockroach {
req.Principals = append([]string{"node"}, req.Principals...)
}
subject := pkix.Name{CommonName: req.Principals[0]}
if req.OutputFormat == identityfile.FormatMongo {
// Include Organization attribute in MongoDB certificates as well.
//
// When using X.509 member authentication, MongoDB requires O or OU to
// be non-empty so this will make the certs we generate compatible:
//
// https://docs.mongodb.com/manual/core/security-internal-authentication/#x.509
//
// The actual O value doesn't matter as long as it matches on all
// MongoDB cluster members so set it to the Teleport cluster name
// to avoid hardcoding anything.
clusterNameType, err := req.ClusterAPI.GetClusterName()
if err != nil {
return nil, trace.Wrap(err)
}
subject.Organization = []string{clusterNameType.GetClusterName()}
}
if req.Key == nil {
key, err := client.GenerateRSAKey()
if err != nil {
return nil, trace.Wrap(err)
}
req.Key = key
}
csr, err := tlsca.GenerateCertificateRequestPEM(subject, req.Key.PrivateKey)
if err != nil {
return nil, trace.Wrap(err)
}
resp, err := req.ClusterAPI.GenerateDatabaseCert(ctx,
&proto.DatabaseCertRequest{
CSR: csr,
// Important to include SANs since CommonName has been deprecated
// since Go 1.15:
// https://golang.org/doc/go1.15#commonname
ServerNames: req.Principals,
// Include legacy ServerName for compatibility.
ServerName: req.Principals[0],
TTL: proto.Duration(req.TTL),
RequesterName: proto.DatabaseCertRequest_TCTL,
})
if err != nil {
return nil, trace.Wrap(err)
}
req.Key.TLSCert = resp.Cert
req.Key.TrustedCerts = []auth.TrustedCerts{{
ClusterName: req.Key.ClusterName,
TLSCertificates: resp.CACerts,
}}
filesWritten, err := identityfile.Write(ctx, identityfile.WriteConfig{
OutputPath: req.OutputLocation,
Key: req.Key,
Format: req.OutputFormat,
OverwriteDestination: req.OutputCanOverwrite,
Writer: req.IdentityFileWriter,
Password: req.Password,
})
if err != nil {
return nil, trace.Wrap(err)
}
return filesWritten, nil
}