diff --git a/common/tools/cryptogen/ca/ca_test.go b/common/tools/cryptogen/ca/ca_test.go index 8d2b84f8578..9e098abce33 100644 --- a/common/tools/cryptogen/ca/ca_test.go +++ b/common/tools/cryptogen/ca/ca_test.go @@ -71,7 +71,7 @@ func TestGenerateSignCertificate(t *testing.T) { rootCA, err := ca.NewCA(caDir, testCA2Name, testCA2Name) assert.NoError(t, err, "Error generating CA") - _, err = rootCA.SignCertificate(certDir, testName, ecPubKey) + _, err = rootCA.SignCertificate(certDir, testName, nil, ecPubKey) assert.NoError(t, err, "Failed to generate signed certificate") // check to make sure the signed public key was stored @@ -79,7 +79,7 @@ func TestGenerateSignCertificate(t *testing.T) { assert.Equal(t, true, checkForFile(pemFile), "Expected to find file "+pemFile) - _, err = rootCA.SignCertificate(certDir, "empty/CA", ecPubKey) + _, err = rootCA.SignCertificate(certDir, "empty/CA", nil, ecPubKey) assert.Error(t, err, "Bad name should fail") // use an empty CA to test error path @@ -87,7 +87,7 @@ func TestGenerateSignCertificate(t *testing.T) { Name: "badCA", SignCert: &x509.Certificate{}, } - _, err = badCA.SignCertificate(certDir, testName, &ecdsa.PublicKey{}) + _, err = badCA.SignCertificate(certDir, testName, nil, &ecdsa.PublicKey{}) assert.Error(t, err, "Empty CA should not be able to sign") cleanup(testDir) diff --git a/common/tools/cryptogen/ca/generator.go b/common/tools/cryptogen/ca/generator.go index 3d991d627b0..f49e8f8aec3 100644 --- a/common/tools/cryptogen/ca/generator.go +++ b/common/tools/cryptogen/ca/generator.go @@ -86,7 +86,7 @@ func NewCA(baseDir, org, name string) (*CA, error) { // SignCertificate creates a signed certificate based on a built-in template // and saves it in baseDir/name -func (ca *CA) SignCertificate(baseDir, name string, pub *ecdsa.PublicKey) (*x509.Certificate, error) { +func (ca *CA) SignCertificate(baseDir, name string, sans []string, pub *ecdsa.PublicKey) (*x509.Certificate, error) { template := x509Template() template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} @@ -96,6 +96,7 @@ func (ca *CA) SignCertificate(baseDir, name string, pub *ecdsa.PublicKey) (*x509 subject.CommonName = name template.Subject = subject + template.DNSNames = sans cert, err := genCertificateECDSA(baseDir, name, &template, ca.SignCert, pub, ca.Signer) diff --git a/common/tools/cryptogen/main.go b/common/tools/cryptogen/main.go index 75d1308f345..89a8b8d4d37 100644 --- a/common/tools/cryptogen/main.go +++ b/common/tools/cryptogen/main.go @@ -46,21 +46,23 @@ type HostnameData struct { Domain string } -type CommonNameData struct { - Hostname string - Domain string +type SpecData struct { + Hostname string + Domain string + CommonName string } type NodeTemplate struct { - Count int `yaml:"Count"` - Start int `yaml:"Start"` - Hostname string `yaml:"Hostname"` + Count int `yaml:"Count"` + Start int `yaml:"Start"` + Hostname string `yaml:"Hostname"` + SANS []string `yaml:"SANS"` } type NodeSpec struct { - Hostname string `yaml:"Hostname"` - AltHostnames []string `yaml:"AltHostnames"` - CommonName string `yaml:"CommonName"` + Hostname string `yaml:"Hostname"` + CommonName string `yaml:"CommonName"` + SANS []string `yaml:"SANS"` } type UsersSpec struct { @@ -132,10 +134,20 @@ PeerOrgs: # # which obtains its values from the Spec.Hostname and # Org.Domain, respectively. + # - SANS: (Optional) Specifies one or more Subject Alternative Names + # the be set in the resulting x509. Accepts template + # variables {{.Hostname}}, {{.Domain}}, {{.CommonName}} + # NOTE: Two implicit entries are created for you: + # - {{ .CommonName }} + # - {{ .Hostname }} # --------------------------------------------------------------------------- # Specs: # - Hostname: foo # implicitly "foo.org1.example.com" # CommonName: foo27.org5.example.com # overrides Hostname-based FQDN set above + # SANS: + # - "bar.{{.Domain}}" + # - "altfoo.{{.Domain}}" + # - "{{.Hostname}}.org6.net" # - Hostname: bar # - Hostname: baz @@ -155,6 +167,8 @@ PeerOrgs: Count: 1 # Start: 5 # Hostname: {{.Prefix}}{{.Index}} # default + # SANS: + # - "{{.Hostname}}.alt.{{.Domain}}" # --------------------------------------------------------------------------- # "Users" @@ -234,7 +248,7 @@ func generate() { } for _, orgSpec := range config.PeerOrgs { - err = generateNodeSpec(&orgSpec, "peer") + err = renderOrgSpec(&orgSpec, "peer") if err != nil { fmt.Printf("Error processing peer configuration: %s", err) os.Exit(-1) @@ -243,7 +257,7 @@ func generate() { } for _, orgSpec := range config.OrdererOrgs { - generateNodeSpec(&orgSpec, "orderer") + renderOrgSpec(&orgSpec, "orderer") if err != nil { fmt.Printf("Error processing orderer configuration: %s", err) os.Exit(-1) @@ -252,12 +266,7 @@ func generate() { } } -func parseTemplate(input, defaultInput string, data interface{}) (string, error) { - - // Use the default if the input is an empty string - if len(input) == 0 { - input = defaultInput - } +func parseTemplate(input string, data interface{}) (string, error) { t, err := template.New("parse").Parse(input) if err != nil { @@ -273,16 +282,51 @@ func parseTemplate(input, defaultInput string, data interface{}) (string, error) return output.String(), nil } -func renderCN(domain string, spec NodeSpec) (string, error) { - data := CommonNameData{ +func parseTemplateWithDefault(input, defaultInput string, data interface{}) (string, error) { + + // Use the default if the input is an empty string + if len(input) == 0 { + input = defaultInput + } + + return parseTemplate(input, data) +} + +func renderNodeSpec(domain string, spec *NodeSpec) error { + data := SpecData{ Hostname: spec.Hostname, Domain: domain, } - return parseTemplate(spec.CommonName, defaultCNTemplate, data) + // Process our CommonName + cn, err := parseTemplateWithDefault(spec.CommonName, defaultCNTemplate, data) + if err != nil { + return err + } + + spec.CommonName = cn + data.CommonName = cn + + // Save off our original, unprocessed SANS entries + origSANS := spec.SANS + + // Set our implicit SANS entries for CN/Hostname + spec.SANS = []string{cn, spec.Hostname} + + // Finally, process any remaining SANS entries + for _, _san := range origSANS { + san, err := parseTemplate(_san, data) + if err != nil { + return err + } + + spec.SANS = append(spec.SANS, san) + } + + return nil } -func generateNodeSpec(orgSpec *OrgSpec, prefix string) error { +func renderOrgSpec(orgSpec *OrgSpec, prefix string) error { // First process all of our templated nodes for i := 0; i < orgSpec.Template.Count; i++ { data := HostnameData{ @@ -291,34 +335,36 @@ func generateNodeSpec(orgSpec *OrgSpec, prefix string) error { Domain: orgSpec.Domain, } - hostname, err := parseTemplate(orgSpec.Template.Hostname, defaultHostnameTemplate, data) + hostname, err := parseTemplateWithDefault(orgSpec.Template.Hostname, defaultHostnameTemplate, data) if err != nil { return err } - spec := NodeSpec{Hostname: hostname} + spec := NodeSpec{ + Hostname: hostname, + SANS: orgSpec.Template.SANS, + } orgSpec.Specs = append(orgSpec.Specs, spec) } // Touch up all general node-specs to add the domain for idx, spec := range orgSpec.Specs { - finalCN, err := renderCN(orgSpec.Domain, spec) + err := renderNodeSpec(orgSpec.Domain, &spec) if err != nil { return err } - orgSpec.Specs[idx].CommonName = finalCN + orgSpec.Specs[idx] = spec } // Process the CA node-spec in the same manner if len(orgSpec.CA.Hostname) == 0 { orgSpec.CA.Hostname = "ca" } - finalCN, err := renderCN(orgSpec.Domain, orgSpec.CA) + err := renderNodeSpec(orgSpec.Domain, &orgSpec.CA) if err != nil { return err } - orgSpec.CA.CommonName = finalCN return nil } @@ -346,27 +392,27 @@ func generatePeerOrg(baseDir string, orgSpec OrgSpec) { os.Exit(1) } - peerNames := []string{} - for _, spec := range orgSpec.Specs { - peerNames = append(peerNames, spec.CommonName) - } - generateNodes(peersDir, peerNames, rootCA) + generateNodes(peersDir, orgSpec.Specs, rootCA) // TODO: add ability to specify usernames - usernames := []string{} + users := []NodeSpec{} for j := 1; j <= orgSpec.Users.Count; j++ { - usernames = append(usernames, fmt.Sprintf("%s%d@%s", - userBaseName, j, orgName)) + user := NodeSpec{ + CommonName: fmt.Sprintf("%s%d@%s", userBaseName, j, orgName), + } + + users = append(users, user) } // add an admin user - adminUserName := fmt.Sprintf("%s@%s", - adminBaseName, orgName) + adminUser := NodeSpec{ + CommonName: fmt.Sprintf("%s@%s", adminBaseName, orgName), + } - usernames = append(usernames, adminUserName) - generateNodes(usersDir, usernames, rootCA) + users = append(users, adminUser) + generateNodes(usersDir, users, rootCA) // copy the admin cert to the org's MSP admincerts - err = copyAdminCert(usersDir, adminCertsDir, adminUserName) + err = copyAdminCert(usersDir, adminCertsDir, adminUser.CommonName) if err != nil { fmt.Printf("Error copying admin cert for org %s:\n%v\n", orgName, err) @@ -374,13 +420,12 @@ func generatePeerOrg(baseDir string, orgSpec OrgSpec) { } // copy the admin cert to each of the org's peer's MSP admincerts - for _, peerName := range peerNames { + for _, spec := range orgSpec.Specs { err = copyAdminCert(usersDir, - filepath.Join(peersDir, peerName, "msp", "admincerts"), - adminUserName) + filepath.Join(peersDir, spec.CommonName, "msp", "admincerts"), adminUser.CommonName) if err != nil { fmt.Printf("Error copying admin cert for org %s peer %s:\n%v\n", - orgName, peerName, err) + orgName, spec.CommonName, err) os.Exit(1) } } @@ -407,17 +452,16 @@ func copyAdminCert(usersDir, adminCertsDir, adminUserName string) error { } -func generateNodes(baseDir string, nodeNames []string, rootCA *ca.CA) { +func generateNodes(baseDir string, nodes []NodeSpec, rootCA *ca.CA) { - for _, nodeName := range nodeNames { - nodeDir := filepath.Join(baseDir, nodeName) - err := msp.GenerateLocalMSP(nodeDir, nodeName, rootCA) + for _, node := range nodes { + nodeDir := filepath.Join(baseDir, node.CommonName) + err := msp.GenerateLocalMSP(nodeDir, node.CommonName, node.SANS, rootCA) if err != nil { - fmt.Printf("Error generating local MSP for %s:\n%v\n", nodeName, err) + fmt.Printf("Error generating local MSP for %s:\n%v\n", node, err) os.Exit(1) } } - } func generateOrdererOrg(baseDir string, orgSpec OrgSpec) { @@ -442,25 +486,20 @@ func generateOrdererOrg(baseDir string, orgSpec OrgSpec) { os.Exit(1) } - // TODO: add ability to specify orderer names - // for name just use default base name - ordererNames := []string{} - for _, spec := range orgSpec.Specs { - ordererNames = append(ordererNames, spec.CommonName) - } - generateNodes(orderersDir, ordererNames, rootCA) + generateNodes(orderersDir, orgSpec.Specs, rootCA) - adminUserName := fmt.Sprintf("%s@%s", - adminBaseName, orgName) + adminUser := NodeSpec{ + CommonName: fmt.Sprintf("%s@%s", adminBaseName, orgName), + } // generate an admin for the orderer org - usernames := []string{} + users := []NodeSpec{} // add an admin user - usernames = append(usernames, adminUserName) - generateNodes(usersDir, usernames, rootCA) + users = append(users, adminUser) + generateNodes(usersDir, users, rootCA) // copy the admin cert to the org's MSP admincerts - err = copyAdminCert(usersDir, adminCertsDir, adminUserName) + err = copyAdminCert(usersDir, adminCertsDir, adminUser.CommonName) if err != nil { fmt.Printf("Error copying admin cert for org %s:\n%v\n", orgName, err) @@ -468,12 +507,12 @@ func generateOrdererOrg(baseDir string, orgSpec OrgSpec) { } // copy the admin cert to each of the org's orderers's MSP admincerts - for _, ordererName := range ordererNames { + for _, spec := range orgSpec.Specs { err = copyAdminCert(usersDir, - filepath.Join(orderersDir, ordererName, "msp", "admincerts"), adminUserName) + filepath.Join(orderersDir, spec.CommonName, "msp", "admincerts"), adminUser.CommonName) if err != nil { fmt.Printf("Error copying admin cert for org %s orderer %s:\n%v\n", - orgName, ordererName, err) + orgName, spec.CommonName, err) os.Exit(1) } } diff --git a/common/tools/cryptogen/msp/generator.go b/common/tools/cryptogen/msp/generator.go index d956bbe138f..33c77b2228a 100644 --- a/common/tools/cryptogen/msp/generator.go +++ b/common/tools/cryptogen/msp/generator.go @@ -29,7 +29,7 @@ import ( "github.com/hyperledger/fabric/common/tools/cryptogen/csp" ) -func GenerateLocalMSP(baseDir, name string, rootCA *ca.CA) error { +func GenerateLocalMSP(baseDir, name string, sans []string, rootCA *ca.CA) error { // create folder structure mspDir := filepath.Join(baseDir, "msp") @@ -60,7 +60,7 @@ func GenerateLocalMSP(baseDir, name string, rootCA *ca.CA) error { return err } - cert, err := rootCA.SignCertificate(filepath.Join(mspDir, "signcerts"), name, ecPubKey) + cert, err := rootCA.SignCertificate(filepath.Join(mspDir, "signcerts"), name, sans, ecPubKey) if err != nil { return err } diff --git a/common/tools/cryptogen/msp/msp_test.go b/common/tools/cryptogen/msp/msp_test.go index fb6d1d59b7e..d5dab4053b1 100644 --- a/common/tools/cryptogen/msp/msp_test.go +++ b/common/tools/cryptogen/msp/msp_test.go @@ -38,14 +38,14 @@ func TestGenerateLocalMSP(t *testing.T) { cleanup(testDir) - err := msp.GenerateLocalMSP(testDir, testName, &ca.CA{}) + err := msp.GenerateLocalMSP(testDir, testName, nil, &ca.CA{}) assert.Error(t, err, "Empty CA should have failed") caDir := filepath.Join(testDir, "ca") mspDir := filepath.Join(testDir, "msp") rootCA, err := ca.NewCA(caDir, testCAOrg, testCAName) assert.NoError(t, err, "Error generating CA") - err = msp.GenerateLocalMSP(testDir, testName, rootCA) + err = msp.GenerateLocalMSP(testDir, testName, nil, rootCA) assert.NoError(t, err, "Failed to generate local MSP") // check to see that the right files were generated/saved @@ -70,7 +70,7 @@ func TestGenerateLocalMSP(t *testing.T) { assert.NoError(t, err, "Error setting up local MSP") rootCA.Name = "test/fail" - err = msp.GenerateLocalMSP(testDir, testName, rootCA) + err = msp.GenerateLocalMSP(testDir, testName, nil, rootCA) assert.Error(t, err, "Should have failed with CA name 'test/fail'") t.Log(err) cleanup(testDir)