main.go 16 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
Copyright IBM Corp. 2017 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main

import (
	"fmt"
Gari Singh's avatar
Gari Singh committed
20
	"io"
21
22
	"os"
	"path/filepath"
23
24
25
26
27
28
29
30
	"text/template"

	"gopkg.in/yaml.v2"

	"gopkg.in/alecthomas/kingpin.v2"

	"bytes"
	"io/ioutil"
31
32

	"github.com/hyperledger/fabric/common/tools/cryptogen/ca"
33
	"github.com/hyperledger/fabric/common/tools/cryptogen/metadata"
34
35
36
37
	"github.com/hyperledger/fabric/common/tools/cryptogen/msp"
)

const (
38
39
40
41
	userBaseName            = "User"
	adminBaseName           = "Admin"
	defaultHostnameTemplate = "{{.Prefix}}{{.Index}}"
	defaultCNTemplate       = "{{.Hostname}}.{{.Domain}}"
42
43
)

44
45
46
47
48
49
type HostnameData struct {
	Prefix string
	Index  int
	Domain string
}

50
51
52
53
type SpecData struct {
	Hostname   string
	Domain     string
	CommonName string
54
55
56
}

type NodeTemplate struct {
57
58
59
60
	Count    int      `yaml:"Count"`
	Start    int      `yaml:"Start"`
	Hostname string   `yaml:"Hostname"`
	SANS     []string `yaml:"SANS"`
61
62
63
}

type NodeSpec struct {
64
65
66
	Hostname   string   `yaml:"Hostname"`
	CommonName string   `yaml:"CommonName"`
	SANS       []string `yaml:"SANS"`
67
68
69
70
71
72
73
74
75
}

type UsersSpec struct {
	Count int `yaml:"Count"`
}

type OrgSpec struct {
	Name     string       `yaml:"Name"`
	Domain   string       `yaml:"Domain"`
76
	CA       NodeSpec     `yaml:"CA"`
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
	Template NodeTemplate `yaml:"Template"`
	Specs    []NodeSpec   `yaml:"Specs"`
	Users    UsersSpec    `yaml:"Users"`
}

type Config struct {
	OrdererOrgs []OrgSpec `yaml:"OrdererOrgs"`
	PeerOrgs    []OrgSpec `yaml:"PeerOrgs"`
}

var defaultConfig = `
# ---------------------------------------------------------------------------
# "OrdererOrgs" - Definition of organizations managing orderer nodes
# ---------------------------------------------------------------------------
OrdererOrgs:
  # ---------------------------------------------------------------------------
  # Orderer
  # ---------------------------------------------------------------------------
  - Name: Orderer
    Domain: example.com

    # ---------------------------------------------------------------------------
    # "Specs" - See PeerOrgs below for complete description
    # ---------------------------------------------------------------------------
    Specs:
      - Hostname: orderer

# ---------------------------------------------------------------------------
# "PeerOrgs" - Definition of organizations managing peer nodes
# ---------------------------------------------------------------------------
PeerOrgs:
  # ---------------------------------------------------------------------------
  # Org1
  # ---------------------------------------------------------------------------
  - Name: Org1
    Domain: org1.example.com

114
115
116
117
118
119
120
121
122
    # ---------------------------------------------------------------------------
    # "CA"
    # ---------------------------------------------------------------------------
    # Uncomment this section to enable the explicit definition of the CA for this
    # organization.  This entry is a Spec.  See "Specs" section below for details.
    # ---------------------------------------------------------------------------
    # CA:
    #    Hostname: ca # implicitly ca.org1.example.com

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    # ---------------------------------------------------------------------------
    # "Specs"
    # ---------------------------------------------------------------------------
    # Uncomment this section to enable the explicit definition of hosts in your
    # configuration.  Most users will want to use Template, below
    #
    # Specs is an array of Spec entries.  Each Spec entry consists of two fields:
    #   - Hostname:   (Required) The desired hostname, sans the domain.
    #   - CommonName: (Optional) Specifies the template or explicit override for
    #                 the CN.  By default, this is the template:
    #
    #                              "{{.Hostname}}.{{.Domain}}"
    #
    #                 which obtains its values from the Spec.Hostname and
    #                 Org.Domain, respectively.
138
    #   - SANS:       (Optional) Specifies one or more Subject Alternative Names
139
140
141
142
    #                 to be set in the resulting x509. Accepts template
    #                 variables {{.Hostname}}, {{.Domain}}, {{.CommonName}}. IP
    #                 addresses provided here will be properly recognized. Other
    #                 values will be taken as DNS names.
143
144
145
    #                 NOTE: Two implicit entries are created for you:
    #                     - {{ .CommonName }}
    #                     - {{ .Hostname }}
146
147
148
149
    # ---------------------------------------------------------------------------
    # Specs:
    #   - Hostname: foo # implicitly "foo.org1.example.com"
    #     CommonName: foo27.org5.example.com # overrides Hostname-based FQDN set above
150
151
152
153
    #     SANS:
    #       - "bar.{{.Domain}}"
    #       - "altfoo.{{.Domain}}"
    #       - "{{.Hostname}}.org6.net"
154
    #       - 172.16.10.31
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
    #   - Hostname: bar
    #   - Hostname: baz

    # ---------------------------------------------------------------------------
    # "Template"
    # ---------------------------------------------------------------------------
    # Allows for the definition of 1 or more hosts that are created sequentially
    # from a template. By default, this looks like "peer%d" from 0 to Count-1.
    # You may override the number of nodes (Count), the starting index (Start)
    # or the template used to construct the name (Hostname).
    #
    # Note: Template and Specs are not mutually exclusive.  You may define both
    # sections and the aggregate nodes will be created for you.  Take care with
    # name collisions
    # ---------------------------------------------------------------------------
    Template:
      Count: 1
      # Start: 5
      # Hostname: {{.Prefix}}{{.Index}} # default
174
175
      # SANS:
      #   - "{{.Hostname}}.alt.{{.Domain}}"
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

    # ---------------------------------------------------------------------------
    # "Users"
    # ---------------------------------------------------------------------------
    # Count: The number of user accounts _in addition_ to Admin
    # ---------------------------------------------------------------------------
    Users:
      Count: 1

  # ---------------------------------------------------------------------------
  # Org2: See "Org1" for full specification
  # ---------------------------------------------------------------------------
  - Name: Org2
    Domain: org2.example.com
    Template:
      Count: 1
    Users:
      Count: 1
`

