2023-03-28 19:12:30 +00:00
// Copyright (c) HashiCorp, Inc.
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
2023-03-28 19:12:30 +00:00
2018-12-19 08:22:49 +00:00
package create
import (
"flag"
"fmt"
2023-01-30 17:26:56 +00:00
"github.com/mitchellh/cli"
2018-12-19 08:22:49 +00:00
"github.com/hashicorp/consul/command/flags"
"github.com/hashicorp/consul/command/tls"
2020-01-31 16:12:36 +00:00
"github.com/hashicorp/consul/lib/file"
2019-06-27 20:22:07 +00:00
"github.com/hashicorp/consul/tlsutil"
2018-12-19 08:22:49 +00:00
)
func New ( ui cli . Ui ) * cmd {
c := & cmd { UI : ui }
c . init ( )
return c
}
type cmd struct {
UI cli . Ui
flags * flag . FlagSet
help string
days int
domain string
2021-01-27 07:52:15 +00:00
clusterID string
2018-12-19 08:22:49 +00:00
constraint bool
2021-01-27 07:52:15 +00:00
commonName string
2018-12-19 08:22:49 +00:00
additionalConstraints flags . AppendSliceValue
}
func ( c * cmd ) init ( ) {
c . flags = flag . NewFlagSet ( "" , flag . ContinueOnError )
2019-03-01 16:25:37 +00:00
// TODO: perhaps add a -years arg to better capture user intent given that leap years are a thing
2023-01-30 17:26:56 +00:00
c . flags . IntVar ( & c . days , "days" , 1825 , "Number of days the CA is valid for. Defaults to 1825 days (approximately 5 years)." )
c . flags . BoolVar ( & c . constraint , "name-constraint" , false , "Enables X.509 name constraints for the CA. " +
"If used, the CA only signs certificates for localhost and the domains specified by -domain and -additional-name-constraint. " +
"If Consul's UI is served over HTTPS in your deployment, add its DNS name with -additional-constraint. Defaults to false." )
c . flags . StringVar ( & c . domain , "domain" , "consul" , "The DNS domain of the Consul cluster that agents are configured with. " +
"Defaults to consul. Only used when -name-constraint is set. " +
"Additional domains can be passed with -additional-name-constraint." )
c . flags . StringVar ( & c . clusterID , "cluster-id" , "" , "ID of the Consul cluster. Sets the CA's URI with the SPIFFEID composed of the cluster ID and domain (specified by -domain or 'consul' by default)." )
2021-01-27 07:52:15 +00:00
c . flags . StringVar ( & c . commonName , "common-name" , "" , "Common Name of CA. Defaults to Consul Agent CA." )
2018-12-19 08:22:49 +00:00
c . flags . Var ( & c . additionalConstraints , "additional-name-constraint" , "Add name constraints for the CA. Results in rejecting certificates " +
"for other DNS than specified. Can be used multiple times. Only used in combination with -name-constraint." )
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
}
certFileName := fmt . Sprintf ( "%s-agent-ca.pem" , c . domain )
pkFileName := fmt . Sprintf ( "%s-agent-ca-key.pem" , c . domain )
if ! ( tls . FileDoesNotExist ( certFileName ) ) {
c . UI . Error ( certFileName + " already exists." )
return 1
}
if ! ( tls . FileDoesNotExist ( pkFileName ) ) {
c . UI . Error ( pkFileName + " already exists." )
return 1
}
constraints := [ ] string { }
if c . constraint {
constraints = append ( c . additionalConstraints , [ ] string { c . domain , "localhost" } ... )
}
2020-01-31 16:12:36 +00:00
2021-01-27 07:52:15 +00:00
ca , pk , err := tlsutil . GenerateCA ( tlsutil . CAOpts { Name : c . commonName , Days : c . days , Domain : c . domain , PermittedDNSDomains : constraints , ClusterID : c . clusterID } )
2018-12-19 08:22:49 +00:00
if err != nil {
c . UI . Error ( err . Error ( ) )
2020-01-31 16:12:36 +00:00
return 1
2018-12-19 08:22:49 +00:00
}
2020-01-31 16:12:36 +00:00
if err := file . WriteAtomicWithPerms ( certFileName , [ ] byte ( ca ) , 0755 , 0666 ) ; err != nil {
2018-12-19 08:22:49 +00:00
c . UI . Error ( err . Error ( ) )
2020-01-31 16:12:36 +00:00
return 1
2018-12-19 08:22:49 +00:00
}
c . UI . Output ( "==> Saved " + certFileName )
2020-01-31 16:12:36 +00:00
2021-12-08 19:16:36 +00:00
if err := file . WriteAtomicWithPerms ( pkFileName , [ ] byte ( pk ) , 0755 , 0600 ) ; err != nil {
2018-12-19 08:22:49 +00:00
c . UI . Error ( err . Error ( ) )
2020-01-31 16:12:36 +00:00
return 1
2018-12-19 08:22:49 +00:00
}
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 consul CA"
const help = `
Usage : consul tls ca create [ options ]
Create a new consul CA :
$ consul tls ca create
== > saved consul - agent - ca . pem
== > saved consul - agent - ca - key . pem
`