Adding additional blob granule authz tests (#9443)
* added granule location authz tests * added authz test for blob worker endpoint * addressing comments * fixing ide build
This commit is contained in:
parent
29819b0645
commit
910965a5a6
|
@ -40,6 +40,32 @@
|
|||
|
||||
FDB_BOOLEAN_PARAM(PositiveTestcase);
|
||||
|
||||
bool checkGranuleLocations(ErrorOr<GetBlobGranuleLocationsReply> rep, TenantInfo tenant) {
|
||||
if (rep.isError()) {
|
||||
if (rep.getError().code() == error_code_permission_denied) {
|
||||
TraceEvent(SevError, "AuthzSecurityError")
|
||||
.detail("Case", "CrossTenantGranuleLocationCheckDisallowed")
|
||||
.log();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
ASSERT(!rep.get().results.empty());
|
||||
for (auto const& [range, bwIface] : rep.get().results) {
|
||||
if (!range.begin.startsWith(tenant.prefix.get())) {
|
||||
TraceEvent(SevError, "AuthzSecurityBlobGranuleRangeLeak")
|
||||
.detail("TenantId", tenant.tenantId)
|
||||
.detail("LeakingRangeBegin", range.begin.printable());
|
||||
}
|
||||
if (!range.end.startsWith(tenant.prefix.get())) {
|
||||
TraceEvent(SevError, "AuthzSecurityBlobGranuleRangeLeak")
|
||||
.detail("TenantId", tenant.tenantId)
|
||||
.detail("LeakingRangeEnd", range.end.printable());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
struct AuthzSecurityWorkload : TestWorkload {
|
||||
static constexpr auto NAME = "AuthzSecurity";
|
||||
int actorCount;
|
||||
|
@ -55,8 +81,9 @@ struct AuthzSecurityWorkload : TestWorkload {
|
|||
WipedString signedTokenAnotherTenant;
|
||||
Standalone<StringRef> tLogConfigKey;
|
||||
PerfIntCounter crossTenantGetPositive, crossTenantGetNegative, crossTenantCommitPositive, crossTenantCommitNegative,
|
||||
publicNonTenantRequestPositive, tLogReadNegative, keyLocationLeakNegative, crossTenantBGReadPositive,
|
||||
crossTenantBGReadNegative;
|
||||
publicNonTenantRequestPositive, tLogReadNegative, keyLocationLeakNegative, bgLocationLeakNegative,
|
||||
crossTenantBGLocPositive, crossTenantBGLocNegative, crossTenantBGReqPositive, crossTenantBGReqNegative,
|
||||
crossTenantBGReadPositive, crossTenantBGReadNegative;
|
||||
std::vector<std::function<Future<Void>(Database cx)>> testFunctions;
|
||||
bool checkBlobGranules;
|
||||
|
||||
|
@ -65,8 +92,10 @@ struct AuthzSecurityWorkload : TestWorkload {
|
|||
crossTenantGetNegative("CrossTenantGetNegative"), crossTenantCommitPositive("CrossTenantCommitPositive"),
|
||||
crossTenantCommitNegative("CrossTenantCommitNegative"),
|
||||
publicNonTenantRequestPositive("PublicNonTenantRequestPositive"), tLogReadNegative("TLogReadNegative"),
|
||||
keyLocationLeakNegative("KeyLocationLeakNegative"), crossTenantBGReadPositive("CrossTenantBGReadPositive"),
|
||||
crossTenantBGReadNegative("CrossTenantBGReadNegative") {
|
||||
keyLocationLeakNegative("KeyLocationLeakNegative"), bgLocationLeakNegative("BGLocationLeakNegative"),
|
||||
crossTenantBGLocPositive("CrossTenantBGLocPositive"), crossTenantBGLocNegative("CrossTenantBGLocNegative"),
|
||||
crossTenantBGReqPositive("CrossTenantBGReqPositive"), crossTenantBGReqNegative("CrossTenantBGReqNegative"),
|
||||
crossTenantBGReadPositive("CrossTenantBGReadPositive"), crossTenantBGReadNegative("CrossTenantBGReadNegative") {
|
||||
testDuration = getOption(options, "testDuration"_sr, 10.0);
|
||||
transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 500.0) / clientCount;
|
||||
actorCount = getOption(options, "actorsPerClient"_sr, transactionsPerSecond / 5);
|
||||
|
@ -91,6 +120,15 @@ struct AuthzSecurityWorkload : TestWorkload {
|
|||
testFunctions.push_back([this](Database cx) { return testKeyLocationLeakDisallowed(this, cx); });
|
||||
|
||||
if (checkBlobGranules) {
|
||||
testFunctions.push_back([this](Database cx) { return testBlobGranuleLocationLeakDisallowed(this, cx); });
|
||||
testFunctions.push_back(
|
||||
[this](Database cx) { return testCrossTenantBGLocDisallowed(this, cx, PositiveTestcase::True); });
|
||||
testFunctions.push_back(
|
||||
[this](Database cx) { return testCrossTenantBGLocDisallowed(this, cx, PositiveTestcase::False); });
|
||||
testFunctions.push_back(
|
||||
[this](Database cx) { return testCrossTenantBGRequestDisallowed(this, cx, PositiveTestcase::True); });
|
||||
testFunctions.push_back(
|
||||
[this](Database cx) { return testCrossTenantBGRequestDisallowed(this, cx, PositiveTestcase::False); });
|
||||
testFunctions.push_back(
|
||||
[this](Database cx) { return testCrossTenantBGReadDisallowed(this, cx, PositiveTestcase::True); });
|
||||
testFunctions.push_back(
|
||||
|
@ -121,10 +159,17 @@ struct AuthzSecurityWorkload : TestWorkload {
|
|||
if (errors)
|
||||
TraceEvent(SevError, "TestFailure").detail("Reason", "There were client errors.");
|
||||
clients.clear();
|
||||
return errors == 0 && crossTenantGetPositive.getValue() > 0 && crossTenantGetNegative.getValue() > 0 &&
|
||||
crossTenantCommitPositive.getValue() > 0 && crossTenantCommitNegative.getValue() > 0 &&
|
||||
publicNonTenantRequestPositive.getValue() > 0 && tLogReadNegative.getValue() > 0 &&
|
||||
keyLocationLeakNegative.getValue() > 0;
|
||||
bool success = errors == 0 && crossTenantGetPositive.getValue() > 0 && crossTenantGetNegative.getValue() > 0 &&
|
||||
crossTenantCommitPositive.getValue() > 0 && crossTenantCommitNegative.getValue() > 0 &&
|
||||
publicNonTenantRequestPositive.getValue() > 0 && tLogReadNegative.getValue() > 0 &&
|
||||
keyLocationLeakNegative.getValue() > 0;
|
||||
if (checkBlobGranules) {
|
||||
success &= bgLocationLeakNegative.getValue() > 0 && crossTenantBGLocPositive.getValue() > 0 &&
|
||||
crossTenantBGLocNegative.getValue() > 0 && crossTenantBGReqPositive.getValue() > 0 &&
|
||||
crossTenantBGReqNegative.getValue() > 0 && crossTenantBGReadPositive.getValue() > 0 &&
|
||||
crossTenantBGReadNegative.getValue() > 0;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void getMetrics(std::vector<PerfMetric>& m) override {
|
||||
|
@ -135,8 +180,15 @@ struct AuthzSecurityWorkload : TestWorkload {
|
|||
m.push_back(publicNonTenantRequestPositive.getMetric());
|
||||
m.push_back(tLogReadNegative.getMetric());
|
||||
m.push_back(keyLocationLeakNegative.getMetric());
|
||||
m.push_back(crossTenantBGReadPositive.getMetric());
|
||||
m.push_back(crossTenantBGReadNegative.getMetric());
|
||||
if (checkBlobGranules) {
|
||||
m.push_back(bgLocationLeakNegative.getMetric());
|
||||
m.push_back(crossTenantBGLocPositive.getMetric());
|
||||
m.push_back(crossTenantBGLocNegative.getMetric());
|
||||
m.push_back(crossTenantBGReqPositive.getMetric());
|
||||
m.push_back(crossTenantBGReqNegative.getMetric());
|
||||
m.push_back(crossTenantBGReadPositive.getMetric());
|
||||
m.push_back(crossTenantBGReadNegative.getMetric());
|
||||
}
|
||||
}
|
||||
|
||||
void setAuthToken(Transaction& tr, StringRef token) {
|
||||
|
@ -482,6 +534,99 @@ struct AuthzSecurityWorkload : TestWorkload {
|
|||
return Void();
|
||||
}
|
||||
|
||||
ACTOR static Future<ErrorOr<GetBlobGranuleLocationsReply>> getGranuleLocations(AuthzSecurityWorkload* self,
|
||||
Database cx,
|
||||
TenantInfo tenant,
|
||||
Version v) {
|
||||
try {
|
||||
GetBlobGranuleLocationsReply reply =
|
||||
wait(basicLoadBalance(cx->getCommitProxies(UseProvisionalProxies::False),
|
||||
&CommitProxyInterface::getBlobGranuleLocations,
|
||||
GetBlobGranuleLocationsRequest(
|
||||
SpanContext(), tenant, ""_sr, Optional<KeyRef>(), 100, false, v, Arena())));
|
||||
return reply;
|
||||
} catch (Error& e) {
|
||||
if (e.code() == error_code_operation_cancelled) {
|
||||
throw e;
|
||||
}
|
||||
CODE_PROBE(e.code() == error_code_permission_denied,
|
||||
"Cross tenant blob granule locations meets permission_denied");
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
ACTOR static Future<Void> testBlobGranuleLocationLeakDisallowed(AuthzSecurityWorkload* self, Database cx) {
|
||||
state Key key = self->randomString();
|
||||
state Value value = self->randomString();
|
||||
state Version v1 =
|
||||
wait(setAndCommitKeyValueAndGetVersion(self, cx, self->tenant, self->signedToken, key, value));
|
||||
state Version v2 = wait(setAndCommitKeyValueAndGetVersion(
|
||||
self, cx, self->anotherTenant, self->signedTokenAnotherTenant, key, value));
|
||||
|
||||
state bool success = true;
|
||||
state TenantInfo tenantInfo;
|
||||
|
||||
{
|
||||
tenantInfo = TenantInfo(self->tenant->id(), self->signedToken);
|
||||
ErrorOr<GetBlobGranuleLocationsReply> rep = wait(getGranuleLocations(self, cx, tenantInfo, v2));
|
||||
bool checkSuccess = checkGranuleLocations(rep, tenantInfo);
|
||||
success &= checkSuccess;
|
||||
}
|
||||
{
|
||||
tenantInfo = TenantInfo(self->anotherTenant->id(), self->signedTokenAnotherTenant);
|
||||
ErrorOr<GetBlobGranuleLocationsReply> rep = wait(getGranuleLocations(self, cx, tenantInfo, v2));
|
||||
bool checkSuccess = checkGranuleLocations(rep, tenantInfo);
|
||||
success &= checkSuccess;
|
||||
}
|
||||
if (success) {
|
||||
++self->bgLocationLeakNegative;
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
ACTOR static Future<Optional<Error>> tryBlobGranuleRequest(AuthzSecurityWorkload* self,
|
||||
Database cx,
|
||||
Reference<Tenant> tenant,
|
||||
WipedString locToken,
|
||||
WipedString reqToken,
|
||||
Version committedVersion) {
|
||||
try {
|
||||
ErrorOr<GetBlobGranuleLocationsReply> rep =
|
||||
wait(getGranuleLocations(self, cx, TenantInfo(tenant->id(), locToken), committedVersion));
|
||||
if (rep.isError()) {
|
||||
if (rep.getError().code() == error_code_permission_denied) {
|
||||
TraceEvent(SevError, "AuthzSecurityError")
|
||||
.detail("Case", "GranuleLocBeforeRequestDisallowed")
|
||||
.log();
|
||||
}
|
||||
throw rep.getError();
|
||||
}
|
||||
|
||||
int locIdx = deterministicRandom()->randomInt(0, rep.get().results.size());
|
||||
|
||||
ASSERT(!rep.get().results.empty());
|
||||
BlobGranuleFileRequest req;
|
||||
req.arena.dependsOn(rep.get().arena);
|
||||
req.keyRange = rep.get().results[locIdx].first;
|
||||
req.tenantInfo = TenantInfo(tenant->id(), reqToken);
|
||||
req.readVersion = committedVersion;
|
||||
|
||||
auto& bwInterf = rep.get().results[locIdx].second;
|
||||
ErrorOr<BlobGranuleFileReply> fileRep = wait(bwInterf.blobGranuleFileRequest.tryGetReply(req));
|
||||
if (fileRep.isError()) {
|
||||
throw fileRep.getError();
|
||||
}
|
||||
ASSERT(!fileRep.get().chunks.empty());
|
||||
|
||||
return Optional<Error>();
|
||||
} catch (Error& e) {
|
||||
CODE_PROBE(e.code() == error_code_permission_denied,
|
||||
"Cross tenant blob granule read meets permission_denied");
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
ACTOR static Future<Optional<Error>> tryBlobGranuleRead(AuthzSecurityWorkload* self,
|
||||
Database cx,
|
||||
Reference<Tenant> tenant,
|
||||
|
@ -501,8 +646,6 @@ struct AuthzSecurityWorkload : TestWorkload {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: add separate tests to separately test blob granule locations call and blob worker interface call
|
||||
|
||||
static void checkCrossTenantOutcome(std::string testcase,
|
||||
PerfIntCounter& positiveCounter,
|
||||
PerfIntCounter& negativeCounter,
|
||||
|
@ -533,6 +676,40 @@ struct AuthzSecurityWorkload : TestWorkload {
|
|||
}
|
||||
}
|
||||
|
||||
ACTOR static Future<Void> testCrossTenantBGLocDisallowed(AuthzSecurityWorkload* self,
|
||||
Database cx,
|
||||
PositiveTestcase positive) {
|
||||
state Key key = self->randomString();
|
||||
state Value value = self->randomString();
|
||||
state Version committedVersion =
|
||||
wait(setAndCommitKeyValueAndGetVersion(self, cx, self->tenant, self->signedToken, key, value));
|
||||
TenantInfo tenantInfo(self->tenant->id(), positive ? self->signedToken : self->signedTokenAnotherTenant);
|
||||
ErrorOr<GetBlobGranuleLocationsReply> rep = wait(getGranuleLocations(self, cx, tenantInfo, committedVersion));
|
||||
Optional<Error> outcome = rep.isError() ? rep.getError() : Optional<Error>();
|
||||
checkCrossTenantOutcome(
|
||||
"BGLoc", self->crossTenantBGLocPositive, self->crossTenantBGLocNegative, outcome, positive);
|
||||
return Void();
|
||||
}
|
||||
|
||||
ACTOR static Future<Void> testCrossTenantBGRequestDisallowed(AuthzSecurityWorkload* self,
|
||||
Database cx,
|
||||
PositiveTestcase positive) {
|
||||
state Key key = self->randomString();
|
||||
state Value value = self->randomString();
|
||||
state Version committedVersion =
|
||||
wait(setAndCommitKeyValueAndGetVersion(self, cx, self->tenant, self->signedToken, key, value));
|
||||
Optional<Error> outcome =
|
||||
wait(tryBlobGranuleRequest(self,
|
||||
cx,
|
||||
self->tenant,
|
||||
self->signedToken,
|
||||
positive ? self->signedToken : self->signedTokenAnotherTenant,
|
||||
committedVersion));
|
||||
checkCrossTenantOutcome(
|
||||
"BGRequest", self->crossTenantBGReqPositive, self->crossTenantBGReqNegative, outcome, positive);
|
||||
return Void();
|
||||
}
|
||||
|
||||
ACTOR static Future<Void> testCrossTenantBGReadDisallowed(AuthzSecurityWorkload* self,
|
||||
Database cx,
|
||||
PositiveTestcase positive) {
|
||||
|
|
|
@ -21,6 +21,7 @@ clearAfterTest = false
|
|||
[[test.workload]]
|
||||
testName = 'CreateTenant'
|
||||
name = 'AnotherAuthzSecurityTenant'
|
||||
blobbify = true
|
||||
|
||||
[[test]]
|
||||
testTitle = 'AuthzSecurityCheck'
|
||||
|
@ -30,12 +31,12 @@ clearAfterTest = false
|
|||
testName = 'LeakTLogInterface'
|
||||
tenant = 'AuthzSecurityTenant'
|
||||
key = 'TLogInterface'
|
||||
testDuration = 10.0
|
||||
testDuration = 20.0
|
||||
|
||||
[[test.workload]]
|
||||
testName = 'AuthzSecurity'
|
||||
tenantA = 'AuthzSecurityTenant'
|
||||
tenantB = 'AnotherAuthzSecurityTenant'
|
||||
tLogConfigKey = 'TLogInterface'
|
||||
testDuration = 10.0
|
||||
testDuration = 20.0
|
||||
checkBlobGranules = true
|
||||
|
|
Loading…
Reference in New Issue