166 lines
4.9 KiB
Go
166 lines
4.9 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/posener/complete"
|
|
|
|
"github.com/hashicorp/nomad/helper/flags"
|
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
|
"github.com/hashicorp/nomad/lib/file"
|
|
)
|
|
|
|
type TLSCACreateCommand struct {
|
|
Meta
|
|
|
|
// days is the number of days the CA will be valid for
|
|
days int
|
|
|
|
// constraint boolean enables the name constraint option in the CA which
|
|
// will then reject any domains other than the ones stiputalted in -domain
|
|
// and -addtitional-domain.
|
|
constraint bool
|
|
|
|
// domain is used to provide a custom domain for the CA
|
|
domain string
|
|
|
|
// commonName is used to set a common name for the CA
|
|
commonName string
|
|
|
|
// additionalDomain provides a list of restricted domains to the CA which
|
|
// will then reject any domains other than these.
|
|
additionalDomain flags.StringFlag
|
|
}
|
|
|
|
func (c *TLSCACreateCommand) Help() string {
|
|
helpText := `
|
|
Usage: nomad tls ca create [options]
|
|
|
|
Create a new certificate authority.
|
|
|
|
CA Create Options:
|
|
|
|
-additional-domain
|
|
Add additional DNS zones to the allowed list for the CA. The server will
|
|
reject certificates for DNS names other than those specified in -domain and
|
|
-additional-domain. This flag can be used multiple times. Only used in
|
|
combination with -domain and -name-constraint.
|
|
|
|
-common-name
|
|
Common Name of CA. Defaults to "Nomad Agent CA".
|
|
|
|
-days
|
|
Provide number of days the CA is valid for from now on.
|
|
Defaults to 5 years or 1825 days.
|
|
|
|
-domain
|
|
Domain of Nomad cluster. Only used in combination with -name-constraint.
|
|
Defaults to "nomad".
|
|
|
|
-name-constraint
|
|
Enables the DNS name restriction functionality to the CA. Results in the CA
|
|
rejecting certificates for any other DNS zone. If enabled, localhost and the
|
|
value of -domain will be added to the allowed DNS zones field. If the UI is
|
|
going to be served over HTTPS its hostname must be added with
|
|
-additional-domain. Defaults to false.
|
|
`
|
|
return strings.TrimSpace(helpText)
|
|
}
|
|
|
|
func (c *TLSCACreateCommand) AutocompleteFlags() complete.Flags {
|
|
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
|
|
complete.Flags{
|
|
"-additional-domain": complete.PredictAnything,
|
|
"-common-name": complete.PredictAnything,
|
|
"-days": complete.PredictAnything,
|
|
"-domain": complete.PredictAnything,
|
|
"-name-constraint": complete.PredictAnything,
|
|
})
|
|
}
|
|
|
|
func (c *TLSCACreateCommand) AutocompleteArgs() complete.Predictor {
|
|
return complete.PredictNothing
|
|
}
|
|
|
|
func (c *TLSCACreateCommand) Synopsis() string {
|
|
return "Create a certificate authority for Nomad"
|
|
}
|
|
|
|
func (c *TLSCACreateCommand) Name() string { return "tls ca create" }
|
|
|
|
func (c *TLSCACreateCommand) Run(args []string) int {
|
|
|
|
flagSet := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
|
flagSet.Usage = func() { c.Ui.Output(c.Help()) }
|
|
flagSet.Var(&c.additionalDomain, "additional-domain", "")
|
|
flagSet.IntVar(&c.days, "days", 1825, "")
|
|
flagSet.BoolVar(&c.constraint, "name-constraint", false, "")
|
|
flagSet.StringVar(&c.domain, "domain", "nomad", "")
|
|
flagSet.StringVar(&c.commonName, "common-name", "", "")
|
|
if err := flagSet.Parse(args); err != nil {
|
|
return 1
|
|
}
|
|
|
|
// Check that we got no arguments
|
|
args = flagSet.Args()
|
|
if l := len(args); l < 0 || l > 1 {
|
|
c.Ui.Error("This command takes up to one argument")
|
|
c.Ui.Error(commandErrorText(c))
|
|
return 1
|
|
}
|
|
if c.domain != "" && c.domain != "nomad" && !c.constraint {
|
|
c.Ui.Error("Please provide the -name-constraint flag to use a custom domain constraint")
|
|
return 1
|
|
}
|
|
if c.domain == "nomad" && c.constraint {
|
|
c.Ui.Error("Please provide the -domain flag if you want to enable custom domain constraints")
|
|
return 1
|
|
}
|
|
if c.additionalDomain != nil && c.domain == "" && !c.constraint {
|
|
c.Ui.Error("Please provide the -name-constraint flag to use a custom domain constraints")
|
|
return 1
|
|
}
|
|
|
|
certFileName := fmt.Sprintf("%s-agent-ca.pem", c.domain)
|
|
pkFileName := fmt.Sprintf("%s-agent-ca-key.pem", c.domain)
|
|
|
|
if !(fileDoesNotExist(certFileName)) {
|
|
c.Ui.Error(fmt.Sprintf("CA certificate file '%s' already exists", certFileName))
|
|
return 1
|
|
}
|
|
if !(fileDoesNotExist(pkFileName)) {
|
|
c.Ui.Error(fmt.Sprintf("CA key file '%s' already exists", pkFileName))
|
|
return 1
|
|
}
|
|
|
|
constraints := []string{}
|
|
if c.constraint {
|
|
constraints = []string{c.domain, "localhost"}
|
|
constraints = append(constraints, c.additionalDomain...)
|
|
}
|
|
|
|
ca, pk, err := tlsutil.GenerateCA(tlsutil.CAOpts{Name: c.commonName, Days: c.days, Domain: c.domain, PermittedDNSDomains: constraints})
|
|
if err != nil {
|
|
c.Ui.Error(err.Error())
|
|
return 1
|
|
}
|
|
|
|
if err := file.WriteAtomicWithPerms(certFileName, []byte(ca), 0755, 0666); err != nil {
|
|
c.Ui.Error(err.Error())
|
|
return 1
|
|
}
|
|
c.Ui.Output("==> CA certificate saved to: " + certFileName)
|
|
|
|
if err := file.WriteAtomicWithPerms(pkFileName, []byte(pk), 0755, 0600); err != nil {
|
|
c.Ui.Error(err.Error())
|
|
return 1
|
|
}
|
|
c.Ui.Output("==> CA certificate key saved to: " + pkFileName)
|
|
|
|
return 0
|
|
}
|