github.imxd.top/hashicorp/consul@v1.4.5/command/tls/cert/create/tls_cert_create.go (about) 1 package create 2 3 import ( 4 "crypto/x509" 5 "flag" 6 "fmt" 7 "io/ioutil" 8 "net" 9 "os" 10 "strings" 11 12 "github.com/hashicorp/consul/command/flags" 13 "github.com/hashicorp/consul/command/tls" 14 "github.com/mitchellh/cli" 15 ) 16 17 func New(ui cli.Ui) *cmd { 18 c := &cmd{UI: ui} 19 c.init() 20 return c 21 } 22 23 type cmd struct { 24 UI cli.Ui 25 flags *flag.FlagSet 26 ca string 27 key string 28 server bool 29 client bool 30 cli bool 31 dc string 32 days int 33 domain string 34 help string 35 dnsnames flags.AppendSliceValue 36 prefix string 37 } 38 39 func (c *cmd) init() { 40 c.flags = flag.NewFlagSet("", flag.ContinueOnError) 41 c.flags.StringVar(&c.ca, "ca", "#DOMAIN#-agent-ca.pem", "Provide path to the ca. Defaults to #DOMAIN#-agent-ca.pem.") 42 c.flags.StringVar(&c.key, "key", "#DOMAIN#-agent-ca-key.pem", "Provide path to the key. Defaults to #DOMAIN#-agent-ca-key.pem.") 43 c.flags.BoolVar(&c.server, "server", false, "Generate server certificate.") 44 c.flags.BoolVar(&c.client, "client", false, "Generate client certificate.") 45 c.flags.BoolVar(&c.cli, "cli", false, "Generate cli certificate.") 46 c.flags.IntVar(&c.days, "days", 365, "Provide number of days the certificate is valid for from now on. Defaults to 1 year.") 47 c.flags.StringVar(&c.dc, "dc", "dc1", "Provide the datacenter. Matters only for -server certificates. Defaults to dc1.") 48 c.flags.StringVar(&c.domain, "domain", "consul", "Provide the domain. Matters only for -server certificates.") 49 c.flags.Var(&c.dnsnames, "additional-dnsname", "Provide an additional dnsname for Subject Alternative Names. "+ 50 "127.0.0.1 and localhost are always included. This flag may be provided multiple times.") 51 c.help = flags.Usage(help, c.flags) 52 } 53 54 func (c *cmd) Run(args []string) int { 55 if err := c.flags.Parse(args); err != nil { 56 if err == flag.ErrHelp { 57 return 0 58 } 59 c.UI.Error(fmt.Sprintf("Failed to parse args: %v", err)) 60 return 1 61 } 62 if c.ca == "" { 63 c.UI.Error("Please provide the ca") 64 return 1 65 } 66 if c.key == "" { 67 c.UI.Error("Please provide the key") 68 return 1 69 } 70 71 if !((c.server && !c.client && !c.cli) || 72 (!c.server && c.client && !c.cli) || 73 (!c.server && !c.client && c.cli)) { 74 c.UI.Error("Please provide either -server, -client, or -cli") 75 return 1 76 } 77 78 var DNSNames []string 79 var IPAddresses []net.IP 80 var extKeyUsage []x509.ExtKeyUsage 81 var name, prefix string 82 83 for _, d := range c.dnsnames { 84 if len(d) > 0 { 85 DNSNames = append(DNSNames, strings.TrimSpace(d)) 86 } 87 } 88 89 if c.server { 90 name = fmt.Sprintf("server.%s.%s", c.dc, c.domain) 91 DNSNames = append(DNSNames, []string{name, "localhost"}...) 92 IPAddresses = []net.IP{net.ParseIP("127.0.0.1")} 93 extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} 94 prefix = fmt.Sprintf("%s-server-%s", c.dc, c.domain) 95 } else if c.client { 96 name = fmt.Sprintf("client.%s.%s", c.dc, c.domain) 97 DNSNames = append(DNSNames, []string{name, "localhost"}...) 98 IPAddresses = []net.IP{net.ParseIP("127.0.0.1")} 99 extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth} 100 prefix = fmt.Sprintf("%s-client-%s", c.dc, c.domain) 101 } else if c.cli { 102 name = fmt.Sprintf("cli.%s.%s", c.dc, c.domain) 103 DNSNames = []string{name, "localhost"} 104 prefix = fmt.Sprintf("%s-cli-%s", c.dc, c.domain) 105 } else { 106 c.UI.Error("Neither client, cli nor server - should not happen") 107 return 1 108 } 109 110 var pkFileName, certFileName string 111 max := 10000 112 for i := 0; i <= max; i++ { 113 tmpCert := fmt.Sprintf("%s-%d.pem", prefix, i) 114 tmpPk := fmt.Sprintf("%s-%d-key.pem", prefix, i) 115 if tls.FileDoesNotExist(tmpCert) && tls.FileDoesNotExist(tmpPk) { 116 certFileName = tmpCert 117 pkFileName = tmpPk 118 break 119 } 120 if i == max { 121 c.UI.Error("Could not find a filename that doesn't already exist") 122 return 1 123 } 124 } 125 126 caFile := strings.Replace(c.ca, "#DOMAIN#", c.domain, 1) 127 keyFile := strings.Replace(c.key, "#DOMAIN#", c.domain, 1) 128 cert, err := ioutil.ReadFile(caFile) 129 if err != nil { 130 c.UI.Error(fmt.Sprintf("Error reading CA: %s", err)) 131 return 1 132 } 133 key, err := ioutil.ReadFile(keyFile) 134 if err != nil { 135 c.UI.Error(fmt.Sprintf("Error reading CA key: %s", err)) 136 return 1 137 } 138 139 if c.server { 140 c.UI.Info( 141 `==> WARNING: Server Certificates grants authority to become a 142 server and access all state in the cluster including root keys 143 and all ACL tokens. Do not distribute them to production hosts 144 that are not server nodes. Store them as securely as CA keys.`) 145 } 146 c.UI.Info("==> Using " + caFile + " and " + keyFile) 147 148 signer, err := tls.ParseSigner(string(key)) 149 if err != nil { 150 c.UI.Error(err.Error()) 151 return 1 152 } 153 154 sn, err := tls.GenerateSerialNumber() 155 if err != nil { 156 c.UI.Error(err.Error()) 157 return 1 158 } 159 160 pub, priv, err := tls.GenerateCert(signer, string(cert), sn, name, c.days, DNSNames, IPAddresses, extKeyUsage) 161 if err != nil { 162 c.UI.Error(err.Error()) 163 return 1 164 } 165 166 if err = tls.Verify(string(cert), pub, name); err != nil { 167 c.UI.Error("==> " + err.Error()) 168 return 1 169 } 170 171 certFile, err := os.Create(certFileName) 172 if err != nil { 173 c.UI.Error(err.Error()) 174 return 1 175 } 176 certFile.WriteString(pub) 177 c.UI.Output("==> Saved " + certFileName) 178 179 pkFile, err := os.Create(pkFileName) 180 if err != nil { 181 c.UI.Error(err.Error()) 182 return 1 183 } 184 pkFile.WriteString(priv) 185 c.UI.Output("==> Saved " + pkFileName) 186 187 return 0 188 } 189 190 func (c *cmd) Synopsis() string { 191 return synopsis 192 } 193 194 func (c *cmd) Help() string { 195 return c.help 196 } 197 198 const synopsis = "Create a new certificate" 199 const help = ` 200 Usage: consul tls cert create [options] 201 202 Create a new certificate 203 204 $ consul tls cert create -server 205 ==> WARNING: Server Certificates grants authority to become a 206 server and access all state in the cluster including root keys 207 and all ACL tokens. Do not distribute them to production hosts 208 that are not server nodes. Store them as securely as CA keys. 209 ==> Using consul-agent-ca.pem and consul-agent-ca-key.pem 210 ==> Saved consul-server-dc1-0.pem 211 ==> Saved consul-server-dc1-0-key.pem 212 $ consul tls cert -client 213 ==> Using consul-agent-ca.pem and consul-agent-ca-key.pem 214 ==> Saved consul-client-dc1-0.pem 215 ==> Saved consul-client-dc1-0-key.pem 216 `