github.com/koko1123/flow-go-1@v0.29.6/engine/access/secure_grpcr_test.go (about) 1 package access 2 3 import ( 4 "context" 5 "io" 6 "os" 7 "testing" 8 "time" 9 10 accessproto "github.com/onflow/flow/protobuf/go/flow/access" 11 "github.com/rs/zerolog" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/mock" 14 "github.com/stretchr/testify/suite" 15 "google.golang.org/grpc" 16 "google.golang.org/grpc/credentials" 17 18 accessmock "github.com/koko1123/flow-go-1/engine/access/mock" 19 "github.com/koko1123/flow-go-1/engine/access/rpc" 20 "github.com/koko1123/flow-go-1/model/flow" 21 "github.com/koko1123/flow-go-1/module/metrics" 22 module "github.com/koko1123/flow-go-1/module/mock" 23 "github.com/koko1123/flow-go-1/network" 24 protocol "github.com/koko1123/flow-go-1/state/protocol/mock" 25 storagemock "github.com/koko1123/flow-go-1/storage/mock" 26 "github.com/koko1123/flow-go-1/utils/grpcutils" 27 "github.com/koko1123/flow-go-1/utils/unittest" 28 "github.com/onflow/flow-go/crypto" 29 ) 30 31 // SecureGRPCTestSuite tests that Access node provides a secure GRPC server 32 type SecureGRPCTestSuite struct { 33 suite.Suite 34 state *protocol.State 35 snapshot *protocol.Snapshot 36 epochQuery *protocol.EpochQuery 37 log zerolog.Logger 38 net *network.Network 39 request *module.Requester 40 collClient *accessmock.AccessAPIClient 41 execClient *accessmock.ExecutionAPIClient 42 me *module.Local 43 chainID flow.ChainID 44 metrics *metrics.NoopCollector 45 rpcEng *rpc.Engine 46 publicKey crypto.PublicKey 47 48 // storage 49 blocks *storagemock.Blocks 50 headers *storagemock.Headers 51 collections *storagemock.Collections 52 transactions *storagemock.Transactions 53 receipts *storagemock.ExecutionReceipts 54 } 55 56 func (suite *SecureGRPCTestSuite) SetupTest() { 57 suite.log = zerolog.New(os.Stdout) 58 suite.net = new(network.Network) 59 suite.state = new(protocol.State) 60 suite.snapshot = new(protocol.Snapshot) 61 62 suite.epochQuery = new(protocol.EpochQuery) 63 suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() 64 suite.state.On("Final").Return(suite.snapshot, nil).Maybe() 65 suite.snapshot.On("Epochs").Return(suite.epochQuery).Maybe() 66 suite.blocks = new(storagemock.Blocks) 67 suite.headers = new(storagemock.Headers) 68 suite.transactions = new(storagemock.Transactions) 69 suite.collections = new(storagemock.Collections) 70 suite.receipts = new(storagemock.ExecutionReceipts) 71 72 suite.collClient = new(accessmock.AccessAPIClient) 73 suite.execClient = new(accessmock.ExecutionAPIClient) 74 75 suite.request = new(module.Requester) 76 suite.request.On("EntityByID", mock.Anything, mock.Anything) 77 78 suite.me = new(module.Local) 79 80 accessIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess)) 81 suite.me. 82 On("NodeID"). 83 Return(accessIdentity.NodeID) 84 85 suite.chainID = flow.Testnet 86 suite.metrics = metrics.NewNoopCollector() 87 88 config := rpc.Config{ 89 UnsecureGRPCListenAddr: unittest.DefaultAddress, 90 SecureGRPCListenAddr: unittest.DefaultAddress, 91 HTTPListenAddr: unittest.DefaultAddress, 92 } 93 94 // generate a server certificate that will be served by the GRPC server 95 networkingKey := unittest.NetworkingPrivKeyFixture() 96 x509Certificate, err := grpcutils.X509Certificate(networkingKey) 97 assert.NoError(suite.T(), err) 98 tlsConfig := grpcutils.DefaultServerTLSConfig(x509Certificate) 99 // set the transport credentials for the server to use 100 config.TransportCredentials = credentials.NewTLS(tlsConfig) 101 // save the public key to use later in tests later 102 suite.publicKey = networkingKey.PublicKey() 103 104 rpcEngBuilder, err := rpc.NewBuilder(suite.log, suite.state, config, suite.collClient, nil, suite.blocks, suite.headers, suite.collections, suite.transactions, nil, 105 nil, suite.chainID, suite.metrics, suite.metrics, 0, 0, false, false, nil, nil) 106 assert.NoError(suite.T(), err) 107 suite.rpcEng, err = rpcEngBuilder.WithLegacy().Build() 108 assert.NoError(suite.T(), err) 109 unittest.AssertClosesBefore(suite.T(), suite.rpcEng.Ready(), 2*time.Second) 110 111 // wait for the server to startup 112 assert.Eventually(suite.T(), func() bool { 113 return suite.rpcEng.SecureGRPCAddress() != nil 114 }, 5*time.Second, 10*time.Millisecond) 115 } 116 117 func TestSecureGRPC(t *testing.T) { 118 suite.Run(t, new(SecureGRPCTestSuite)) 119 } 120 121 func (suite *SecureGRPCTestSuite) TestAPICallUsingSecureGRPC() { 122 123 req := &accessproto.PingRequest{} 124 ctx := context.Background() 125 126 // expect 2 upstream calls 127 suite.execClient.On("Ping", mock.Anything, mock.Anything).Return(nil, nil).Twice() 128 suite.collClient.On("Ping", mock.Anything, mock.Anything).Return(nil, nil).Twice() 129 130 suite.Run("happy path - grpc client can connect successfully with the correct public key", func() { 131 client, closer := suite.secureGRPCClient(suite.publicKey) 132 defer closer.Close() 133 resp, err := client.Ping(ctx, req) 134 assert.NoError(suite.T(), err) 135 assert.NotNil(suite.T(), resp) 136 }) 137 138 suite.Run("happy path - connection fails with an incorrect public key", func() { 139 newKey := unittest.NetworkingPrivKeyFixture() 140 client, closer := suite.secureGRPCClient(newKey.PublicKey()) 141 defer closer.Close() 142 _, err := client.Ping(ctx, req) 143 assert.Error(suite.T(), err) 144 }) 145 } 146 147 func (suite *SecureGRPCTestSuite) TearDownTest() { 148 // close the server 149 if suite.rpcEng != nil { 150 unittest.AssertClosesBefore(suite.T(), suite.rpcEng.Done(), 2*time.Second) 151 } 152 } 153 154 // secureGRPCClient creates a secure GRPC client using the given public key 155 func (suite *SecureGRPCTestSuite) secureGRPCClient(publicKey crypto.PublicKey) (accessproto.AccessAPIClient, io.Closer) { 156 tlsConfig, err := grpcutils.DefaultClientTLSConfig(publicKey) 157 assert.NoError(suite.T(), err) 158 159 conn, err := grpc.Dial( 160 suite.rpcEng.SecureGRPCAddress().String(), 161 grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) 162 assert.NoError(suite.T(), err) 163 164 client := accessproto.NewAccessAPIClient(conn) 165 closer := io.Closer(conn) 166 return client, closer 167 }