Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command "nomad tls" #14296

Merged
merged 34 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3a08f4b
Add command "nomad tls"
lhaig Aug 24, 2022
ade8d20
Add tls to the commands.go file
lhaig Aug 24, 2022
6d2f7a4
Fix Some Go Critic Errors
lhaig Aug 24, 2022
f3d58f8
Initial changes as per the comments from @jrasell and @pkazmierczak
lhaig Aug 25, 2022
3945895
Multiple Updates
lhaig Aug 25, 2022
9ef49b8
Remove the flags package collision
lhaig Aug 25, 2022
6f88e75
Remove Unnecessary tabs test
lhaig Aug 25, 2022
495573a
Replace DC with region to match Nomad naming standard.
lhaig Aug 25, 2022
2e11f27
Remove an not needed check
lhaig Aug 25, 2022
1f64b66
Update Helptext to clarify tls command usage
lhaig Aug 27, 2022
a245268
Change the variables to follow convention in the code.
lhaig Aug 28, 2022
869fcb6
Remove -node as an option as there is no use case I can see for that.
lhaig Sep 14, 2022
a4be138
Update Atomic package
lhaig Sep 16, 2022
2f029ac
Update the CA and Cert test
lhaig Sep 25, 2022
be17376
Removed the possibility to have multiple files in the destination
lhaig Sep 25, 2022
7aaa7d1
Add CA and Cert info commands
lhaig Sep 25, 2022
7b14a8c
Remove ioutil requirement
lhaig Sep 26, 2022
7138fbe
Removed Unused code refactored the method added notes
lhaig Sep 28, 2022
9de9370
Update website/content/docs/commands/tls/index.mdx
lhaig Oct 1, 2022
b8ea752
Update website/content/docs/commands/tls/index.mdx
lhaig Oct 1, 2022
3d19045
Make `tls ca` more user friendly
lhaig Oct 18, 2022
8c706bb
Remove the need for the io.go file
lhaig Oct 18, 2022
699e2ef
Update tls.go
lhaig Oct 18, 2022
72ece7b
Delete atomic_test.go
lhaig Nov 14, 2022
5f78540
Update tls_cert_info.go
lhaig Nov 14, 2022
61cf003
Update tls_cert_create.go
lhaig Nov 14, 2022
cc1a8bb
Add Info Out put to the examples
lhaig Nov 14, 2022
b3f308d
Add Website Content for the tls command
lhaig Nov 14, 2022
c00c1e9
Update tls_cert_create_test.go
lhaig Nov 14, 2022
7fb317e
Create 14296.txt
lhaig Nov 14, 2022
ce5da2b
Spelling grammar and style updates
lhaig Nov 18, 2022
8267b0c
Remove unnecessary function.
lhaig Nov 18, 2022
b3af8a2
docs tweaks around spacing, consistency
tgross Nov 18, 2022
1f45b1a
fixup docs nav
tgross Nov 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changelog/14296.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:improvement
cli: Added tls command to enable creating Certificate Authority and Self signed TLS certificates.
There are two sub commands `tls ca` and `tls cert` that are helpers when creating certificates.
```
35 changes: 35 additions & 0 deletions command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,41 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
Meta: meta,
}, nil
},
"tls": func() (cli.Command, error) {
return &TLSCommand{
Meta: meta,
}, nil
},
"tls ca": func() (cli.Command, error) {
return &TLSCACommand{
Meta: meta,
}, nil
},
"tls ca create": func() (cli.Command, error) {
return &TLSCACreateCommand{
Meta: meta,
}, nil
},
"tls ca info": func() (cli.Command, error) {
return &TLSCAInfoCommand{
Meta: meta,
}, nil
},
"tls cert": func() (cli.Command, error) {
return &TLSCertCommand{
Meta: meta,
}, nil
},
"tls cert create": func() (cli.Command, error) {
return &TLSCertCreateCommand{
Meta: meta,
}, nil
},
"tls cert info": func() (cli.Command, error) {
return &TLSCertInfoCommand{
Meta: meta,
}, nil
},
"ui": func() (cli.Command, error) {
return &UiCommand{
Meta: meta,
Expand Down
58 changes: 58 additions & 0 deletions command/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package command

import (
"os"
"strings"

"github.com/mitchellh/cli"
)

type TLSCommand struct {
Meta
}

func fileDoesNotExist(file string) bool {
if _, err := os.Stat(file); os.IsNotExist(err) {
return true
}
return false
}

func (c *TLSCommand) Help() string {
helpText := `
Usage: nomad tls <subcommand> <subcommand> [options]

