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