diff options
author | Tor Brede Vekterli <vekterli@yahooinc.com> | 2022-06-22 15:44:57 +0000 |
---|---|---|
committer | Tor Brede Vekterli <vekterli@yahooinc.com> | 2022-06-29 11:20:24 +0000 |
commit | cc44b799f0d78a5e26f12ecb8b868301095570c4 (patch) | |
tree | 374f50996663fbdfa85d529202c0e7cccb99648d /fnet/src/tests | |
parent | cbe98d69506bf60f7fcf7681eb99a79589300882 (diff) |
Support mTLS connection-level capabilities and RPC access filtering in C++
Adds the following:
* Named capabilities and capability sets that represent (respectively)
a single Vespa access API (such as Document API, search API etc)
or a concrete subset of individual capabilities that make up a
particular Vespa service (such as a content node).
* A new `capabilities` array field to the mTLS authorization policies
that allows for constraining what requests sent over a particular
connection are allowed to actually do. Capabilities are referenced
by name and may include any combination of capability sets and
individual capabilities. If multiple capabilities/sets are configured,
the resulting set of capabilities is the union set of all of them.
* An FRT RPC-level access filter that can be set up as part of RPC
method definitions. If set, filters are invoked prior to RPC methods.
* A new `PERMISSION_DENIED` error code to FRT RPC that is invoked if
an access filter denies a request.
This also GCs the unused `AssumedRoles` concept which is now deprecated
in favor of capabilities.
Note: this is **not yet** a public or stable API, and capability
names/semantics may change at any time.
Diffstat (limited to 'fnet/src/tests')
-rw-r--r-- | fnet/src/tests/frt/rpc/invoke.cpp | 55 | ||||
-rw-r--r-- | fnet/src/tests/info/info.cpp | 2 |
2 files changed, 53 insertions, 4 deletions
diff --git a/fnet/src/tests/frt/rpc/invoke.cpp b/fnet/src/tests/frt/rpc/invoke.cpp index 1fbd356b239..e1912985379 100644 --- a/fnet/src/tests/frt/rpc/invoke.cpp +++ b/fnet/src/tests/frt/rpc/invoke.cpp @@ -7,8 +7,10 @@ #include <vespa/fnet/frt/target.h> #include <vespa/fnet/frt/rpcrequest.h> #include <vespa/fnet/frt/invoker.h> +#include <vespa/fnet/frt/request_access_filter.h> #include <mutex> #include <condition_variable> +#include <string_view> using vespalib::SocketSpec; using vespalib::BenchmarkTimer; @@ -175,11 +177,25 @@ public: //------------------------------------------------------------- +struct MyAccessFilter : FRT_RequestAccessFilter { + ~MyAccessFilter() override = default; + + constexpr static std::string_view WRONG_KEY = "...mellon!"; + constexpr static std::string_view CORRECT_KEY = "let me in, I have cake"; + + bool allow(FRT_RPCRequest& req) const noexcept override { + const auto& req_param = req.GetParams()->GetValue(0)._string; + const auto magic_key = std::string_view(req_param._str, req_param._len); + return (magic_key == CORRECT_KEY); + } +}; + class TestRPC : public FRT_Invokable { private: - uint32_t _intValue; - RequestLatch _detached_req; + uint32_t _intValue; + RequestLatch _detached_req; + std::atomic<bool> _restricted_method_was_invoked; TestRPC(const TestRPC &); TestRPC &operator=(const TestRPC &); @@ -187,7 +203,8 @@ private: public: TestRPC(FRT_Supervisor *supervisor) : _intValue(0), - _detached_req() + _detached_req(), + _restricted_method_was_invoked(false) { FRT_ReflectionBuilder rb(supervisor); @@ -201,6 +218,9 @@ public: FRT_METHOD(TestRPC::RPC_GetValue), this); rb.DefineMethod("test", "iibb", "i", FRT_METHOD(TestRPC::RPC_Test), this); + rb.DefineMethod("accessRestricted", "s", "", + FRT_METHOD(TestRPC::RPC_AccessRestricted), this); + rb.RequestAccessFilter(std::make_unique<MyAccessFilter>()); } void RPC_Test(FRT_RPCRequest *req) @@ -244,6 +264,16 @@ public: req->GetReturn()->AddInt32(_intValue); } + void RPC_AccessRestricted([[maybe_unused]] FRT_RPCRequest *req) + { + // We'll only get here if the access filter lets us in + _restricted_method_was_invoked.store(true); + } + + bool restricted_method_was_invoked() const noexcept { + return _restricted_method_was_invoked.load(); + } + RequestLatch &detached_req() { return _detached_req; } }; @@ -264,6 +294,7 @@ public: FRT_Target *make_bad_target() { return _client.supervisor().GetTarget("bogus address"); } RequestLatch &detached_req() { return _testRPC.detached_req(); } EchoTest &echo() { return _echoTest; } + const TestRPC& server_instance() const noexcept { return _testRPC; } Fixture() : _client(crypto), @@ -421,6 +452,24 @@ TEST_F("require that parameters can be echoed as return values", Fixture()) { EXPECT_TRUE(req.get().GetParams()->Equals(req.get().GetReturn())); } +TEST_F("request denied by access filter returns PERMISSION_DENIED and does not invoke server method", Fixture()) { + MyReq req("accessRestricted"); + auto key = MyAccessFilter::WRONG_KEY; + req.get().GetParams()->AddString(key.data(), key.size()); + f1.target().InvokeSync(req.borrow(), timeout); + EXPECT_EQUAL(req.get().GetErrorCode(), FRTE_RPC_PERMISSION_DENIED); + EXPECT_FALSE(f1.server_instance().restricted_method_was_invoked()); +} + +TEST_F("request allowed by access filter invokes server method as usual", Fixture()) { + MyReq req("accessRestricted"); + auto key = MyAccessFilter::CORRECT_KEY; + req.get().GetParams()->AddString(key.data(), key.size()); + f1.target().InvokeSync(req.borrow(), timeout); + ASSERT_FALSE(req.get().IsError()); + EXPECT_TRUE(f1.server_instance().restricted_method_was_invoked()); +} + TEST_MAIN() { crypto = my_crypto_engine(); TEST_RUN_ALL(); diff --git a/fnet/src/tests/info/info.cpp b/fnet/src/tests/info/info.cpp index 0d4e0f90a09..4271546e647 100644 --- a/fnet/src/tests/info/info.cpp +++ b/fnet/src/tests/info/info.cpp @@ -80,7 +80,7 @@ TEST("size of important objects") EXPECT_EQUAL(MUTEX_SIZE + sizeof(std::string) + 112u, sizeof(FNET_IOComponent)); EXPECT_EQUAL(32u, sizeof(FNET_Channel)); EXPECT_EQUAL(40u, sizeof(FNET_PacketQueue_NoLock)); - EXPECT_EQUAL(MUTEX_SIZE + sizeof(std::string) + 408u, sizeof(FNET_Connection)); + EXPECT_EQUAL(MUTEX_SIZE + sizeof(std::string) + 416u, sizeof(FNET_Connection)); EXPECT_EQUAL(48u, sizeof(std::condition_variable)); EXPECT_EQUAL(56u, sizeof(FNET_DataBuffer)); EXPECT_EQUAL(8u, sizeof(FNET_Context)); |