github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/orderer/common/server/main_test.go (about) 1 // Copyright IBM Corp. All Rights Reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package server 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "strconv" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/golang/protobuf/proto" 20 "github.com/hyperledger/fabric-protos-go/common" 21 "github.com/osdi23p228/fabric/bccsp/factory" 22 "github.com/osdi23p228/fabric/bccsp/sw" 23 "github.com/osdi23p228/fabric/common/channelconfig" 24 "github.com/osdi23p228/fabric/common/crypto/tlsgen" 25 "github.com/osdi23p228/fabric/common/flogging" 26 "github.com/osdi23p228/fabric/common/flogging/floggingtest" 27 "github.com/osdi23p228/fabric/common/ledger/blockledger" 28 "github.com/osdi23p228/fabric/common/ledger/blockledger/fileledger" 29 "github.com/osdi23p228/fabric/common/metrics/disabled" 30 "github.com/osdi23p228/fabric/common/metrics/prometheus" 31 "github.com/osdi23p228/fabric/core/config/configtest" 32 "github.com/osdi23p228/fabric/internal/configtxgen/encoder" 33 "github.com/osdi23p228/fabric/internal/configtxgen/genesisconfig" 34 "github.com/osdi23p228/fabric/internal/pkg/comm" 35 "github.com/osdi23p228/fabric/internal/pkg/identity" 36 "github.com/osdi23p228/fabric/orderer/common/bootstrap/file" 37 "github.com/osdi23p228/fabric/orderer/common/cluster" 38 "github.com/osdi23p228/fabric/orderer/common/localconfig" 39 "github.com/osdi23p228/fabric/orderer/common/multichannel" 40 "github.com/osdi23p228/fabric/orderer/common/onboarding" 41 server_mocks "github.com/osdi23p228/fabric/orderer/common/server/mocks" 42 "github.com/osdi23p228/fabric/orderer/consensus" 43 "github.com/osdi23p228/fabric/protoutil" 44 . "github.com/onsi/gomega" 45 "github.com/onsi/gomega/gexec" 46 "github.com/pkg/errors" 47 "github.com/stretchr/testify/assert" 48 "github.com/stretchr/testify/require" 49 "go.uber.org/zap" 50 "go.uber.org/zap/zapcore" 51 ) 52 53 //go:generate counterfeiter -o mocks/signer_serializer.go --fake-name SignerSerializer . signerSerializer 54 55 type signerSerializer interface { 56 identity.SignerSerializer 57 } 58 59 // the path to cryptogen, which can be used by tests to create certificates 60 var cryptogen, tempDir string 61 62 func TestMain(m *testing.M) { 63 var err error 64 cryptogen, err = gexec.Build("github.com/osdi23p228/fabric/cmd/cryptogen") 65 if err != nil { 66 fmt.Fprintf(os.Stderr, "cryptogen build failed: %v", err) 67 os.Exit(-1) 68 } 69 defer gexec.CleanupBuildArtifacts() 70 71 tempDir, err = ioutil.TempDir("", "main-test") 72 if err != nil { 73 fmt.Fprintf(os.Stderr, "failed to create temporary directory: %v", err) 74 os.Exit(-1) 75 } 76 defer os.RemoveAll(tempDir) 77 78 copyYamlFiles("testdata", tempDir) 79 80 os.Exit(m.Run()) 81 } 82 83 func copyYamlFiles(src, dst string) { 84 for _, file := range []string{"configtx.yaml", "examplecom-config.yaml", "orderer.yaml"} { 85 fileBytes, err := ioutil.ReadFile(filepath.Join(src, file)) 86 if err != nil { 87 os.Exit(-1) 88 } 89 err = ioutil.WriteFile(filepath.Join(dst, file), fileBytes, 0644) 90 if err != nil { 91 os.Exit(-1) 92 } 93 } 94 } 95 96 func TestInitializeLogging(t *testing.T) { 97 origEnvValue := os.Getenv("FABRIC_LOGGING_SPEC") 98 os.Setenv("FABRIC_LOGGING_SPEC", "foo=debug") 99 initializeLogging() 100 assert.Equal(t, "debug", flogging.LoggerLevel("foo")) 101 os.Setenv("FABRIC_LOGGING_SPEC", origEnvValue) 102 } 103 104 func TestInitializeProfilingService(t *testing.T) { 105 origEnvValue := os.Getenv("FABRIC_LOGGING_SPEC") 106 defer os.Setenv("FABRIC_LOGGING_SPEC", origEnvValue) 107 os.Setenv("FABRIC_LOGGING_SPEC", "debug") 108 // get a free random port 109 listenAddr := func() string { 110 l, _ := net.Listen("tcp", "localhost:0") 111 l.Close() 112 return l.Addr().String() 113 }() 114 go initializeProfilingService( 115 &localconfig.TopLevel{ 116 General: localconfig.General{ 117 Profile: localconfig.Profile{ 118 Enabled: true, 119 Address: listenAddr, 120 }}, 121 Kafka: localconfig.Kafka{Verbose: true}, 122 }, 123 ) 124 time.Sleep(500 * time.Millisecond) 125 if _, err := http.Get("http://" + listenAddr + "/" + "/debug/"); err != nil { 126 t.Logf("Expected pprof to be up (will retry again in 3 seconds): %s", err) 127 time.Sleep(3 * time.Second) 128 if _, err := http.Get("http://" + listenAddr + "/" + "/debug/"); err != nil { 129 t.Fatalf("Expected pprof to be up: %s", err) 130 } 131 } 132 } 133 134 func TestInitializeServerConfig(t *testing.T) { 135 conf := &localconfig.TopLevel{ 136 General: localconfig.General{ 137 ConnectionTimeout: 7 * time.Second, 138 TLS: localconfig.TLS{ 139 Enabled: true, 140 ClientAuthRequired: true, 141 Certificate: "main.go", 142 PrivateKey: "main.go", 143 RootCAs: []string{"main.go"}, 144 ClientRootCAs: []string{"main.go"}, 145 }, 146 }, 147 } 148 sc := initializeServerConfig(conf, nil) 149 expectedContent, _ := ioutil.ReadFile("main.go") 150 assert.Equal(t, expectedContent, sc.SecOpts.Certificate) 151 assert.Equal(t, expectedContent, sc.SecOpts.Key) 152 assert.Equal(t, [][]byte{expectedContent}, sc.SecOpts.ServerRootCAs) 153 assert.Equal(t, [][]byte{expectedContent}, sc.SecOpts.ClientRootCAs) 154 155 sc = initializeServerConfig(conf, nil) 156 defaultOpts := comm.DefaultKeepaliveOptions 157 assert.Equal(t, defaultOpts.ServerMinInterval, sc.KaOpts.ServerMinInterval) 158 assert.Equal(t, time.Duration(0), sc.KaOpts.ServerInterval) 159 assert.Equal(t, time.Duration(0), sc.KaOpts.ServerTimeout) 160 assert.Equal(t, 7*time.Second, sc.ConnectionTimeout) 161 testDuration := 10 * time.Second 162 conf.General.Keepalive = localconfig.Keepalive{ 163 ServerMinInterval: testDuration, 164 ServerInterval: testDuration, 165 ServerTimeout: testDuration, 166 } 167 sc = initializeServerConfig(conf, nil) 168 assert.Equal(t, testDuration, sc.KaOpts.ServerMinInterval) 169 assert.Equal(t, testDuration, sc.KaOpts.ServerInterval) 170 assert.Equal(t, testDuration, sc.KaOpts.ServerTimeout) 171 172 sc = initializeServerConfig(conf, nil) 173 assert.NotNil(t, sc.Logger) 174 assert.Equal(t, comm.NewServerStatsHandler(&disabled.Provider{}), sc.ServerStatsHandler) 175 assert.Len(t, sc.UnaryInterceptors, 2) 176 assert.Len(t, sc.StreamInterceptors, 2) 177 178 sc = initializeServerConfig(conf, &prometheus.Provider{}) 179 assert.NotNil(t, sc.ServerStatsHandler) 180 181 goodFile := "main.go" 182 badFile := "does_not_exist" 183 184 oldLogger := logger 185 defer func() { logger = oldLogger }() 186 logger, _ = floggingtest.NewTestLogger(t) 187 188 testCases := []struct { 189 name string 190 certificate string 191 privateKey string 192 rootCA string 193 clientRootCert string 194 clusterCert string 195 clusterKey string 196 clusterCA string 197 isCluster bool 198 }{ 199 { 200 name: "BadCertificate", 201 certificate: badFile, 202 privateKey: goodFile, 203 rootCA: goodFile, 204 clientRootCert: goodFile, 205 }, 206 { 207 name: "BadPrivateKey", 208 certificate: goodFile, 209 privateKey: badFile, 210 rootCA: goodFile, 211 clientRootCert: goodFile, 212 }, 213 { 214 name: "BadRootCA", 215 certificate: goodFile, 216 privateKey: goodFile, 217 rootCA: badFile, 218 clientRootCert: goodFile, 219 }, 220 { 221 name: "BadClientRootCertificate", 222 certificate: goodFile, 223 privateKey: goodFile, 224 rootCA: goodFile, 225 clientRootCert: badFile, 226 }, 227 { 228 name: "BadCertificate - cluster reuses server config", 229 certificate: badFile, 230 privateKey: goodFile, 231 rootCA: goodFile, 232 clientRootCert: goodFile, 233 clusterCert: "", 234 clusterKey: "", 235 clusterCA: "", 236 isCluster: true, 237 }, 238 { 239 name: "BadPrivateKey - cluster reuses server config", 240 certificate: goodFile, 241 privateKey: badFile, 242 rootCA: goodFile, 243 clientRootCert: goodFile, 244 clusterCert: "", 245 clusterKey: "", 246 clusterCA: "", 247 isCluster: true, 248 }, 249 { 250 name: "BadRootCA - cluster reuses server config", 251 certificate: goodFile, 252 privateKey: goodFile, 253 rootCA: badFile, 254 clientRootCert: goodFile, 255 clusterCert: "", 256 clusterKey: "", 257 clusterCA: "", 258 isCluster: true, 259 }, 260 { 261 name: "ClusterBadCertificate", 262 certificate: goodFile, 263 privateKey: goodFile, 264 rootCA: goodFile, 265 clientRootCert: goodFile, 266 clusterCert: badFile, 267 clusterKey: goodFile, 268 clusterCA: goodFile, 269 isCluster: true, 270 }, 271 { 272 name: "ClusterBadPrivateKey", 273 certificate: goodFile, 274 privateKey: goodFile, 275 rootCA: goodFile, 276 clientRootCert: goodFile, 277 clusterCert: goodFile, 278 clusterKey: badFile, 279 clusterCA: goodFile, 280 isCluster: true, 281 }, 282 { 283 name: "ClusterBadRootCA", 284 certificate: goodFile, 285 privateKey: goodFile, 286 rootCA: goodFile, 287 clientRootCert: goodFile, 288 clusterCert: goodFile, 289 clusterKey: goodFile, 290 clusterCA: badFile, 291 isCluster: true, 292 }, 293 } 294 for _, tc := range testCases { 295 t.Run(tc.name, func(t *testing.T) { 296 conf := &localconfig.TopLevel{ 297 General: localconfig.General{ 298 TLS: localconfig.TLS{ 299 Enabled: true, 300 ClientAuthRequired: true, 301 Certificate: tc.certificate, 302 PrivateKey: tc.privateKey, 303 RootCAs: []string{tc.rootCA}, 304 ClientRootCAs: []string{tc.clientRootCert}, 305 }, 306 Cluster: localconfig.Cluster{ 307 ClientCertificate: tc.clusterCert, 308 ClientPrivateKey: tc.clusterKey, 309 RootCAs: []string{tc.clusterCA}, 310 }, 311 }, 312 } 313 require.Panics(t, func() { 314 if !tc.isCluster { 315 initializeServerConfig(conf, nil) 316 } else { 317 initializeClusterClientConfig(conf) 318 } 319 }, 320 ) 321 }) 322 } 323 } 324 325 func TestInitializeBootstrapChannel(t *testing.T) { 326 cleanup := configtest.SetDevFabricConfigPath(t) 327 defer cleanup() 328 329 genesisFile := produceGenesisFile(t, genesisconfig.SampleSingleMSPSoloProfile, "testchannelid") 330 defer os.Remove(genesisFile) 331 332 fileLedgerLocation, _ := ioutil.TempDir("", "main_test-") 333 defer os.RemoveAll(fileLedgerLocation) 334 335 ledgerFactory, _, err := createLedgerFactory( 336 &localconfig.TopLevel{ 337 FileLedger: localconfig.FileLedger{ 338 Location: fileLedgerLocation, 339 }, 340 }, 341 &disabled.Provider{}, 342 ) 343 assert.NoError(t, err) 344 bootstrapConfig := &localconfig.TopLevel{ 345 General: localconfig.General{ 346 BootstrapMethod: "file", 347 BootstrapFile: genesisFile, 348 }, 349 } 350 351 bootstrapBlock := extractBootstrapBlock(bootstrapConfig) 352 initializeBootstrapChannel(bootstrapBlock, ledgerFactory) 353 354 ledger, err := ledgerFactory.GetOrCreate("testchannelid") 355 assert.NoError(t, err) 356 assert.Equal(t, uint64(1), ledger.Height()) 357 } 358 359 func TestExtractBootstrapBlock(t *testing.T) { 360 cleanup := configtest.SetDevFabricConfigPath(t) 361 defer cleanup() 362 363 genesisFile := produceGenesisFile(t, genesisconfig.SampleSingleMSPSoloProfile, "testchannelid") 364 defer os.Remove(genesisFile) 365 366 tests := []struct { 367 config *localconfig.TopLevel 368 block *common.Block 369 }{ 370 { 371 config: &localconfig.TopLevel{ 372 General: localconfig.General{BootstrapMethod: "file", BootstrapFile: genesisFile}, 373 }, 374 block: file.New(genesisFile).GenesisBlock(), 375 }, 376 { 377 config: &localconfig.TopLevel{ 378 General: localconfig.General{BootstrapMethod: "none"}, 379 }, 380 block: nil, 381 }, 382 } 383 for _, tt := range tests { 384 b := extractBootstrapBlock(tt.config) 385 assert.Truef(t, proto.Equal(tt.block, b), "wanted %v, got %v", tt.block, b) 386 } 387 } 388 389 func TestExtractSysChanLastConfig(t *testing.T) { 390 tmpdir, err := ioutil.TempDir("", "main_test-") 391 require.NoError(t, err) 392 defer os.RemoveAll(tmpdir) 393 394 rlf, err := fileledger.New(tmpdir, &disabled.Provider{}) 395 require.NoError(t, err) 396 397 conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 398 genesisBlock := encoder.New(conf).GenesisBlock() 399 400 lastConf := extractSysChanLastConfig(rlf, genesisBlock) 401 assert.Nil(t, lastConf) 402 403 rl, err := rlf.GetOrCreate("testchannelid") 404 require.NoError(t, err) 405 406 err = rl.Append(genesisBlock) 407 require.NoError(t, err) 408 409 lastConf = extractSysChanLastConfig(rlf, genesisBlock) 410 assert.NotNil(t, lastConf) 411 assert.Equal(t, uint64(0), lastConf.Header.Number) 412 413 assert.Panics(t, func() { 414 _ = extractSysChanLastConfig(rlf, nil) 415 }) 416 417 configTx, err := protoutil.CreateSignedEnvelope(common.HeaderType_CONFIG, "testchannelid", nil, &common.ConfigEnvelope{}, 0, 0) 418 require.NoError(t, err) 419 420 nextBlock := blockledger.CreateNextBlock(rl, []*common.Envelope{configTx}) 421 nextBlock.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&common.Metadata{ 422 Value: protoutil.MarshalOrPanic(&common.OrdererBlockMetadata{ 423 LastConfig: &common.LastConfig{Index: rl.Height()}, 424 }), 425 }) 426 nextBlock.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&common.Metadata{ 427 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: rl.Height()}), 428 }) 429 err = rl.Append(nextBlock) 430 require.NoError(t, err) 431 432 lastConf = extractSysChanLastConfig(rlf, genesisBlock) 433 assert.NotNil(t, lastConf) 434 assert.Equal(t, uint64(1), lastConf.Header.Number) 435 } 436 437 func TestSelectClusterBootBlock(t *testing.T) { 438 bootstrapBlock := &common.Block{Header: &common.BlockHeader{Number: 100}} 439 lastConfBlock := &common.Block{Header: &common.BlockHeader{Number: 100}} 440 441 clusterBoot := selectClusterBootBlock(bootstrapBlock, nil) 442 assert.NotNil(t, clusterBoot) 443 assert.Equal(t, uint64(100), clusterBoot.Header.Number) 444 assert.True(t, bootstrapBlock == clusterBoot) 445 446 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 447 assert.NotNil(t, clusterBoot) 448 assert.Equal(t, uint64(100), clusterBoot.Header.Number) 449 assert.True(t, bootstrapBlock == clusterBoot) 450 451 lastConfBlock.Header.Number = 200 452 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 453 assert.NotNil(t, clusterBoot) 454 assert.Equal(t, uint64(200), clusterBoot.Header.Number) 455 assert.True(t, lastConfBlock == clusterBoot) 456 457 bootstrapBlock.Header.Number = 300 458 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 459 assert.NotNil(t, clusterBoot) 460 assert.Equal(t, uint64(300), clusterBoot.Header.Number) 461 assert.True(t, bootstrapBlock == clusterBoot) 462 } 463 464 func TestLoadLocalMSP(t *testing.T) { 465 t.Run("Happy", func(t *testing.T) { 466 localMSPDir := configtest.GetDevMspDir() 467 localMSP := loadLocalMSP( 468 &localconfig.TopLevel{ 469 General: localconfig.General{ 470 LocalMSPDir: localMSPDir, 471 LocalMSPID: "SampleOrg", 472 BCCSP: &factory.FactoryOpts{ 473 ProviderName: "SW", 474 SwOpts: &factory.SwOpts{ 475 HashFamily: "SHA2", 476 SecLevel: 256, 477 }, 478 }, 479 }, 480 }, 481 ) 482 require.NotNil(t, localMSP) 483 id, err := localMSP.GetIdentifier() 484 require.NoError(t, err) 485 require.Equal(t, id, "SampleOrg") 486 }) 487 488 t.Run("Error", func(t *testing.T) { 489 oldLogger := logger 490 defer func() { logger = oldLogger }() 491 logger, _ = floggingtest.NewTestLogger(t) 492 493 assert.Panics(t, func() { 494 loadLocalMSP( 495 &localconfig.TopLevel{ 496 General: localconfig.General{ 497 LocalMSPDir: "", 498 LocalMSPID: "", 499 }, 500 }, 501 ) 502 }) 503 }) 504 } 505 506 func TestInitializeMultichannelRegistrar(t *testing.T) { 507 cleanup := configtest.SetDevFabricConfigPath(t) 508 defer cleanup() 509 genesisFile := produceGenesisFile(t, genesisconfig.SampleDevModeSoloProfile, "testchannelid") 510 defer os.Remove(genesisFile) 511 512 conf := genesisConfig(t, genesisFile) 513 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 514 assert.NoError(t, err) 515 516 signer := &server_mocks.SignerSerializer{} 517 518 t.Run("registrar with a system channel", func(t *testing.T) { 519 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 520 assert.NoError(t, err) 521 bootBlock := file.New(genesisFile).GenesisBlock() 522 initializeBootstrapChannel(bootBlock, lf) 523 registrar := initializeMultichannelRegistrar( 524 bootBlock, 525 onboarding.NewReplicationInitiator(lf, bootBlock, conf, comm.SecureOptions{}, signer, cryptoProvider), 526 &cluster.PredicateDialer{}, 527 comm.ServerConfig{}, 528 nil, 529 conf, 530 signer, 531 &disabled.Provider{}, 532 &server_mocks.HealthChecker{}, 533 lf, 534 cryptoProvider, 535 ) 536 assert.NotNil(t, registrar) 537 assert.Equal(t, "testchannelid", registrar.SystemChannelID()) 538 }) 539 540 t.Run("registrar without a system channel", func(t *testing.T) { 541 conf.General.BootstrapMethod = "none" 542 conf.General.GenesisFile = "" 543 srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{}) 544 assert.NoError(t, err) 545 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 546 assert.NoError(t, err) 547 registrar := initializeMultichannelRegistrar( 548 nil, 549 nil, 550 &cluster.PredicateDialer{}, 551 comm.ServerConfig{}, 552 srv, 553 conf, 554 signer, 555 &disabled.Provider{}, 556 &server_mocks.HealthChecker{}, 557 lf, 558 cryptoProvider, 559 ) 560 assert.NotNil(t, registrar) 561 assert.Empty(t, registrar.SystemChannelID()) 562 }) 563 } 564 565 func TestInitializeGrpcServer(t *testing.T) { 566 // get a free random port 567 listenAddr := func() string { 568 l, _ := net.Listen("tcp", "localhost:0") 569 l.Close() 570 return l.Addr().String() 571 }() 572 host := strings.Split(listenAddr, ":")[0] 573 port, _ := strconv.ParseUint(strings.Split(listenAddr, ":")[1], 10, 16) 574 conf := &localconfig.TopLevel{ 575 General: localconfig.General{ 576 ListenAddress: host, 577 ListenPort: uint16(port), 578 TLS: localconfig.TLS{ 579 Enabled: false, 580 ClientAuthRequired: false, 581 }, 582 }, 583 } 584 assert.NotPanics(t, func() { 585 grpcServer := initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 586 grpcServer.Listener().Close() 587 }) 588 } 589 590 // generateCryptoMaterials uses cryptogen to generate the necessary 591 // MSP files and TLS certificates 592 func generateCryptoMaterials(t *testing.T, cryptogen string) string { 593 gt := NewGomegaWithT(t) 594 cryptoPath := filepath.Join(tempDir, "crypto") 595 596 cmd := exec.Command( 597 cryptogen, 598 "generate", 599 "--config", filepath.Join(tempDir, "examplecom-config.yaml"), 600 "--output", cryptoPath, 601 ) 602 cryptogenProcess, err := gexec.Start(cmd, nil, nil) 603 gt.Expect(err).NotTo(HaveOccurred()) 604 gt.Eventually(cryptogenProcess, time.Minute).Should(gexec.Exit(0)) 605 606 return cryptoPath 607 } 608 609 func TestUpdateTrustedRoots(t *testing.T) { 610 cleanup := configtest.SetDevFabricConfigPath(t) 611 defer cleanup() 612 613 genesisFile := produceGenesisFile(t, genesisconfig.SampleDevModeSoloProfile, "testchannelid") 614 defer os.Remove(genesisFile) 615 616 cryptoPath := generateCryptoMaterials(t, cryptogen) 617 defer os.RemoveAll(cryptoPath) 618 619 // get a free random port 620 listenAddr := func() string { 621 l, _ := net.Listen("tcp", "localhost:0") 622 l.Close() 623 return l.Addr().String() 624 }() 625 port, _ := strconv.ParseUint(strings.Split(listenAddr, ":")[1], 10, 16) 626 conf := &localconfig.TopLevel{ 627 General: localconfig.General{ 628 BootstrapMethod: "file", 629 BootstrapFile: genesisFile, 630 ListenAddress: "localhost", 631 ListenPort: uint16(port), 632 TLS: localconfig.TLS{ 633 Enabled: false, 634 ClientAuthRequired: false, 635 }, 636 }, 637 } 638 grpcServer := initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 639 caMgr := &caManager{ 640 appRootCAsByChain: make(map[string][][]byte), 641 ordererRootCAsByChain: make(map[string][][]byte), 642 } 643 callback := func(bundle *channelconfig.Bundle) { 644 if grpcServer.MutualTLSRequired() { 645 t.Log("callback called") 646 caMgr.updateTrustedRoots(bundle, grpcServer) 647 } 648 } 649 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 650 assert.NoError(t, err) 651 bootBlock := file.New(genesisFile).GenesisBlock() 652 initializeBootstrapChannel(bootBlock, lf) 653 signer := &server_mocks.SignerSerializer{} 654 655 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 656 assert.NoError(t, err) 657 658 initializeMultichannelRegistrar( 659 bootBlock, 660 onboarding.NewReplicationInitiator(lf, bootBlock, conf, comm.SecureOptions{}, signer, cryptoProvider), 661 &cluster.PredicateDialer{}, 662 comm.ServerConfig{}, 663 nil, 664 genesisConfig(t, genesisFile), 665 signer, 666 &disabled.Provider{}, 667 &server_mocks.HealthChecker{}, 668 lf, 669 cryptoProvider, 670 callback, 671 ) 672 t.Logf("# app CAs: %d", len(caMgr.appRootCAsByChain["testchannelid"])) 673 t.Logf("# orderer CAs: %d", len(caMgr.ordererRootCAsByChain["testchannelid"])) 674 // mutual TLS not required so no updates should have occurred 675 assert.Equal(t, 0, len(caMgr.appRootCAsByChain["testchannelid"])) 676 assert.Equal(t, 0, len(caMgr.ordererRootCAsByChain["testchannelid"])) 677 grpcServer.Listener().Close() 678 679 conf = &localconfig.TopLevel{ 680 General: localconfig.General{ 681 ListenAddress: "localhost", 682 ListenPort: uint16(port), 683 TLS: localconfig.TLS{ 684 Enabled: true, 685 ClientAuthRequired: true, 686 PrivateKey: filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls", "server.key"), 687 Certificate: filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls", "server.crt"), 688 }, 689 }, 690 } 691 grpcServer = initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 692 caMgr = &caManager{ 693 appRootCAsByChain: make(map[string][][]byte), 694 ordererRootCAsByChain: make(map[string][][]byte), 695 } 696 697 clusterConf := initializeClusterClientConfig(conf) 698 predDialer := &cluster.PredicateDialer{ 699 Config: clusterConf, 700 } 701 702 callback = func(bundle *channelconfig.Bundle) { 703 if grpcServer.MutualTLSRequired() { 704 t.Log("callback called") 705 caMgr.updateTrustedRoots(bundle, grpcServer) 706 caMgr.updateClusterDialer(predDialer, clusterConf.SecOpts.ServerRootCAs) 707 } 708 } 709 initializeMultichannelRegistrar( 710 bootBlock, 711 onboarding.NewReplicationInitiator(lf, bootBlock, conf, comm.SecureOptions{}, signer, cryptoProvider), 712 predDialer, 713 comm.ServerConfig{}, 714 nil, 715 genesisConfig(t, genesisFile), 716 signer, 717 &disabled.Provider{}, 718 &server_mocks.HealthChecker{}, 719 lf, 720 cryptoProvider, 721 callback, 722 ) 723 t.Logf("# app CAs: %d", len(caMgr.appRootCAsByChain["testchannelid"])) 724 t.Logf("# orderer CAs: %d", len(caMgr.ordererRootCAsByChain["testchannelid"])) 725 // mutual TLS is required so updates should have occurred 726 // we expect an intermediate and root CA for apps and orderers 727 assert.Equal(t, 2, len(caMgr.appRootCAsByChain["testchannelid"])) 728 assert.Equal(t, 2, len(caMgr.ordererRootCAsByChain["testchannelid"])) 729 assert.Len(t, predDialer.Config.SecOpts.ServerRootCAs, 2) 730 grpcServer.Listener().Close() 731 } 732 733 func TestRootServerCertAggregation(t *testing.T) { 734 caMgr := &caManager{ 735 appRootCAsByChain: make(map[string][][]byte), 736 ordererRootCAsByChain: make(map[string][][]byte), 737 } 738 739 predDialer := &cluster.PredicateDialer{ 740 Config: comm.ClientConfig{}, 741 } 742 743 ca1, err := tlsgen.NewCA() 744 require.NoError(t, err) 745 746 ca2, err := tlsgen.NewCA() 747 require.NoError(t, err) 748 749 caMgr.ordererRootCAsByChain["foo"] = [][]byte{ca1.CertBytes()} 750 caMgr.ordererRootCAsByChain["bar"] = [][]byte{ca1.CertBytes()} 751 752 caMgr.updateClusterDialer(predDialer, [][]byte{ca2.CertBytes(), ca2.CertBytes(), ca2.CertBytes()}) 753 754 require.Len(t, predDialer.Config.SecOpts.ServerRootCAs, 2) 755 require.Contains(t, predDialer.Config.SecOpts.ServerRootCAs, ca1.CertBytes()) 756 require.Contains(t, predDialer.Config.SecOpts.ServerRootCAs, ca2.CertBytes()) 757 } 758 759 func TestConfigureClusterListener(t *testing.T) { 760 logEntries := make(chan string, 100) 761 762 allocatePort := func() uint16 { 763 l, err := net.Listen("tcp", "127.0.0.1:0") 764 assert.NoError(t, err) 765 _, portStr, err := net.SplitHostPort(l.Addr().String()) 766 assert.NoError(t, err) 767 port, err := strconv.ParseInt(portStr, 10, 64) 768 assert.NoError(t, err) 769 assert.NoError(t, l.Close()) 770 t.Log("picked unused port", port) 771 return uint16(port) 772 } 773 774 unUsedPort := allocatePort() 775 776 backupLogger := logger 777 logger = logger.WithOptions(zap.Hooks(func(entry zapcore.Entry) error { 778 logEntries <- entry.Message 779 return nil 780 })) 781 782 defer func() { 783 logger = backupLogger 784 }() 785 786 ca, err := tlsgen.NewCA() 787 assert.NoError(t, err) 788 serverKeyPair, err := ca.NewServerCertKeyPair("127.0.0.1") 789 assert.NoError(t, err) 790 791 loadPEM := func(fileName string) ([]byte, error) { 792 switch fileName { 793 case "cert": 794 return serverKeyPair.Cert, nil 795 case "key": 796 return serverKeyPair.Key, nil 797 case "ca": 798 return ca.CertBytes(), nil 799 default: 800 return nil, errors.New("I/O error") 801 } 802 } 803 804 for _, testCase := range []struct { 805 name string 806 conf *localconfig.TopLevel 807 generalConf comm.ServerConfig 808 generalSrv *comm.GRPCServer 809 shouldBeEqual bool 810 expectedPanic string 811 expectedLogEntries []string 812 }{ 813 { 814 name: "invalid certificate", 815 generalConf: comm.ServerConfig{}, 816 conf: &localconfig.TopLevel{ 817 General: localconfig.General{ 818 Cluster: localconfig.Cluster{ 819 ListenAddress: "127.0.0.1", 820 ListenPort: 5000, 821 ServerPrivateKey: "key", 822 ServerCertificate: "bad", 823 RootCAs: []string{"ca"}, 824 }, 825 }, 826 }, 827 expectedPanic: "Failed to load cluster server certificate from 'bad' (I/O error)", 828 generalSrv: &comm.GRPCServer{}, 829 expectedLogEntries: []string{"Failed to load cluster server certificate from 'bad' (I/O error)"}, 830 }, 831 { 832 name: "invalid key", 833 generalConf: comm.ServerConfig{}, 834 conf: &localconfig.TopLevel{ 835 General: localconfig.General{ 836 Cluster: localconfig.Cluster{ 837 ListenAddress: "127.0.0.1", 838 ListenPort: 5000, 839 ServerPrivateKey: "bad", 840 ServerCertificate: "cert", 841 RootCAs: []string{"ca"}, 842 }, 843 }, 844 }, 845 expectedPanic: "Failed to load cluster server key from 'bad' (I/O error)", 846 generalSrv: &comm.GRPCServer{}, 847 expectedLogEntries: []string{"Failed to load cluster server key from 'bad' (I/O error)"}, 848 }, 849 { 850 name: "invalid ca cert", 851 generalConf: comm.ServerConfig{}, 852 conf: &localconfig.TopLevel{ 853 General: localconfig.General{ 854 Cluster: localconfig.Cluster{ 855 ListenAddress: "127.0.0.1", 856 ListenPort: 5000, 857 ServerPrivateKey: "key", 858 ServerCertificate: "cert", 859 RootCAs: []string{"bad"}, 860 }, 861 }, 862 }, 863 expectedPanic: "Failed to load CA cert file 'bad' (I/O error)", 864 generalSrv: &comm.GRPCServer{}, 865 expectedLogEntries: []string{"Failed to load CA cert file 'bad' (I/O error)"}, 866 }, 867 { 868 name: "bad listen address", 869 generalConf: comm.ServerConfig{}, 870 conf: &localconfig.TopLevel{ 871 General: localconfig.General{ 872 Cluster: localconfig.Cluster{ 873 ListenAddress: "99.99.99.99", 874 ListenPort: unUsedPort, 875 ServerPrivateKey: "key", 876 ServerCertificate: "cert", 877 RootCAs: []string{"ca"}, 878 }, 879 }, 880 }, 881 expectedPanic: fmt.Sprintf("Failed creating gRPC server on 99.99.99.99:%d due "+ 882 "to listen tcp 99.99.99.99:%d:", unUsedPort, unUsedPort), 883 generalSrv: &comm.GRPCServer{}, 884 }, 885 { 886 name: "green path", 887 generalConf: comm.ServerConfig{}, 888 conf: &localconfig.TopLevel{ 889 General: localconfig.General{ 890 Cluster: localconfig.Cluster{ 891 ListenAddress: "127.0.0.1", 892 ListenPort: 5000, 893 ServerPrivateKey: "key", 894 ServerCertificate: "cert", 895 RootCAs: []string{"ca"}, 896 }, 897 }, 898 }, 899 generalSrv: &comm.GRPCServer{}, 900 }, 901 } { 902 t.Run(testCase.name, func(t *testing.T) { 903 if testCase.shouldBeEqual { 904 conf, srv := configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 905 assert.Equal(t, conf, testCase.generalConf) 906 assert.Equal(t, srv, testCase.generalSrv) 907 } 908 909 if testCase.expectedPanic != "" { 910 f := func() { 911 configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 912 } 913 assert.Contains(t, panicMsg(f), testCase.expectedPanic) 914 } else { 915 configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 916 } 917 // Ensure logged messages that are expected were all logged 918 var loggedMessages []string 919 for len(logEntries) > 0 { 920 logEntry := <-logEntries 921 loggedMessages = append(loggedMessages, logEntry) 922 } 923 assert.Subset(t, loggedMessages, testCase.expectedLogEntries) 924 }) 925 } 926 } 927 928 func TestReuseListener(t *testing.T) { 929 t.Run("good to reuse", func(t *testing.T) { 930 top := &localconfig.TopLevel{General: localconfig.General{TLS: localconfig.TLS{Enabled: true}}} 931 require.True(t, reuseListener(top)) 932 }) 933 934 t.Run("reuse tls disabled", func(t *testing.T) { 935 top := &localconfig.TopLevel{} 936 require.PanicsWithValue( 937 t, 938 "TLS is required for running ordering nodes of cluster type.", 939 func() { reuseListener(top) }, 940 ) 941 }) 942 943 t.Run("good not to reuse", func(t *testing.T) { 944 top := &localconfig.TopLevel{ 945 General: localconfig.General{ 946 Cluster: localconfig.Cluster{ 947 ListenAddress: "127.0.0.1", 948 ListenPort: 5000, 949 ServerPrivateKey: "key", 950 ServerCertificate: "bad", 951 }, 952 }, 953 } 954 require.False(t, reuseListener(top)) 955 }) 956 957 t.Run("partial config", func(t *testing.T) { 958 top := &localconfig.TopLevel{ 959 General: localconfig.General{ 960 Cluster: localconfig.Cluster{ 961 ListenAddress: "127.0.0.1", 962 ListenPort: 5000, 963 ServerCertificate: "bad", 964 }, 965 }, 966 } 967 require.PanicsWithValue( 968 t, 969 "Options: General.Cluster.ListenPort, General.Cluster.ListenAddress,"+ 970 " General.Cluster.ServerCertificate, General.Cluster.ServerPrivateKey, should be defined altogether.", 971 func() { reuseListener(top) }, 972 ) 973 }) 974 } 975 976 func TestInitializeEtcdraftConsenter(t *testing.T) { 977 consenters := make(map[string]consensus.Consenter) 978 979 tmpdir, err := ioutil.TempDir("", "main_test-") 980 require.NoError(t, err) 981 defer os.RemoveAll(tmpdir) 982 rlf, err := fileledger.New(tmpdir, &disabled.Provider{}) 983 require.NoError(t, err) 984 985 conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 986 genesisBlock := encoder.New(conf).GenesisBlock() 987 988 ca, _ := tlsgen.NewCA() 989 crt, _ := ca.NewServerCertKeyPair("127.0.0.1") 990 991 srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{}) 992 assert.NoError(t, err) 993 994 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 995 assert.NoError(t, err) 996 997 initializeEtcdraftConsenter( 998 consenters, 999 &localconfig.TopLevel{}, 1000 rlf, 1001 &cluster.PredicateDialer{}, 1002 genesisBlock, 1003 onboarding.NewReplicationInitiator(rlf, genesisBlock, nil, comm.SecureOptions{}, nil, cryptoProvider), 1004 comm.ServerConfig{ 1005 SecOpts: comm.SecureOptions{ 1006 Certificate: crt.Cert, 1007 Key: crt.Key, 1008 UseTLS: true, 1009 }, 1010 }, 1011 srv, 1012 &multichannel.Registrar{}, 1013 &disabled.Provider{}, 1014 cryptoProvider, 1015 ) 1016 assert.NotNil(t, consenters["etcdraft"]) 1017 } 1018 1019 func genesisConfig(t *testing.T, genesisFile string) *localconfig.TopLevel { 1020 t.Helper() 1021 localMSPDir := configtest.GetDevMspDir() 1022 return &localconfig.TopLevel{ 1023 General: localconfig.General{ 1024 BootstrapMethod: "file", 1025 BootstrapFile: genesisFile, 1026 LocalMSPDir: localMSPDir, 1027 LocalMSPID: "SampleOrg", 1028 BCCSP: &factory.FactoryOpts{ 1029 ProviderName: "SW", 1030 SwOpts: &factory.SwOpts{ 1031 HashFamily: "SHA2", 1032 SecLevel: 256, 1033 }, 1034 }, 1035 }, 1036 } 1037 } 1038 1039 func panicMsg(f func()) string { 1040 var message interface{} 1041 func() { 1042 1043 defer func() { 1044 message = recover() 1045 }() 1046 1047 f() 1048 1049 }() 1050 1051 return message.(string) 1052 1053 } 1054 1055 func produceGenesisFile(t *testing.T, profile, channelID string) string { 1056 conf := genesisconfig.Load(profile, configtest.GetDevConfigDir()) 1057 f, err := ioutil.TempFile("", fmt.Sprintf("%s-genesis_block-", t.Name())) 1058 require.NoError(t, err) 1059 _, err = f.Write(protoutil.MarshalOrPanic(encoder.New(conf).GenesisBlockForChannel(channelID))) 1060 require.NoError(t, err) 1061 err = f.Close() 1062 require.NoError(t, err) 1063 return f.Name() 1064 }