196
197
//command line flags
var (
198
199
200
201
202
203
204
	app = kingpin.New("cryptogen", "Utility for generating Hyperledger Fabric key material")

	gen        = app.Command("generate", "Generate key material")
	outputDir  = gen.Flag("output", "The output directory in which to place artifacts").Default("crypto-config").String()
	configFile = gen.Flag("config", "The configuration template to use").File()

	showtemplate = app.Command("showtemplate", "Show the default configuration template")
205
206

	version = app.Command("version", "Show version information")
207
208
209
)

func main() {
210
211
	kingpin.Version("0.0.1")
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
212

213
214
215
	// "generate" command
	case gen.FullCommand():
		generate()
216

217
218
219
220
	// "showtemplate" command
	case showtemplate.FullCommand():
		fmt.Print(defaultConfig)
		os.Exit(0)
221
222
223
224

	// "version" command
	case version.FullCommand():
		printVersion()
225
226
	}

227
228
229
230
231
232
233
234
235
236
237
238
239
240
}

func getConfig() (*Config, error) {
	var configData string

	if *configFile != nil {
		data, err := ioutil.ReadAll(*configFile)
		if err != nil {
			return nil, fmt.Errorf("Error reading configuration: %s", err)
		}

		configData = string(data)
	} else {
		configData = defaultConfig
241
242
	}

243
244
245
246
	config := &Config{}
	err := yaml.Unmarshal([]byte(configData), &config)
	if err != nil {
		return nil, fmt.Errorf("Error Unmarshaling YAML: %s", err)
247
248
	}

249
	return config, nil
250
251
}

252
253
254
255
256
257
258
func generate() {

	config, err := getConfig()
	if err != nil {
		fmt.Printf("Error reading config: %s", err)
		os.Exit(-1)
	}
259

260
	for _, orgSpec := range config.PeerOrgs {
261
		err = renderOrgSpec(&orgSpec, "peer")
262
		if err != nil {
263
264
			fmt.Printf("Error processing peer configuration: %s", err)
			os.Exit(-1)
265
		}
266
267
268
269
		generatePeerOrg(*outputDir, orgSpec)
	}

	for _, orgSpec := range config.OrdererOrgs {
270
		renderOrgSpec(&orgSpec, "orderer")
271
		if err != nil {
272
273
			fmt.Printf("Error processing orderer configuration: %s", err)
			os.Exit(-1)
274
		}
275
276
277
278
		generateOrdererOrg(*outputDir, orgSpec)
	}
}

279
func parseTemplate(input string, data interface{}) (string, error) {
280
281
282
283
284
285
286
287
288
289
290

	t, err := template.New("parse").Parse(input)
	if err != nil {
		return "", fmt.Errorf("Error parsing template: %s", err)
	}

	output := new(bytes.Buffer)
	err = t.Execute(output, data)
	if err != nil {
		return "", fmt.Errorf("Error executing template: %s", err)
	}
291

292
293
294
	return output.String(), nil
}

295
296
297
298
299
300
301
302
303
304
305
306
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{
307
308
309
310
		Hostname: spec.Hostname,
		Domain:   domain,
	}

