Skip to content

Commit c3cd0d2

Browse files
committed
Add healthcheck (format=prometheus) custom auth
1 parent b83612c commit c3cd0d2

File tree

6 files changed

+76
-9
lines changed

6 files changed

+76
-9
lines changed

ydb/core/driver_lib/run/run.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -621,11 +621,15 @@ void TKikimrRunner::InitializeMonitoring(const TKikimrRunConfig& runConfig, bool
621621
monConfig.PrivateKeyFile = appConfig.GetMonitoringConfig().GetMonitoringPrivateKeyFile();
622622
monConfig.RedirectMainPageTo = appConfig.GetMonitoringConfig().GetRedirectMainPageTo();
623623
monConfig.RequireCountersAuthentication = appConfig.GetMonitoringConfig().GetRequireCountersAuthentication();
624+
monConfig.RequireHealthcheckAuthentication = appConfig.GetMonitoringConfig().GetRequireHealthcheckAuthentication();
624625

625-
// custom monitoring authentication may be enabled only if enforce_user_token_requirement is enabled
626+
// Custom monitoring authentication may be enabled only if user authentication is mandatory
626627
const auto& securityConfig(runConfig.AppConfig.GetDomainsConfig().GetSecurityConfig());
627628
const bool enforceUserToken = securityConfig.GetEnforceUserTokenRequirement();
628-
Y_ABORT_UNLESS(enforceUserToken || !monConfig.RequireCountersAuthentication, "Setting EnforceUserTokenRequirement is disabled, but RequireCountersAuthentication is enabled");
629+
Y_ABORT_UNLESS(enforceUserToken || !monConfig.RequireCountersAuthentication,
630+
"Setting EnforceUserTokenRequirement is disabled, but RequireCountersAuthentication is enabled");
631+
Y_ABORT_UNLESS(enforceUserToken || !monConfig.RequireHealthcheckAuthentication,
632+
"Setting EnforceUserTokenRequirement is disabled, but RequireHealthcheckAuthentication is enabled");
629633

630634
if (includeHostName) {
631635
if (appConfig.HasNameserviceConfig() && appConfig.GetNameserviceConfig().NodeSize() > 0) {

ydb/core/mon/mon.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ TVector<TString> TMon::GetCountersAllowedSIDs(TActorSystem* actorSystem) {
167167
return countersAllowedSIDs;
168168
}
169169
const auto& securityConfig = appData->DomainsConfig.GetSecurityConfig();
170-
auto addSIDs = [&countersAllowedSIDs](const auto& protoAllowedSIDs) {
171-
for (const auto& sid : protoAllowedSIDs) {
170+
const auto addSIDs = [&countersAllowedSIDs](const auto& allowedSIDs) {
171+
for (const auto& sid : allowedSIDs) {
172172
countersAllowedSIDs.emplace_back(sid);
173173
}
174174
};
@@ -1680,12 +1680,11 @@ std::future<void> TMon::Start(TActorSystem* actorSystem) {
16801680
ActorSystem->RegisterLocalService(NodeProxyServiceActorId, nodeProxyActorId);
16811681
IActor* countersMonPageServiceActor;
16821682
if (Config.RequireCountersAuthentication) {
1683-
const auto countersAllowedSIDs = GetCountersAllowedSIDs(ActorSystem);
16841683
countersMonPageServiceActor = new THttpMonPageService(
16851684
HttpProxyActorId,
16861685
CountersMonPage,
16871686
Config.Authorizer,
1688-
countersAllowedSIDs);
1687+
GetCountersAllowedSIDs(ActorSystem));
16891688
} else {
16901689
countersMonPageServiceActor = new THttpMonPageService(
16911690
HttpProxyActorId,

ydb/core/protos/config.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ message TMonitoringConfig {
620620
optional string InactivityTimeout = 18 [default = "2m"];
621621
optional bool HideHttpEndpoint = 19 [default = false];
622622
optional bool RequireCountersAuthentication = 21 [default = false];
623+
optional bool RequireHealthcheckAuthentication = 22 [default = false];
623624
}
624625

625626
message TRestartsCountConfig {

ydb/core/viewer/viewer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class TViewer : public TActorBootstrapped<TViewer>, public IViewer {
140140
.ActorSystem = ctx.ActorSystem(),
141141
.ActorId = ctx.SelfID,
142142
.AuthMode = enforceUserToken ? TMon::EAuthMode::ExtractOnly : TMon::EAuthMode::Disabled,
143-
.AllowedSIDs = enforceUserToken ? databaseAllowedSIDs : TVector<TString>(),
143+
.AllowedSIDs = enforceUserToken ? viewerAllowedSIDs : TVector<TString>(),
144144
});
145145
mon->RegisterActorPage({
146146
.RelPath = "vdisk",

ydb/core/viewer/viewer_healthcheck.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class TJsonHealthCheck : public TViewerPipeClient {
8989
if (Database.empty()) {
9090
Database = Params.Get("tenant");
9191
}
92-
if (!IsDatabaseRequest() && Format != HealthCheckResponseFormat::PROMETHEUS && !Viewer->CheckAccessMonitoring(GetRequest())) {
92+
if (!IsDatabaseRequest() && !CheckAccess()) {
9393
return TBase::ReplyAndPassAway(GetHTTPFORBIDDEN("text/plain", "Access denied"));
9494
}
9595
Cache = FromStringWithDefault<bool>(Params.Get("cache"), Cache);
@@ -107,6 +107,21 @@ class TJsonHealthCheck : public TViewerPipeClient {
107107
Become(&TThis::StateRequestedInfo, Timeout, new TEvents::TEvWakeup());
108108
}
109109

110+
bool CheckAccess() const {
111+
if (Format == HealthCheckResponseFormat::PROMETHEUS) {
112+
// This format was left without any authentication checks for a long time,
113+
// so we check access for it only when it's required with a separate flag.
114+
const auto& config = Viewer->GetKikimrRunConfig();
115+
const auto requireHealthcheckAuth = config.AppConfig.GetMonitoringConfig().GetRequireHealthcheckAuthentication();
116+
// We want metrics collection systems to have minimal permissions, so we check for viewer access
117+
return !requireHealthcheckAuth || Viewer->CheckAccessViewer(GetRequest());
118+
}
119+
120+
// But the general healthcheck info should not be accessible for those
121+
// who have only viewer grants so we check for the monitoring grants.
122+
return Viewer->CheckAccessMonitoring(GetRequest());
123+
}
124+
110125
STFUNC(StateRequestedInfo) {
111126
switch (ev->GetTypeRewrite()) {
112127
hFunc(NHealthCheck::TEvSelfCheckResult, Handle);

ydb/tests/functional/security/test_mon_endpoints_auth.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def create_ydb_configurator(
114114
certificates,
115115
enforce_user_token_requirement=True,
116116
require_counters_authentication=None,
117+
require_healthcheck_authentication=None,
117118
database_allowed_sids=['database@builtin'],
118119
viewer_allowed_sids=['viewer@builtin'],
119120
monitoring_allowed_sids=['monitoring@builtin'],
@@ -132,13 +133,17 @@ def create_ydb_configurator(
132133
config_generator.monitoring_tls_cert_path = certificates['server_cert']
133134
config_generator.monitoring_tls_key_path = certificates['server_key']
134135

135-
if require_counters_authentication is not None:
136+
if require_counters_authentication is not None or require_healthcheck_authentication is not None:
136137
if 'monitoring_config' not in config_generator.yaml_config:
137138
config_generator.yaml_config['monitoring_config'] = {}
138139
if require_counters_authentication is not None:
139140
config_generator.yaml_config['monitoring_config'][
140141
'require_counters_authentication'
141142
] = require_counters_authentication
143+
if require_healthcheck_authentication is not None:
144+
config_generator.yaml_config['monitoring_config'][
145+
'require_healthcheck_authentication'
146+
] = require_healthcheck_authentication
142147

143148
if database_allowed_sids is not None:
144149
config_generator.yaml_config['domains_config']['security_config'][
@@ -201,6 +206,19 @@ def ydb_cluster_with_require_counters_auth(certificates):
201206
cluster.stop()
202207

203208

209+
@pytest.fixture(scope='module')
210+
def ydb_cluster_with_require_healthcheck_auth(certificates):
211+
configurator = create_ydb_configurator(
212+
certificates,
213+
enforce_user_token_requirement=True,
214+
require_healthcheck_authentication=True,
215+
)
216+
cluster = KiKiMR(configurator)
217+
cluster.start()
218+
yield cluster
219+
cluster.stop()
220+
221+
204222
EXPECTED_RESULTS_WITH_ENFORCE_USER_TOKEN = {
205223
'/counters': {
206224
None: 200,
@@ -479,3 +497,33 @@ def test_with_require_counters_authentication(ydb_cluster_with_require_counters_
479497
}, # checks this just in case
480498
}
481499
_test_endpoints(ydb_cluster_with_require_counters_auth, EXPECTED_RESULTS_WITH_REQUIRE_COUNTERS_AUTH)
500+
501+
502+
def test_with_require_healthcheck_authentication(ydb_cluster_with_require_healthcheck_auth):
503+
EXPECTED_RESULTS_WITH_REQUIRE_HEALTHCHECK_AUTH = {
504+
'/healthcheck?format=prometheus': {
505+
None: 403,
506+
'user@builtin': 403,
507+
'database@builtin': 403,
508+
'viewer@builtin': 200,
509+
'monitoring@builtin': 200,
510+
'root@builtin': 200,
511+
},
512+
'/healthcheck': {
513+
None: 403,
514+
'user@builtin': 403,
515+
'database@builtin': 403,
516+
'viewer@builtin': 403,
517+
'monitoring@builtin': 200,
518+
'root@builtin': 200,
519+
},
520+
'/ping': {
521+
None: 200,
522+
'user@builtin': 200,
523+
'database@builtin': 200,
524+
'viewer@builtin': 200,
525+
'monitoring@builtin': 200,
526+
'root@builtin': 200,
527+
}, # checks this just in case
528+
}
529+
_test_endpoints(ydb_cluster_with_require_healthcheck_auth, EXPECTED_RESULTS_WITH_REQUIRE_HEALTHCHECK_AUTH)

0 commit comments

Comments
 (0)