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