311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
	// 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
337
338
}

339
func renderOrgSpec(orgSpec *OrgSpec, prefix string) error {
340
341
342
343
344
345
346
347
	// First process all of our templated nodes
	for i := 0; i < orgSpec.Template.Count; i++ {
		data := HostnameData{
			Prefix: prefix,
			Index:  i + orgSpec.Template.Start,
			Domain: orgSpec.Domain,
		}

348
		hostname, err := parseTemplateWithDefault(orgSpec.Template.Hostname, defaultHostnameTemplate, data)
349
350
		if err != nil {
			return err
351
		}
Gari Singh's avatar
Gari Singh committed
352

353
354
355
356
		spec := NodeSpec{
			Hostname: hostname,
			SANS:     orgSpec.Template.SANS,
		}
357
358
359
		orgSpec.Specs = append(orgSpec.Specs, spec)
	}

360
	// Touch up all general node-specs to add the domain
361
	for idx, spec := range orgSpec.Specs {
362
		err := renderNodeSpec(orgSpec.Domain, &spec)
Gari Singh's avatar
Gari Singh committed
363
		if err != nil {
364
			return err
Gari Singh's avatar
Gari Singh committed
365
366
		}

367
		orgSpec.Specs[idx] = spec
368
	}
369

370
371
372
373
	// Process the CA node-spec in the same manner
	if len(orgSpec.CA.Hostname) == 0 {
		orgSpec.CA.Hostname = "ca"
	}
374
	err := renderNodeSpec(orgSpec.Domain, &orgSpec.CA)
375
376
377
378
	if err != nil {
		return err
	}

379
	return nil
380
381
}

382
383
384
385
386
func generatePeerOrg(baseDir string, orgSpec OrgSpec) {

	orgName := orgSpec.Domain

	fmt.Println(orgName)
387
	// generate CAs
388
389
	orgDir := filepath.Join(baseDir, "peerOrganizations", orgName)
	caDir := filepath.Join(orgDir, "ca")
390
	tlsCADir := filepath.Join(orgDir, "tlsca")
391
392
393
394
	mspDir := filepath.Join(orgDir, "msp")
	peersDir := filepath.Join(orgDir, "peers")
	usersDir := filepath.Join(orgDir, "users")
	adminCertsDir := filepath.Join(mspDir, "admincerts")
395
396
	// generate signing CA
	signCA, err := ca.NewCA(caDir, orgName, orgSpec.CA.CommonName)
397
	if err != nil {
398
		fmt.Printf("Error generating signCA for org %s:\n%v\n", orgName, err)
399
400
		os.Exit(1)
	}
401
402
403
404
405
406
	// generate TLS CA
	tlsCA, err := ca.NewCA(tlsCADir, orgName, "tls"+orgSpec.CA.CommonName)
	if err != nil {
		fmt.Printf("Error generating tlsCA for org %s:\n%v\n", orgName, err)
		os.Exit(1)
	}
407

408
	err = msp.GenerateVerifyingMSP(mspDir, signCA, tlsCA)
409
410
411
412
413
	if err != nil {
		fmt.Printf("Error generating MSP for org %s:\n%v\n", orgName, err)
		os.Exit(1)
	}

414
	generateNodes(peersDir, orgSpec.Specs, signCA, tlsCA)
415
416

	// TODO: add ability to specify usernames
417
	users := []NodeSpec{}
418
	for j := 1; j <= orgSpec.Users.Count; j++ {
419
420
421
422
423
		user := NodeSpec{
			CommonName: fmt.Sprintf("%s%d@%s", userBaseName, j, orgName),
		}

		users = append(users, user)
424
425
	}
	// add an admin user
426
427
428
	adminUser := NodeSpec{
		CommonName: fmt.Sprintf("%s@%s", adminBaseName, orgName),
	}
429

430
	users = append(users, adminUser)
431
	generateNodes(usersDir, users, signCA, tlsCA)
432
433

	// copy the admin cert to the org's MSP admincerts
434
	err = copyAdminCert(usersDir, adminCertsDir, adminUser.CommonName)
435
436
437
438
	if err != nil {
		fmt.Printf("Error copying admin cert for org %s:\n%v\n",
			orgName, err)
		os.Exit(1)
439
	}
440
441

	// copy the admin cert to each of the org's peer's MSP admincerts
442
	for _, spec := range orgSpec.Specs {
443
		err = copyAdminCert(usersDir,
444
			filepath.Join(peersDir, spec.CommonName, "msp", "admincerts"), adminUser.CommonName)
445
446
		if err != nil {
			fmt.Printf("Error copying admin cert for org %s peer %s:\n%v\n",
447
				orgName, spec.CommonName, err)
448
449
450
			os.Exit(1)
		}
	}
451
452
}

