// 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) } }