Skip to content

Commit

Permalink
Generate CA & Node certificates using a config yaml file
Browse files Browse the repository at this point in the history
- Use the same argument structs used in create_ca & create_node commands in create_certs command
- Use reflection to build the argument string dynamically
  • Loading branch information
John Vincent authored and hayley-jean committed Apr 3, 2023
1 parent 93c4c27 commit 017ebc9
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 13 deletions.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,38 @@ Generating a certificate for an EventStoreDB node:
./es-gencert-cli create-node -ca-certificate ./es-ca/ca.crt -ca-key ./es-ca/ca.key -out ./node1 -ip-addresses 127.0.0.1,172.20.240.1 -dns-names localhost,eventstore-node1.localhost.com
```

Generating certificates using config file:
```
./es-gencert-cli create-certs --config-file ./certs.yml
```

An example config file:
```yaml
certificates:
ca-certs:
- out: "./root_ca"
- out: "./intermediate_ca"
cert-path: "./root_ca/ca.crt"
key-path: "./root_ca/ca.key"
days: 5
node-certs:
- out: "./node1"
ca-certificate: "./intermediate_ca/ca.crt"
ca-key: "./intermediate_ca/ca.key"
ip-addresses: "127.0.0.1,172.20.240.1"
dns-names: "localhost,eventstore-node1.localhost.com"
- out: "./node2"
ca-certificate: "./intermediate_ca/ca.crt"
ca-key: "./intermediate_ca/ca.key"
ip-addresses: "127.0.0.2,172.20.240.2"
dns-names: "localhost,eventstore-node2.localhost.com"
- out: "./node3"
ca-certificate: "./intermediate_ca/ca.crt"
ca-key: "./intermediate_ca/ca.key"
ip-addresses: "127.0.0.3,172.20.240.3"
dns-names: "localhost,eventstore-node2.localhost.com"
```
## Development
Building or working on `es-gencert-cli` requires a Go environment, version 1.14 or higher.
12 changes: 6 additions & 6 deletions certificates/create_ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ type CreateCA struct {
}

type CreateCAArguments struct {
Days int
OutputDir string
CACertificatePath string
CAKeyPath string
Days int `yaml:"days"`
OutputDir string `yaml:"out"`
CACertificatePath string `yaml:"ca-certificate"`
CAKeyPath string `yaml:"ca-key"`
}

func (c *CreateCA) Run(args []string) int {
Expand All @@ -52,7 +52,7 @@ func (c *CreateCA) Run(args []string) int {

caCertPathLen := len(config.CACertificatePath)
caKeyPathLen := len(config.CAKeyPath)
if (caCertPathLen > 0 && caKeyPathLen == 0) || (caKeyPathLen > 0 && caCertPathLen == 0){
if (caCertPathLen > 0 && caKeyPathLen == 0) || (caKeyPathLen > 0 && caCertPathLen == 0) {
multierror.Append(validationErrors, errors.New("both -ca-certificate and -ca-key options are required"))
}

Expand All @@ -69,7 +69,7 @@ func (c *CreateCA) Run(args []string) int {
days = config.Days
years = 0
}

var caCert *x509.Certificate
var caKey *rsa.PrivateKey
var err error
Expand Down
111 changes: 111 additions & 0 deletions certificates/create_certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package certificates

import (
"flag"
"fmt"
"github.com/mitchellh/cli"
"gopkg.in/yaml.v3"
"io/ioutil"
"reflect"
"strings"
)

type CreateCertificates struct {
Ui cli.Ui
}

type CreateCertificateArguments struct {
ConfigPath string
}

type Config struct {
Certificates struct {
CaCerts []CreateCAArguments `yaml:"ca-certs"`
Nodes []CreateNodeArguments `yaml:"node-certs"`
} `yaml:"certificates"`
}

func (c *CreateCertificates) Run(args []string) int {
var arguments CreateCertificateArguments
flags := flag.NewFlagSet("create_certs", flag.ContinueOnError)
flags.Usage = func() { c.Ui.Info(c.Help()) }
flags.StringVar(&arguments.ConfigPath, "config-file", "./certs.yml", "The config yml file")
if err := flags.Parse(args); err != nil {
return 1
}
configData, err := ioutil.ReadFile(arguments.ConfigPath)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
config := Config{}

if yaml.Unmarshal(configData, &config) != nil {
c.Ui.Error(err.Error())
return 1
}

if c.generateCaCerts(config) != 0 || c.generateNodes(config) != 0 {
return 1
}

return 0

}

func (c *CreateCertificates) generateNodes(config Config) int {
for _, node := range config.Certificates.Nodes {
createNode := CreateNode{
Ui: &cli.ColoredUi{
Ui: c.Ui,
OutputColor: cli.UiColorBlue,
},
}
if createNode.Run(toArguments(node)) != 0 {
return 1
}
}
return 0
}

func (c *CreateCertificates) generateCaCerts(config Config) int {
for _, ca := range config.Certificates.CaCerts {
createCa := CreateCA{
Ui: &cli.ColoredUi{
Ui: c.Ui,
OutputColor: cli.UiColorBlue,
},
}
if createCa.Run(toArguments(ca)) != 0 {
return 1
}
}
return 0
}

func toArguments(config interface{}) []string {
var args strings.Builder
fields := reflect.ValueOf(config)
for i := 0; i < fields.NumField(); i++ {
key := reflect.TypeOf(config).Field(i).Tag.Get("yaml")
value := fmt.Sprintf("%v", fields.Field(i).Interface())
if len(value) > 0 {
args.WriteString(fmt.Sprintf("-%s %s ", key, value))
}
}
return strings.Fields(args.String())
}

func (c *CreateCertificates) Help() string {
helpText := `
Usage: create_certs [options]
Generate ca and node Certificates from an yml configuration file.
Options:
-config-file the path to the yml config file
`
return strings.TrimSpace(helpText)
}

func (c *CreateCertificates) Synopsis() string {
return "Generate Certificates for both the CA and the eventstore Nodes in a single command using a config yml"
}
14 changes: 7 additions & 7 deletions certificates/create_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ type CreateNode struct {
}

type CreateNodeArguments struct {
CACertificatePath string
CAKeyPath string
IPAddresses string
DNSNames string
Days int
OutputDir string
CommonName string
CACertificatePath string `yaml:"ca-certificate"`
CAKeyPath string `yaml:"ca-key"`
IPAddresses string `yaml:"ip-addresses"`
DNSNames string `yaml:"dns-names"`
Days int `yaml:"days"`
OutputDir string `yaml:"out"`
CommonName string `yaml:"common-name"`
}

func readCertificateFromFile(path string) (*x509.Certificate, error) {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ go 1.14
require (
github.com/hashicorp/go-multierror v1.0.0
github.com/mitchellh/cli v1.1.1
gopkg.in/yaml.v3 v3.0.1 // indirect
)
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc h1:MeuS1UDyZyFH++6vVy44PuufTeFF0d0nfI6XB87YGSk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
8 changes: 8 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ func main() {
},
}, nil
},
"create-certs": func() (cli.Command, error) {
return &certificates.CreateCertificates{
Ui: &cli.ColoredUi{
Ui: ui,
OutputColor: cli.UiColorBlue,
},
}, nil
},
}
c.HelpFunc = createGeneralHelpFunc(appName, flags)

Expand Down

0 comments on commit 017ebc9

Please sign in to comment.