|
|
package main
|
|
|
|
|
|
import (
|
|
|
"bufio"
|
|
|
"encoding/json"
|
|
|
"flag"
|
|
|
"log"
|
|
|
"math"
|
|
|
"os"
|
|
|
"os/exec"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
)
|
|
|
|
|
|
type CA struct {
|
|
|
Name string `json:"name"`
|
|
|
Duration float64 `json:"duration,omitempty"` //in days
|
|
|
}
|
|
|
|
|
|
type Host struct {
|
|
|
Hostname string `json:"hostname"`
|
|
|
IP string `json:"ip"` //with CIDR network suffix
|
|
|
Groups []string `json:"groups,omitempty"`
|
|
|
Duration float64 `json:"duration,omitempty"` //in days
|
|
|
}
|
|
|
|
|
|
type Network struct {
|
|
|
CA CA `json:"ca"`
|
|
|
Hosts []Host `json:"hosts"`
|
|
|
}
|
|
|
|
|
|
func main() {
|
|
|
var err error
|
|
|
l := log.New(os.Stderr, "", 0) //set logging to standard error and no timestamp
|
|
|
|
|
|
//commandline options
|
|
|
caCertFile := flag.String("c", "./ca.crt", "CA certificate path.")
|
|
|
caKeyFile := flag.String("k", "./ca.key", "CA key path.")
|
|
|
binaryPath := flag.String("p", "", "Path to nebula-cert binary file. If not specified, search $PATH and current directory.")
|
|
|
networkFile := flag.String("f", "-", "Path to network input file. Use '-' for standard input.")
|
|
|
overwrite := flag.Bool("o", false, "Overwrite existing files.")
|
|
|
flag.Parse()
|
|
|
|
|
|
//Locate binary
|
|
|
pathFailText := "Executable not found in $PATH or current directory. Specify the path with the '-p' option"
|
|
|
if *binaryPath == "" {
|
|
|
*binaryPath, err = exec.LookPath("nebula-cert")
|
|
|
if err != nil {
|
|
|
*binaryPath = "./nebula-cert"
|
|
|
}
|
|
|
}
|
|
|
if _, err := os.Stat(*binaryPath); os.IsNotExist(err) { //check if file exists...
|
|
|
if *binaryPath != "./nebula-cert" {
|
|
|
pathFailText = "Executable not found at " + *binaryPath
|
|
|
}
|
|
|
l.Fatal(pathFailText)
|
|
|
}
|
|
|
|
|
|
var inputFile *os.File
|
|
|
if *networkFile == "-" { //read input from stdin
|
|
|
inputFile = os.Stdin
|
|
|
} else { //read input from file
|
|
|
l.Println("Processing network description file: " + *networkFile)
|
|
|
inputFile, err = os.Open(*networkFile)
|
|
|
if err != nil {
|
|
|
l.Fatal("Could not open network description file: " + *networkFile + "\n" + err.Error())
|
|
|
}
|
|
|
defer inputFile.Close()
|
|
|
}
|
|
|
|
|
|
var input string
|
|
|
scanner := bufio.NewScanner(inputFile)
|
|
|
for scanner.Scan() {
|
|
|
input = input + scanner.Text()
|
|
|
}
|
|
|
|
|
|
var network Network
|
|
|
|
|
|
err = json.Unmarshal([]byte(input), &network) //read the network config
|
|
|
if err != nil {
|
|
|
if *networkFile == "-" {
|
|
|
*networkFile = "standard input."
|
|
|
}
|
|
|
l.Fatal("Could not parse network description from " + *networkFile + "\nError: " + err.Error())
|
|
|
}
|
|
|
|
|
|
var cmd *exec.Cmd
|
|
|
|
|
|
//Create CA if name is specified, AND existing cert doesn't already exist OR overwrite is true.
|
|
|
if len(network.CA.Name) > 0 {
|
|
|
if _, err := os.Stat(*caCertFile); os.IsNotExist(err) || *overwrite {
|
|
|
duration := "8760h" //default 1 year
|
|
|
if network.CA.Duration > 0 {
|
|
|
duration = strconv.Itoa(int(math.Round(network.CA.Duration*24))) + "h" //convert days to hours
|
|
|
}
|
|
|
cmd := exec.Command(*binaryPath, "ca", "-out-crt", *caCertFile, "-out-key", *caKeyFile, "-name", network.CA.Name, "-duration", duration)
|
|
|
output, err := cmd.CombinedOutput()
|
|
|
if err != nil {
|
|
|
l.Fatal("CA: " + string(output) + " Error: " + err.Error())
|
|
|
}
|
|
|
l.Println("Created CA '" + network.CA.Name + "' OK " + string(output))
|
|
|
} else {
|
|
|
l.Println("CA certificate '" + *caCertFile + "' already exists. Skipping...")
|
|
|
}
|
|
|
}
|
|
|
|
|
|
for _, h := range network.Hosts {
|
|
|
if _, err := os.Stat(h.Hostname + ".crt"); err == nil && !*overwrite { //check if host certificate file exists and overwrite not true
|
|
|
l.Println(h.Hostname + " certificate already exists. Skipping...")
|
|
|
continue
|
|
|
}
|
|
|
groups := strings.Join(h.Groups, ",")
|
|
|
if h.Duration > 0 {
|
|
|
duration := strconv.Itoa(int(math.Round(h.Duration*24))) + "h"
|
|
|
cmd = exec.Command(*binaryPath, "sign", "-ca-crt", *caCertFile, "-ca-key", *caKeyFile, "-duration", duration, "-name", h.Hostname, "-ip", h.IP, "-groups", groups)
|
|
|
} else {
|
|
|
cmd = exec.Command(*binaryPath, "sign", "-ca-crt", *caCertFile, "-ca-key", *caKeyFile, "-name", h.Hostname, "-ip", h.IP, "-groups", groups)
|
|
|
}
|
|
|
output, err := cmd.CombinedOutput()
|
|
|
if err != nil {
|
|
|
l.Fatal("Host: " + h.Hostname + " " + string(output) + " Error: " + err.Error())
|
|
|
}
|
|
|
l.Println(h.Hostname + " OK " + string(output))
|
|
|
}
|
|
|
|
|
|
}
|