itest: add integration test for request replacement

This commit is contained in:
Oliver Gugger 2022-07-06 21:17:02 +02:00
parent b1d8767a0c
commit e1efb39177
No known key found for this signature in database
GPG key ID: 8E4256593F177720

View file

@ -10,6 +10,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
@ -41,13 +42,20 @@ func testRPCMiddlewareInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
net.Alice.ReadMacPath(), defaultTimeout, net.Alice.ReadMacPath(), defaultTimeout,
) )
require.NoError(t.t, err) require.NoError(t.t, err)
adminMac, err := net.Alice.ReadMacaroon(
net.Alice.AdminMacPath(), defaultTimeout,
)
require.NoError(t.t, err)
customCaveatMac, err := macaroons.SafeCopyMacaroon(readonlyMac) customCaveatReadonlyMac, err := macaroons.SafeCopyMacaroon(readonlyMac)
require.NoError(t.t, err) require.NoError(t.t, err)
addConstraint := macaroons.CustomConstraint( addConstraint := macaroons.CustomConstraint(
"itest-caveat", "itest-value", "itest-caveat", "itest-value",
) )
require.NoError(t.t, addConstraint(customCaveatMac)) require.NoError(t.t, addConstraint(customCaveatReadonlyMac))
customCaveatAdminMac, err := macaroons.SafeCopyMacaroon(adminMac)
require.NoError(t.t, err)
require.NoError(t.t, addConstraint(customCaveatAdminMac))
// Run all sub-tests now. We can't run anything in parallel because that // Run all sub-tests now. We can't run anything in parallel because that
// would cause the main test function to exit and the nodes being // would cause the main test function to exit and the nodes being
@ -66,7 +74,7 @@ func testRPCMiddlewareInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
middlewareInterceptionTest( middlewareInterceptionTest(
tt, net.Alice, net.Bob, registration, readonlyMac, tt, net.Alice, net.Bob, registration, readonlyMac,
customCaveatMac, true, customCaveatReadonlyMac, true,
) )
}) })
@ -83,8 +91,8 @@ func testRPCMiddlewareInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
defer registration.cancel() defer registration.cancel()
middlewareInterceptionTest( middlewareInterceptionTest(
tt, net.Alice, net.Bob, registration, customCaveatMac, tt, net.Alice, net.Bob, registration,
readonlyMac, false, customCaveatReadonlyMac, readonlyMac, false,
) )
}) })
@ -99,7 +107,10 @@ func testRPCMiddlewareInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
) )
defer registration.cancel() defer registration.cancel()
middlewareManipulationTest( middlewareRequestManipulationTest(
tt, net.Alice, registration, adminMac, true,
)
middlewareResponseManipulationTest(
tt, net.Alice, net.Bob, registration, readonlyMac, true, tt, net.Alice, net.Bob, registration, readonlyMac, true,
) )
}) })
@ -113,10 +124,14 @@ func testRPCMiddlewareInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
) )
defer registration.cancel() defer registration.cancel()
middlewareManipulationTest( middlewareRequestManipulationTest(
tt, net.Alice, net.Bob, registration, customCaveatMac, tt, net.Alice, registration, customCaveatAdminMac,
false, false,
) )
middlewareResponseManipulationTest(
tt, net.Alice, net.Bob, registration,
customCaveatReadonlyMac, false,
)
}) })
// And finally make sure mandatory middleware is always checked for any // And finally make sure mandatory middleware is always checked for any
@ -195,7 +210,7 @@ func middlewareInterceptionTest(t *testing.T, node *lntest.HarnessNode,
// block the execution of the main task otherwise. // block the execution of the main task otherwise.
req := &lnrpc.ListChannelsRequest{ActiveOnly: true} req := &lnrpc.ListChannelsRequest{ActiveOnly: true}
go registration.interceptUnary( go registration.interceptUnary(
"/lnrpc.Lightning/ListChannels", req, nil, readOnly, "/lnrpc.Lightning/ListChannels", req, nil, readOnly, false,
) )
// Do the actual call now and wait for the interceptor to do its thing. // Do the actual call now and wait for the interceptor to do its thing.
@ -292,10 +307,10 @@ func middlewareInterceptionTest(t *testing.T, node *lntest.HarnessNode,
} }
} }
// middlewareManipulationTest tests that unary and streaming requests can be // middlewareResponseManipulationTest tests that unary and streaming responses
// intercepted and also manipulated, at least if the middleware didn't register // can be intercepted and also manipulated, at least if the middleware didn't
// for read-only access. // register for read-only access.
func middlewareManipulationTest(t *testing.T, node *lntest.HarnessNode, func middlewareResponseManipulationTest(t *testing.T, node *lntest.HarnessNode,
peer *lntest.HarnessNode, registration *middlewareHarness, peer *lntest.HarnessNode, registration *middlewareHarness,
userMac *macaroon.Macaroon, readOnly bool) { userMac *macaroon.Macaroon, readOnly bool) {
@ -327,7 +342,7 @@ func middlewareManipulationTest(t *testing.T, node *lntest.HarnessNode,
req := &lnrpc.ListChannelsRequest{ActiveOnly: true} req := &lnrpc.ListChannelsRequest{ActiveOnly: true}
go registration.interceptUnary( go registration.interceptUnary(
"/lnrpc.Lightning/ListChannels", req, replacementResponse, "/lnrpc.Lightning/ListChannels", req, replacementResponse,
readOnly, readOnly, false,
) )
// Do the actual call now and wait for the interceptor to do its thing. // Do the actual call now and wait for the interceptor to do its thing.
@ -384,6 +399,58 @@ func middlewareManipulationTest(t *testing.T, node *lntest.HarnessNode,
peerCancel() peerCancel()
} }
// middlewareRequestManipulationTest tests that unary and streaming requests
// can be intercepted and also manipulated, at least if the middleware didn't
// register for read-only access.
func middlewareRequestManipulationTest(t *testing.T, node *lntest.HarnessNode,
registration *middlewareHarness, userMac *macaroon.Macaroon,
readOnly bool) {
// Everything we test here should be executed in a matter of
// milliseconds, so we can use one single timeout context for all calls.
ctxb := context.Background()
ctxc, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
// Create a client connection that we'll use to simulate user requests
// to lnd with.
cleanup, client := macaroonClient(t, node, userMac)
defer cleanup()
// We're going to attempt to replace the request with our own. But since
// we only registered for read-only access, our replacement should just
// be ignored.
replacementRequest := &lnrpc.Invoice{
Memo: "This is the replaced memo",
Value: 777444,
}
// We're going to send a simple RPC request to add an invoice. We need
// to invoke the intercept logic in a goroutine because we'd block the
// execution of the main task otherwise.
req := &lnrpc.Invoice{
Memo: "Plz pay me",
Value: 123456,
}
go registration.interceptUnary(
"/lnrpc.Lightning/AddInvoice", req, replacementRequest,
readOnly, true,
)
// Do the actual call now and wait for the interceptor to do its thing.
resp, err := client.AddInvoice(ctxc, req)
require.NoError(t, err)
// Did we get the manipulated response or the original one?
invoice, err := zpay32.Decode(resp.PaymentRequest, harnessNetParams)
require.NoError(t, err)
if readOnly {
require.Equal(t, req.Memo, *invoice.Description)
} else {
require.Equal(t, replacementRequest.Memo, *invoice.Description)
}
}
// middlewareMandatoryTest tests that all RPC requests are blocked if there is // middlewareMandatoryTest tests that all RPC requests are blocked if there is
// a mandatory middleware declared that's currently not registered. // a mandatory middleware declared that's currently not registered.
func middlewareMandatoryTest(t *testing.T, node *lntest.HarnessNode, func middlewareMandatoryTest(t *testing.T, node *lntest.HarnessNode,
@ -524,7 +591,7 @@ func registerMiddleware(t *testing.T, node *lntest.HarnessNode,
// read from the response channel. // read from the response channel.
func (h *middlewareHarness) interceptUnary(methodURI string, func (h *middlewareHarness) interceptUnary(methodURI string,
expectedRequest proto.Message, responseReplacement proto.Message, expectedRequest proto.Message, responseReplacement proto.Message,
readOnly bool) { readOnly bool, replaceRequest bool) {
// Read intercept message and make sure it's for an RPC request. // Read intercept message and make sure it's for an RPC request.
reqIntercept, err := h.stream.Recv() reqIntercept, err := h.stream.Recv()
@ -547,7 +614,11 @@ func (h *middlewareHarness) interceptUnary(methodURI string,
assertInterceptedType(h.t, expectedRequest, req) assertInterceptedType(h.t, expectedRequest, req)
// We need to accept the request. // We need to accept the request.
h.sendAccept(reqIntercept.MsgId, nil) if replaceRequest {
h.sendAccept(reqIntercept.MsgId, responseReplacement)
} else {
h.sendAccept(reqIntercept.MsgId, nil)
}
// Now read the intercept message for the response. // Now read the intercept message for the response.
respIntercept, err := h.stream.Recv() respIntercept, err := h.stream.Recv()
@ -562,7 +633,11 @@ func (h *middlewareHarness) interceptUnary(methodURI string,
require.NotEqual(h.t, reqIntercept.MsgId, respIntercept.MsgId) require.NotEqual(h.t, reqIntercept.MsgId, respIntercept.MsgId)
// We need to accept the response as well. // We need to accept the response as well.
h.sendAccept(respIntercept.MsgId, responseReplacement) if replaceRequest {
h.sendAccept(respIntercept.MsgId, nil)
} else {
h.sendAccept(respIntercept.MsgId, responseReplacement)
}
h.responsesChan <- res h.responsesChan <- res
} }