This command groups subcommands for creating certificates for Nomad TLS configuration.
The TLS command allows operators to generate self signed certificates to use
lhaig marked this conversation as resolved.
Show resolved Hide resolved
when securing your Nomad cluster.

Some simple examples for creating certificates can be found here.
More detailed examples are available in the subcommands or the documentation.

Create a CA

$ nomad tls ca create

Create a server certificate

$ nomad tls cert create -server

Create a client certificate

$ nomad tls cert create -client

For more examples, ask for subcommand help or view the documentation.
lhaig marked this conversation as resolved.
Show resolved Hide resolved

`
return strings.TrimSpace(helpText)
}

func (c *TLSCommand) Synopsis() string {
return "Generate Self Signed TLS Certificates for Nomad"
}

func (c *TLSCommand) Name() string { return "tls" }

func (c *TLSCommand) Run(_ []string) int {
return cli.RunResultHelp
}
53 changes: 53 additions & 0 deletions command/tls_ca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package command

import (
"strings"

"github.com/mitchellh/cli"
"github.com/posener/complete"
)

type TLSCACommand struct {
Meta
}

func (c *TLSCACommand) Help() string {
helpText := `
Usage: nomad tls ca <subcommand> [options]

This command has subcommands for interacting with Certificate Authorities.
lhaig marked this conversation as resolved.
Show resolved Hide resolved

Here is a simple example, and more detailed examples are available
in the subcommands or the documentation.

Create a CA

$ nomad tls ca create
==> CA Certificate saved to: nomad-agent-ca.pem
==> CA Certificate key saved to: nomad-agent-ca-key.pem
$ nomad tls ca info nomad-agent-ca.pem
nomad-agent-ca.pem
Issuer CN Nomad Agent CA 58896012363767591697986789371079092261
Common Name CN=Nomad Agent CA 58896012363767591697986789371079092261,O=HashiCorp Inc.,...
Expiry Date 2027-09-24 22:24:08 +0000 UTC
Permitted DNS Domains []

For more examples, ask for subcommand help or view the documentation.
lhaig marked this conversation as resolved.
Show resolved Hide resolved

`
return strings.TrimSpace(helpText)
}

func (c *TLSCACommand) AutocompleteArgs() complete.Predictor {
return complete.PredictNothing
}

func (c *TLSCACommand) Synopsis() string {
return "Helpers for creating CAs"
}

func (c *TLSCACommand) Name() string { return "tls ca" }

func (c *TLSCACommand) Run(_ []string) int {
return cli.RunResultHelp
}
175 changes: 175 additions & 0 deletions command/tls_ca_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package command

import (
"fmt"
"strings"

"github.com/hashicorp/nomad/helper/flags"
"github.com/hashicorp/nomad/helper/tlsutil"
"github.com/hashicorp/nomad/lib/file"
"github.com/posener/complete"
)

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 -addtitiona-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.

Here are some simple examples, more details can be found below or in
the documentation.

Create a new Nomad CA

$ nomad tls ca create
==> CA Certificate saved to: nomad-agent-ca.pem
==> CA Certificate key saved to: nomad-agent-ca-key.pem

Create a new Nomad CA with a Custiom Domain
lhaig marked this conversation as resolved.
Show resolved Hide resolved

$ nomad tls ca create -domain foo
==> CA Certificate saved to: foo-agent-ca.pem
==> CA Certificate key saved to: foo-agent-ca-key.pem


CA Create Options:
-additional-domain
Add additional DNS zones to the allowed list for the CA. Results in rejecting certificates
for other DNS zone than the ones 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..
lhaig marked this conversation as resolved.
Show resolved Hide resolved
lhaig marked this conversation as resolved.
Show resolved Hide resolved

-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 DNS has to be added with -additional-domain. It is not
possible to add that after the fact! 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))
lhaig marked this conversation as resolved.
Show resolved Hide resolved
return 1
}
if !(fileDoesNotExist(pkFileName)) {
c.Ui.Error(fmt.Sprintf("CA Key File '%s' already exists", pkFileName))
lhaig marked this conversation as resolved.
Show resolved Hide resolved
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)
lhaig marked this conversation as resolved.
Show resolved Hide resolved

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)
lhaig marked this conversation as resolved.
Show resolved Hide resolved

return 0
}
Loading