github.com/onflow/flow-go@v0.33.17/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 "google.golang.org/grpc/credentials/insecure" 18 19 "github.com/onflow/flow-go/crypto" 20 accessmock "github.com/onflow/flow-go/engine/access/mock" 21 "github.com/onflow/flow-go/engine/access/rpc" 22 "github.com/onflow/flow-go/engine/access/rpc/backend" 23 statestreambackend "github.com/onflow/flow-go/engine/access/state_stream/backend" 24 "github.com/onflow/flow-go/model/flow" 25 "github.com/onflow/flow-go/module/grpcserver" 26 "github.com/onflow/flow-go/module/irrecoverable" 27 "github.com/onflow/flow-go/module/metrics" 28 module "github.com/onflow/flow-go/module/mock" 29 "github.com/onflow/flow-go/network" 30 protocol "github.com/onflow/flow-go/state/protocol/mock" 31 storagemock "github.com/onflow/flow-go/storage/mock" 32 "github.com/onflow/flow-go/utils/grpcutils" 33 "github.com/onflow/flow-go/utils/unittest" 34 ) 35 36 // SecureGRPCTestSuite tests that Access node provides a secure GRPC server 37 type SecureGRPCTestSuite struct { 38 suite.Suite 39 state *protocol.State 40 snapshot *protocol.Snapshot 41 epochQuery *protocol.EpochQuery 42 log zerolog.Logger 43 net *network.EngineRegistry 44 request *module.Requester 45 collClient *accessmock.AccessAPIClient 46 execClient *accessmock.ExecutionAPIClient 47 me *module.Local 48 chainID flow.ChainID 49 metrics *metrics.NoopCollector 50 rpcEng *rpc.Engine 51 publicKey crypto.PublicKey 52 53 // storage 54 blocks *storagemock.Blocks 55 headers *storagemock.Headers 56 collections *storagemock.Collections 57 transactions *storagemock.Transactions 58 receipts *storagemock.ExecutionReceipts 59 60 ctx irrecoverable.SignalerContext 61 cancel context.CancelFunc 62 63 // grpc servers 64 secureGrpcServer *grpcserver.GrpcServer 65 unsecureGrpcServer *grpcserver.GrpcServer 66 } 67 68 func (suite *SecureGRPCTestSuite) SetupTest() { 69 suite.log = zerolog.New(os.Stdout) 70 suite.net = new(network.EngineRegistry) 71 suite.state = new(protocol.State) 72 suite.snapshot = new(protocol.Snapshot) 73 74 rootHeader := unittest.BlockHeaderFixture() 75 params := new(protocol.Params) 76 params.On("SporkID").Return(unittest.IdentifierFixture(), nil) 77 params.On("ProtocolVersion").Return(uint(unittest.Uint64InRange(10, 30)), nil) 78 params.On("SporkRootBlockHeight").Return(rootHeader.Height, nil) 79 params.On("SealedRoot").Return(rootHeader, nil) 80 81 suite.epochQuery = new(protocol.EpochQuery) 82 suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() 83 suite.state.On("Final").Return(suite.snapshot, nil).Maybe() 84 suite.state.On("Params").Return(params, nil).Maybe() 85 suite.snapshot.On("Epochs").Return(suite.epochQuery).Maybe() 86 suite.blocks = new(storagemock.Blocks) 87 suite.headers = new(storagemock.Headers) 88 suite.transactions = new(storagemock.Transactions) 89 suite.collections = new(storagemock.Collections) 90 suite.receipts = new(storagemock.ExecutionReceipts) 91 92 suite.collClient = new(accessmock.AccessAPIClient) 93 suite.execClient = new(accessmock.ExecutionAPIClient) 94 95 suite.request = new(module.Requester) 96 suite.request.On("EntityByID", mock.Anything, mock.Anything) 97 98 suite.me = new(module.Local) 99 100 accessIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess)) 101 suite.me. 102 On("NodeID"). 103 Return(accessIdentity.NodeID) 104 105 suite.chainID = flow.Testnet 106 suite.metrics = metrics.NewNoopCollector() 107 108 config := rpc.Config{ 109 UnsecureGRPCListenAddr: unittest.DefaultAddress, 110 SecureGRPCListenAddr: unittest.DefaultAddress, 111 HTTPListenAddr: unittest.DefaultAddress, 112 } 113 114 // generate a server certificate that will be served by the GRPC server 115 networkingKey := unittest.NetworkingPrivKeyFixture() 116 x509Certificate, err := grpcutils.X509Certificate(networkingKey) 117 assert.NoError(suite.T(), err) 118 tlsConfig := grpcutils.DefaultServerTLSConfig(x509Certificate) 119 // set the transport credentials for the server to use 120 config.TransportCredentials = credentials.NewTLS(tlsConfig) 121 // save the public key to use later in tests later 122 suite.publicKey = networkingKey.PublicKey() 123 124 suite.secureGrpcServer = grpcserver.NewGrpcServerBuilder(suite.log, 125 config.SecureGRPCListenAddr, 126 grpcutils.DefaultMaxMsgSize, 127 false, 128 nil, 129 nil, 130 grpcserver.WithTransportCredentials(config.TransportCredentials)).Build() 131 132 suite.unsecureGrpcServer = grpcserver.NewGrpcServerBuilder(suite.log, 133 config.UnsecureGRPCListenAddr, 134 grpcutils.DefaultMaxMsgSize, 135 false, 136 nil, 137 nil).Build() 138 139 block := unittest.BlockHeaderFixture() 140 suite.snapshot.On("Head").Return(block, nil) 141 142 bnd, err := backend.New(backend.Params{ 143 State: suite.state, 144 CollectionRPC: suite.collClient, 145 Blocks: suite.blocks, 146 Headers: suite.headers, 147 Collections: suite.collections, 148 Transactions: suite.transactions, 149 ChainID: suite.chainID, 150 AccessMetrics: suite.metrics, 151 MaxHeightRange: 0, 152 Log: suite.log, 153 SnapshotHistoryLimit: 0, 154 Communicator: backend.NewNodeCommunicator(false), 155 }) 156 suite.Require().NoError(err) 157 158 stateStreamConfig := statestreambackend.Config{} 159 rpcEngBuilder, err := rpc.NewBuilder( 160 suite.log, 161 suite.state, 162 config, 163 suite.chainID, 164 suite.metrics, 165 false, 166 suite.me, 167 bnd, 168 bnd, 169 suite.secureGrpcServer, 170 suite.unsecureGrpcServer, 171 nil, 172 stateStreamConfig, 173 ) 174 assert.NoError(suite.T(), err) 175 suite.rpcEng, err = rpcEngBuilder.WithLegacy().Build() 176 assert.NoError(suite.T(), err) 177 suite.ctx, suite.cancel = irrecoverable.NewMockSignalerContextWithCancel(suite.T(), context.Background()) 178 179 suite.rpcEng.Start(suite.ctx) 180 181 suite.secureGrpcServer.Start(suite.ctx) 182 suite.unsecureGrpcServer.Start(suite.ctx) 183 184 // wait for the servers to startup 185 unittest.AssertClosesBefore(suite.T(), suite.secureGrpcServer.Ready(), 2*time.Second) 186 unittest.AssertClosesBefore(suite.T(), suite.unsecureGrpcServer.Ready(), 2*time.Second) 187 188 // wait for the engine to startup 189 unittest.AssertClosesBefore(suite.T(), suite.rpcEng.Ready(), 2*time.Second) 190 } 191 192 func (suite *SecureGRPCTestSuite) TearDownTest() { 193 if suite.cancel != nil { 194 suite.cancel() 195 unittest.AssertClosesBefore(suite.T(), suite.secureGrpcServer.Done(), 2*time.Second) 196 unittest.AssertClosesBefore(suite.T(), suite.unsecureGrpcServer.Done(), 2*time.Second) 197 unittest.AssertClosesBefore(suite.T(), suite.rpcEng.Done(), 2*time.Second) 198 } 199 } 200 201 func TestSecureGRPC(t *testing.T) { 202 suite.Run(t, new(SecureGRPCTestSuite)) 203 } 204 205 func (suite *SecureGRPCTestSuite) TestAPICallUsingSecureGRPC() { 206 207 req := &accessproto.PingRequest{} 208 ctx := context.Background() 209 210 // expect 2 upstream calls 211 suite.execClient.On("Ping", mock.Anything, mock.Anything).Return(nil, nil).Twice() 212 suite.collClient.On("Ping", mock.Anything, mock.Anything).Return(nil, nil).Twice() 213 214 suite.Run("happy path - grpc client can connect successfully with the correct public key", func() { 215 client, closer := suite.secureGRPCClient(suite.publicKey) 216 defer closer.Close() 217 resp, err := client.Ping(ctx, req) 218 assert.NoError(suite.T(), err) 219 assert.NotNil(suite.T(), resp) 220 }) 221 222 suite.Run("happy path - connection fails with an incorrect public key", func() { 223 newKey := unittest.NetworkingPrivKeyFixture() 224 client, closer := suite.secureGRPCClient(newKey.PublicKey()) 225 defer closer.Close() 226 _, err := client.Ping(ctx, req) 227 assert.Error(suite.T(), err) 228 }) 229 230 suite.Run("happy path - connection fails, unsecure client can not get info from secure server connection", func() { 231 conn, err := grpc.Dial( 232 suite.secureGrpcServer.GRPCAddress().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) 233 assert.NoError(suite.T(), err) 234 235 client := accessproto.NewAccessAPIClient(conn) 236 closer := io.Closer(conn) 237 defer closer.Close() 238 239 _, err = client.Ping(ctx, req) 240 assert.Error(suite.T(), err) 241 }) 242 } 243 244 // secureGRPCClient creates a secure GRPC client using the given public key 245 func (suite *SecureGRPCTestSuite) secureGRPCClient(publicKey crypto.PublicKey) (accessproto.AccessAPIClient, io.Closer) { 246 tlsConfig, err := grpcutils.DefaultClientTLSConfig(publicKey) 247 assert.NoError(suite.T(), err) 248 249 conn, err := grpc.Dial( 250 suite.secureGrpcServer.GRPCAddress().String(), 251 grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) 252 assert.NoError(suite.T(), err) 253 254 client := accessproto.NewAccessAPIClient(conn) 255 closer := io.Closer(conn) 256 return client, closer 257 }