consul/connect/certgen/certgen.go

96 lines
2.8 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
// certgen: a tool for generating test certificates on disk for use as
// test-fixtures and for end-to-end testing and local development.
//
// Example usage:
//
// $ go run connect/certgen/certgen.go -out-dir /tmp/connect-certs
//
// You can verify a given leaf with a given root using:
//
// $ openssl verify -verbose -CAfile ca1-ca.cert.pem ca1-svc-db.cert.pem
//
// Note that to verify via the cross-signed intermediate, openssl requires it to
// be bundled with the _root_ CA bundle and will ignore the cert if it's passed
// with the subject. You can do that with:
//
// $ openssl verify -verbose -CAfile \
// <(cat ca1-ca.cert.pem ca2-xc-by-ca1.cert.pem) \
// ca2-svc-db.cert.pem
// ca2-svc-db.cert.pem: OK
//
// Note that the same leaf and root without the intermediate should fail:
//
// $ openssl verify -verbose -CAfile ca1-ca.cert.pem ca2-svc-db.cert.pem
// ca2-svc-db.cert.pem: CN = db
// error 20 at 0 depth lookup:unable to get local issuer certificate
//
// NOTE: THIS IS A QUIRK OF OPENSSL; in Connect we distribute the roots alone
// and stable intermediates like the XC cert to the _leaf_.
package main // import "github.com/hashicorp/consul/connect/certgen"
import (
"flag"
"fmt"
"log"
"os"
"github.com/mitchellh/go-testing-interface"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/structs"
)
func main() {
var numCAs = 2
var services = []string{"web", "db", "cache"}
var outDir string
var keyType string = "ec"
var keyBits int = 256
flag.StringVar(&outDir, "out-dir", "",
"REQUIRED: the dir to write certificates to")
flag.StringVar(&keyType, "key-type", "ec",
"Type of private key to create (ec, rsa)")
flag.IntVar(&keyBits, "key-bits", 256,
"Size of private key to create, in bits")
flag.Parse()
if outDir == "" {
flag.PrintDefaults()
os.Exit(1)
}
// Create CA certs
var prevCA *structs.CARoot
for i := 1; i <= numCAs; i++ {
ca := connect.TestCAWithKeyType(&testing.RuntimeT{}, prevCA, keyType, keyBits)
prefix := fmt.Sprintf("%s/ca%d-ca", outDir, i)
writeFile(prefix+".cert.pem", ca.RootCert)
writeFile(prefix+".key.pem", ca.SigningKey)
if prevCA != nil {
fname := fmt.Sprintf("%s/ca%d-xc-by-ca%d.cert.pem", outDir, i, i-1)
writeFile(fname, ca.SigningCert)
}
prevCA = ca
// Create service certs for each CA
for _, svc := range services {
certPEM, keyPEM := connect.TestLeaf(&testing.RuntimeT{}, svc, ca)
prefix := fmt.Sprintf("%s/ca%d-svc-%s", outDir, i, svc)
writeFile(prefix+".cert.pem", certPEM)
writeFile(prefix+".key.pem", keyPEM)
}
}
}
func writeFile(name, content string) {
fmt.Println("Writing ", name)
err := os.WriteFile(name, []byte(content), 0600)
if err != nil {
log.Fatalf("failed writing file: %s", err)
}
}