Gari Singh's avatar
Gari Singh committed
453
454
455
456
457
458
459
460
461
462
463
func copyAdminCert(usersDir, adminCertsDir, adminUserName string) error {
	// delete the contents of admincerts
	err := os.RemoveAll(adminCertsDir)
	if err != nil {
		return err
	}
	// recreate the admincerts directory
	err = os.MkdirAll(adminCertsDir, 0755)
	if err != nil {
		return err
	}
464
	err = copyFile(filepath.Join(usersDir, adminUserName, "msp", "signcerts",
Gari Singh's avatar
Gari Singh committed
465
466
467
468
469
470
471
472
473
		adminUserName+"-cert.pem"), filepath.Join(adminCertsDir,
		adminUserName+"-cert.pem"))
	if err != nil {
		return err
	}
	return nil

}

474
func generateNodes(baseDir string, nodes []NodeSpec, signCA *ca.CA, tlsCA *ca.CA) {
475

476
477
	for _, node := range nodes {
		nodeDir := filepath.Join(baseDir, node.CommonName)
478
		err := msp.GenerateLocalMSP(nodeDir, node.CommonName, node.SANS, signCA, tlsCA)
479
		if err != nil {
480
			fmt.Printf("Error generating local MSP for %s:\n%v\n", node, err)
481
482
483
484
485
			os.Exit(1)
		}
	}
}

486
487
488
func generateOrdererOrg(baseDir string, orgSpec OrgSpec) {

	orgName := orgSpec.Domain
489

490
	// generate CAs
491
492
	orgDir := filepath.Join(baseDir, "ordererOrganizations", orgName)
	caDir := filepath.Join(orgDir, "ca")
493
	tlsCADir := filepath.Join(orgDir, "tlsca")
494
495
	mspDir := filepath.Join(orgDir, "msp")
	orderersDir := filepath.Join(orgDir, "orderers")
Gari Singh's avatar
Gari Singh committed
496
497
	usersDir := filepath.Join(orgDir, "users")
	adminCertsDir := filepath.Join(mspDir, "admincerts")
498
499
500
501
502
503
504
505
	// generate signing CA
	signCA, err := ca.NewCA(caDir, orgName, orgSpec.CA.CommonName)
	if err != nil {
		fmt.Printf("Error generating signCA for org %s:\n%v\n", orgName, err)
		os.Exit(1)
	}
	// generate TLS CA
	tlsCA, err := ca.NewCA(tlsCADir, orgName, "tls"+orgSpec.CA.CommonName)
506
	if err != nil {
507
		fmt.Printf("Error generating tlsCA for org %s:\n%v\n", orgName, err)
508
509
		os.Exit(1)
	}
510

511
	err = msp.GenerateVerifyingMSP(mspDir, signCA, tlsCA)
512
513
514
515
516
	if err != nil {
		fmt.Printf("Error generating MSP for org %s:\n%v\n", orgName, err)
		os.Exit(1)
	}

517
	generateNodes(orderersDir, orgSpec.Specs, signCA, tlsCA)
518

519
520
521
	adminUser := NodeSpec{
		CommonName: fmt.Sprintf("%s@%s", adminBaseName, orgName),
	}
522

Gari Singh's avatar
Gari Singh committed
523
	// generate an admin for the orderer org
524
	users := []NodeSpec{}
Gari Singh's avatar
Gari Singh committed
525
	// add an admin user
526
	users = append(users, adminUser)
527
	generateNodes(usersDir, users, signCA, tlsCA)
Gari Singh's avatar
Gari Singh committed
528
529

	// copy the admin cert to the org's MSP admincerts
530
	err = copyAdminCert(usersDir, adminCertsDir, adminUser.CommonName)
Gari Singh's avatar
Gari Singh committed
531
532
533
534
535
536
	if err != nil {
		fmt.Printf("Error copying admin cert for org %s:\n%v\n",
			orgName, err)
		os.Exit(1)
	}

537
	// copy the admin cert to each of the org's orderers's MSP admincerts
538
	for _, spec := range orgSpec.Specs {
539
		err = copyAdminCert(usersDir,
540
			filepath.Join(orderersDir, spec.CommonName, "msp", "admincerts"), adminUser.CommonName)
541
542
		if err != nil {
			fmt.Printf("Error copying admin cert for org %s orderer %s:\n%v\n",
543
				orgName, spec.CommonName, err)
544
545
546
547
			os.Exit(1)
		}
	}

Gari Singh's avatar
Gari Singh committed
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
}

func copyFile(src, dst string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()
	_, err = io.Copy(out, in)
	cerr := out.Close()
	if err != nil {
		return err
	}
	return cerr
567
}
568
569
570
571

func printVersion() {
	fmt.Println(metadata.GetVersionInfo())
}