/
tls_cert_create.go
226 lines (200 loc) · 6.48 KB
/
tls_cert_create.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
package create
import (
"crypto/x509"
"flag"
"fmt"
"io/ioutil"
"net"
"os"
"strings"
"github.com/hashicorp/consul/command/flags"
"github.com/hashicorp/consul/command/tls"
"github.com/hashicorp/consul/tlsutil"
"github.com/mitchellh/cli"
)
func New(ui cli.Ui) *cmd {
c := &cmd{UI: ui}
c.init()
return c
}
type cmd struct {
UI cli.Ui
flags *flag.FlagSet
ca string
key string
server bool
client bool
cli bool
dc string
days int
domain string
help string
dnsnames flags.AppendSliceValue
ipaddresses flags.AppendSliceValue
prefix string
}
func (c *cmd) init() {
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
c.flags.StringVar(&c.ca, "ca", "#DOMAIN#-agent-ca.pem", "Provide path to the ca. Defaults to #DOMAIN#-agent-ca.pem.")
c.flags.StringVar(&c.key, "key", "#DOMAIN#-agent-ca-key.pem", "Provide path to the key. Defaults to #DOMAIN#-agent-ca-key.pem.")
c.flags.BoolVar(&c.server, "server", false, "Generate server certificate.")
c.flags.BoolVar(&c.client, "client", false, "Generate client certificate.")
c.flags.BoolVar(&c.cli, "cli", false, "Generate cli certificate.")
c.flags.IntVar(&c.days, "days", 365, "Provide number of days the certificate is valid for from now on. Defaults to 1 year.")
c.flags.StringVar(&c.dc, "dc", "dc1", "Provide the datacenter. Matters only for -server certificates. Defaults to dc1.")
c.flags.StringVar(&c.domain, "domain", "consul", "Provide the domain. Matters only for -server certificates.")
c.flags.Var(&c.dnsnames, "additional-dnsname", "Provide an additional dnsname for Subject Alternative Names. "+
"localhost is always included. This flag may be provided multiple times.")
c.flags.Var(&c.ipaddresses, "additional-ipaddress", "Provide an additional ipaddress for Subject Alternative Names. "+
"127.0.0.1 is always included. This flag may be provided multiple times.")
c.help = flags.Usage(help, c.flags)
}
func (c *cmd) Run(args []string) int {
if err := c.flags.Parse(args); err != nil {
if err == flag.ErrHelp {
return 0
}
c.UI.Error(fmt.Sprintf("Failed to parse args: %v", err))
return 1
}
if c.ca == "" {
c.UI.Error("Please provide the ca")
return 1
}
if c.key == "" {
c.UI.Error("Please provide the key")
return 1
}
if !((c.server && !c.client && !c.cli) ||
(!c.server && c.client && !c.cli) ||
(!c.server && !c.client && c.cli)) {
c.UI.Error("Please provide either -server, -client, or -cli")
return 1
}
var DNSNames []string
var IPAddresses []net.IP
var extKeyUsage []x509.ExtKeyUsage
var name, prefix string
for _, d := range c.dnsnames {
if len(d) > 0 {
DNSNames = append(DNSNames, strings.TrimSpace(d))
}
}
for _, i := range c.ipaddresses {
if len(i) > 0 {
IPAddresses = append(IPAddresses, net.ParseIP(strings.TrimSpace(i)))
}
}
if c.server {
name = fmt.Sprintf("server.%s.%s", c.dc, c.domain)
DNSNames = append(DNSNames, []string{name, "localhost"}...)
IPAddresses = append(IPAddresses, net.ParseIP("127.0.0.1"))
extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
prefix = fmt.Sprintf("%s-server-%s", c.dc, c.domain)
} else if c.client {
name = fmt.Sprintf("client.%s.%s", c.dc, c.domain)
DNSNames = append(DNSNames, []string{name, "localhost"}...)
IPAddresses = append(IPAddresses, net.ParseIP("127.0.0.1"))
extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
prefix = fmt.Sprintf("%s-client-%s", c.dc, c.domain)
} else if c.cli {
name = fmt.Sprintf("cli.%s.%s", c.dc, c.domain)
DNSNames = []string{name, "localhost"}
prefix = fmt.Sprintf("%s-cli-%s", c.dc, c.domain)
} else {
c.UI.Error("Neither client, cli nor server - should not happen")
return 1
}
var pkFileName, certFileName string
max := 10000
for i := 0; i <= max; i++ {
tmpCert := fmt.Sprintf("%s-%d.pem", prefix, i)
tmpPk := fmt.Sprintf("%s-%d-key.pem", prefix, i)
if tls.FileDoesNotExist(tmpCert) && tls.FileDoesNotExist(tmpPk) {
certFileName = tmpCert
pkFileName = tmpPk
break
}
if i == max {
c.UI.Error("Could not find a filename that doesn't already exist")
return 1
}
}
caFile := strings.Replace(c.ca, "#DOMAIN#", c.domain, 1)
keyFile := strings.Replace(c.key, "#DOMAIN#", c.domain, 1)
cert, err := ioutil.ReadFile(caFile)
if err != nil {
c.UI.Error(fmt.Sprintf("Error reading CA: %s", err))
return 1
}
key, err := ioutil.ReadFile(keyFile)
if err != nil {
c.UI.Error(fmt.Sprintf("Error reading CA key: %s", err))
return 1
}
if c.server {
c.UI.Info(
`==> WARNING: Server Certificates grants authority to become a
server and access all state in the cluster including root keys
and all ACL tokens. Do not distribute them to production hosts
that are not server nodes. Store them as securely as CA keys.`)
}
c.UI.Info("==> Using " + caFile + " and " + keyFile)
signer, err := tlsutil.ParseSigner(string(key))
if err != nil {
c.UI.Error(err.Error())
return 1
}
sn, err := tlsutil.GenerateSerialNumber()
if err != nil {
c.UI.Error(err.Error())
return 1
}
pub, priv, err := tlsutil.GenerateCert(signer, string(cert), sn, name, c.days, DNSNames, IPAddresses, extKeyUsage)
if err != nil {
c.UI.Error(err.Error())
return 1
}
if err = tlsutil.Verify(string(cert), pub, name); err != nil {
c.UI.Error("==> " + err.Error())
return 1
}
certFile, err := os.Create(certFileName)
if err != nil {
c.UI.Error(err.Error())
return 1
}
certFile.WriteString(pub)
c.UI.Output("==> Saved " + certFileName)
pkFile, err := os.Create(pkFileName)
if err != nil {
c.UI.Error(err.Error())
return 1
}
pkFile.WriteString(priv)
c.UI.Output("==> Saved " + pkFileName)
return 0
}
func (c *cmd) Synopsis() string {
return synopsis
}
func (c *cmd) Help() string {
return c.help
}
const synopsis = "Create a new certificate"
const help = `
Usage: consul tls cert create [options]
Create a new certificate
$ consul tls cert create -server
==> WARNING: Server Certificates grants authority to become a
server and access all state in the cluster including root keys
and all ACL tokens. Do not distribute them to production hosts
that are not server nodes. Store them as securely as CA keys.
==> Using consul-agent-ca.pem and consul-agent-ca-key.pem
==> Saved consul-server-dc1-0.pem
==> Saved consul-server-dc1-0-key.pem
$ consul tls cert -client
==> Using consul-agent-ca.pem and consul-agent-ca-key.pem
==> Saved consul-client-dc1-0.pem
==> Saved consul-client-dc1-0-key.pem
`