Skip to content

Commit e838a62

Browse files
fstec: Add rules for password strength (#11963)
Co-authored-by: azevaykin <145343289+azevaykin@users.noreply.github.com>
1 parent 961df40 commit e838a62

File tree

21 files changed

+823
-1
lines changed

21 files changed

+823
-1
lines changed

ydb/core/cms/console/console__replace_yaml_config.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "console_audit.h"
44

55
#include <ydb/core/tablet_flat/tablet_flat_executed.h>
6+
#include <ydb/core/config/validation/validators.h>
67
#include <ydb/library/aclib/aclib.h>
78
#include <ydb/library/yaml_config/yaml_config.h>
89
#include <yql/essentials/public/issue/protos/issue_severity.pb.h>
@@ -100,12 +101,17 @@ class TConfigsManager::TTxReplaceYamlConfig : public TTransactionBase<TConfigsMa
100101

101102
UnknownFieldsCollector = new NYamlConfig::TBasicUnknownFieldsCollector;
102103

104+
std::vector<TString> errors;
103105
for (auto& [_, config] : resolved.Configs) {
104106
auto cfg = NYamlConfig::YamlToProto(
105107
config.second,
106108
true,
107109
true,
108110
UnknownFieldsCollector);
111+
NKikimr::NConfig::EValidationResult result = NKikimr::NConfig::ValidateConfig(cfg, errors);
112+
if (result == NKikimr::NConfig::EValidationResult::Error) {
113+
ythrow yexception() << errors.front();
114+
}
109115
}
110116

111117
const auto& deprecatedPaths = NKikimrConfig::TAppConfig::GetReservedChildrenPaths();

ydb/core/config/init/init_impl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <ydb/core/protos/tenant_pool.pb.h>
1919
#include <ydb/core/protos/compile_service_config.pb.h>
2020
#include <ydb/core/protos/cms.pb.h>
21+
#include <ydb/core/config/validation/validators.h>
2122
#include <ydb/library/aclib/aclib.h>
2223
#include <ydb/library/actors/core/log_iface.h>
2324
#include <ydb/library/yaml_config/yaml_config.h>
@@ -1126,6 +1127,12 @@ class TInitialConfiguratorImpl
11261127

11271128
TenantName = FillTenantPoolConfig(CommonAppOptions);
11281129

1130+
std::vector<TString> errors;
1131+
EValidationResult result = ValidateConfig(AppConfig, errors);
1132+
if (result == EValidationResult::Error) {
1133+
ythrow yexception() << errors.front();
1134+
}
1135+
11291136
Logger.Out() << "configured" << Endl;
11301137

11311138
FillData(CommonAppOptions);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include <ydb/core/protos/auth.pb.h>
2+
#include <vector>
3+
#include <util/generic/string.h>
4+
#include "validators.h"
5+
6+
7+
namespace NKikimr::NConfig {
8+
namespace {
9+
10+
EValidationResult ValidatePasswordComplexity(const NKikimrProto::TPasswordComplexity& passwordComplexity, std::vector<TString>&msg) {
11+
size_t minCountOfRequiredChars = passwordComplexity.GetMinLowerCaseCount() +
12+
passwordComplexity.GetMinUpperCaseCount() +
13+
passwordComplexity.GetMinNumbersCount() +
14+
passwordComplexity.GetMinSpecialCharsCount();
15+
if (passwordComplexity.GetMinLength() < minCountOfRequiredChars) {
16+
msg = std::vector<TString>{"password_complexity: Min length of password cannot be less than "
17+
"total min counts of lower case chars, upper case chars, numbers and special chars"};
18+
return EValidationResult::Error;
19+
}
20+
return EValidationResult::Ok;
21+
}
22+
23+
} // namespace
24+
25+
EValidationResult ValidateAuthConfig(const NKikimrProto::TAuthConfig& authConfig, std::vector<TString>& msg) {
26+
EValidationResult validatePasswordComplexityResult = ValidatePasswordComplexity(authConfig.GetPasswordComplexity(), msg);
27+
if (validatePasswordComplexityResult == EValidationResult::Error) {
28+
return EValidationResult::Error;
29+
}
30+
if (msg.size() > 0) {
31+
return EValidationResult::Warn;
32+
}
33+
return EValidationResult::Ok;
34+
}
35+
36+
} // NKikimr::NConfig
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <library/cpp/testing/unittest/registar.h>
2+
#include <ydb/core/config/validation/validators.h>
3+
#include <ydb/core/protos/auth.pb.h>
4+
#include <vector>
5+
6+
using namespace NKikimr::NConfig;
7+
8+
Y_UNIT_TEST_SUITE(AuthConfigValidation) {
9+
Y_UNIT_TEST(AcceptValidPasswordComplexity) {
10+
NKikimrProto::TAuthConfig authConfig;
11+
NKikimrProto::TPasswordComplexity* validPasswordComplexity = authConfig.MutablePasswordComplexity();
12+
13+
validPasswordComplexity->SetMinLength(8);
14+
validPasswordComplexity->SetMinLowerCaseCount(2);
15+
validPasswordComplexity->SetMinUpperCaseCount(2);
16+
validPasswordComplexity->SetMinNumbersCount(2);
17+
validPasswordComplexity->SetMinSpecialCharsCount(2);
18+
19+
std::vector<TString> error;
20+
EValidationResult result = ValidateAuthConfig(authConfig, error);
21+
UNIT_ASSERT_EQUAL(result, EValidationResult::Ok);
22+
UNIT_ASSERT_C(error.empty(), "Should not be errors");
23+
}
24+
25+
Y_UNIT_TEST(CannotAcceptInvalidPasswordComplexity) {
26+
NKikimrProto::TAuthConfig authConfig;
27+
NKikimrProto::TPasswordComplexity* invalidPasswordComplexity = authConfig.MutablePasswordComplexity();
28+
29+
// 8 < 2 + 2 + 2 + 3
30+
invalidPasswordComplexity->SetMinLength(8);
31+
invalidPasswordComplexity->SetMinLowerCaseCount(2);
32+
invalidPasswordComplexity->SetMinUpperCaseCount(2);
33+
invalidPasswordComplexity->SetMinNumbersCount(2);
34+
invalidPasswordComplexity->SetMinSpecialCharsCount(3);
35+
36+
std::vector<TString> error;
37+
EValidationResult result = ValidateAuthConfig(authConfig, error);
38+
UNIT_ASSERT_EQUAL(result, EValidationResult::Error);
39+
UNIT_ASSERT_VALUES_EQUAL(error.size(), 1);
40+
UNIT_ASSERT_STRINGS_EQUAL(error.front(), "password_complexity: Min length of password cannot be less than "
41+
"total min counts of lower case chars, upper case chars, numbers and special chars");
42+
}
43+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
UNITTEST_FOR(ydb/core/config/validation)
2+
3+
SRC(
4+
auth_config_validator_ut.cpp
5+
)
6+
7+
YQL_LAST_ABI_VERSION()
8+
9+
END()

ydb/core/config/validation/validators.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,18 @@ EValidationResult ValidateStaticGroup(const NKikimrConfig::TAppConfig& current,
161161
return EValidationResult::Ok;
162162
}
163163

164+
EValidationResult ValidateConfig(const NKikimrConfig::TAppConfig& config, std::vector<TString>& msg) {
165+
if (config.HasAuthConfig()) {
166+
NKikimr::NConfig::EValidationResult result = NKikimr::NConfig::ValidateAuthConfig(config.GetAuthConfig(), msg);
167+
if (result == NKikimr::NConfig::EValidationResult::Error) {
168+
return EValidationResult::Error;
169+
}
170+
}
171+
if (msg.size() > 0) {
172+
return EValidationResult::Warn;
173+
}
174+
175+
return EValidationResult::Ok;
176+
}
177+
164178
} // namespace NKikimr::NConfig

ydb/core/config/validation/validators.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
#include <vector>
66

7+
namespace NKikimrProto {
8+
9+
class TAuthConfig;
10+
11+
} // NKikimrProto
12+
713
namespace NKikimr::NConfig {
814

915
enum class EValidationResult {
@@ -32,4 +38,12 @@ EValidationResult ValidateStaticGroup(
3238
const NKikimrConfig::TAppConfig& proposed,
3339
std::vector<TString>& msg);
3440

41+
EValidationResult ValidateAuthConfig(
42+
const NKikimrProto::TAuthConfig& authConfig,
43+
std::vector<TString>& msg);
44+
45+
EValidationResult ValidateConfig(
46+
const NKikimrConfig::TAppConfig& config,
47+
std::vector<TString>& msg);
48+
3549
} // namespace NKikimr::NConfig

ydb/core/config/validation/ya.make

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ LIBRARY()
33
SRCS(
44
validators.h
55
validators.cpp
6+
auth_config_validator.cpp
67
)
78

89
PEERDIR(
@@ -13,5 +14,5 @@ END()
1314

1415
RECURSE_FOR_TESTS(
1516
ut
17+
auth_config_validator_ut
1618
)
17-

ydb/core/protos/auth.proto

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ message TAuthConfig {
5656
optional string CertificateAuthenticationDomain = 80 [default = "cert"];
5757
optional bool EnableLoginAuthentication = 81 [default = true];
5858
optional string NodeRegistrationToken = 82 [default = "root@builtin", (Ydb.sensitive) = true];
59+
optional TPasswordComplexity PasswordComplexity = 83;
5960
}
6061

6162
message TUserRegistryConfig {
@@ -122,3 +123,13 @@ message TLdapAuthentication {
122123
optional string Scheme = 11 [default = "ldap"];
123124
optional TExtendedSettings ExtendedSettings = 12;
124125
}
126+
127+
message TPasswordComplexity {
128+
optional uint32 MinLength = 1;
129+
optional uint32 MinLowerCaseCount = 2;
130+
optional uint32 MinUpperCaseCount = 3;
131+
optional uint32 MinNumbersCount = 4;
132+
optional uint32 MinSpecialCharsCount = 5;
133+
optional string SpecialChars = 6;
134+
optional bool CanContainUsername = 7;
135+
}

ydb/core/tx/schemeshard/schemeshard_impl.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
#include <ydb/core/base/tx_processing.h>
1313
#include <ydb/core/protos/feature_flags.pb.h>
1414
#include <ydb/core/protos/table_stats.pb.h> // for TStoragePoolsStats
15+
#include <ydb/core/protos/auth.pb.h>
1516
#include <ydb/core/engine/mkql_proto.h>
1617
#include <ydb/core/sys_view/partition_stats/partition_stats.h>
1718
#include <ydb/core/statistics/events.h>
1819
#include <ydb/core/statistics/service/service.h>
1920
#include <ydb/core/scheme/scheme_types_proto.h>
2021
#include <ydb/core/tx/columnshard/bg_tasks/events/events.h>
2122
#include <ydb/core/tx/scheme_board/events_schemeshard.h>
23+
#include <ydb/library/login/password_checker/password_checker.h>
2224
#include <yql/essentials/minikql/mkql_type_ops.h>
2325
#include <yql/essentials/providers/common/proto/gateways_config.pb.h>
2426
#include <util/random/random.h>
@@ -4440,6 +4442,15 @@ TSchemeShard::TSchemeShard(const TActorId &tablet, TTabletStorageInfo *info)
44404442
COUNTER_PQ_STATS_WRITTEN,
44414443
COUNTER_PQ_STATS_BATCH_LATENCY)
44424444
, AllowDataColumnForIndexTable(0, 0, 1)
4445+
, LoginProvider(NLogin::TPasswordComplexity({
4446+
.MinLength = AppData()->AuthConfig.GetPasswordComplexity().GetMinLength(),
4447+
.MinLowerCaseCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinLowerCaseCount(),
4448+
.MinUpperCaseCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinUpperCaseCount(),
4449+
.MinNumbersCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinNumbersCount(),
4450+
.MinSpecialCharsCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinSpecialCharsCount(),
4451+
.SpecialChars = AppData()->AuthConfig.GetPasswordComplexity().GetSpecialChars(),
4452+
.CanContainUsername = AppData()->AuthConfig.GetPasswordComplexity().GetCanContainUsername()
4453+
}))
44434454
{
44444455
TabletCountersPtr.Reset(new TProtobufTabletCounters<
44454456
ESimpleCounters_descriptor,
@@ -7128,6 +7139,10 @@ void TSchemeShard::ApplyConsoleConfigs(const NKikimrConfig::TAppConfig& appConfi
71287139
);
71297140
}
71307141

7142+
if (appConfig.HasAuthConfig()) {
7143+
ConfigureLoginProvider(appConfig.GetAuthConfig(), ctx);
7144+
}
7145+
71317146
if (IsSchemeShardConfigured()) {
71327147
StartStopCompactionQueues();
71337148
if (BackgroundCleaningQueue) {
@@ -7321,6 +7336,32 @@ void TSchemeShard::ConfigureBackgroundCleaningQueue(
73217336
<< ", InflightLimit# " << cleaningConfig.InflightLimit);
73227337
}
73237338

7339+
void TSchemeShard::ConfigureLoginProvider(
7340+
const ::NKikimrProto::TAuthConfig& config,
7341+
const TActorContext &ctx)
7342+
{
7343+
const auto& passwordComplexityConfig = config.GetPasswordComplexity();
7344+
NLogin::TPasswordComplexity passwordComplexity({
7345+
.MinLength = passwordComplexityConfig.GetMinLength(),
7346+
.MinLowerCaseCount = passwordComplexityConfig.GetMinLowerCaseCount(),
7347+
.MinUpperCaseCount = passwordComplexityConfig.GetMinUpperCaseCount(),
7348+
.MinNumbersCount = passwordComplexityConfig.GetMinNumbersCount(),
7349+
.MinSpecialCharsCount = passwordComplexityConfig.GetMinSpecialCharsCount(),
7350+
.SpecialChars = (passwordComplexityConfig.GetSpecialChars().empty() ? NLogin::TPasswordComplexity::VALID_SPECIAL_CHARS : passwordComplexityConfig.GetSpecialChars()),
7351+
.CanContainUsername = passwordComplexityConfig.GetCanContainUsername()
7352+
});
7353+
LoginProvider.UpdatePasswordCheckParameters(passwordComplexity);
7354+
7355+
LOG_NOTICE_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
7356+
"PasswordComplexity for LoginProvider configured: MinLength# " << passwordComplexity.MinLength
7357+
<< ", MinLowerCaseCount# " << passwordComplexity.MinLowerCaseCount
7358+
<< ", MinUpperCaseCount# " << passwordComplexity.MinUpperCaseCount
7359+
<< ", MinNumbersCount# " << passwordComplexity.MinNumbersCount
7360+
<< ", MinSpecialCharsCount# " << passwordComplexity.MinSpecialCharsCount
7361+
<< ", SpecialChars# " << (passwordComplexityConfig.GetSpecialChars().empty() ? NLogin::TPasswordComplexity::VALID_SPECIAL_CHARS : passwordComplexityConfig.GetSpecialChars())
7362+
<< ", CanContainUsername# " << (passwordComplexity.CanContainUsername ? "true" : "false"));
7363+
}
7364+
73247365
void TSchemeShard::StartStopCompactionQueues() {
73257366
// note, that we don't need to check current state of compaction queue
73267367
if (IsServerlessDomain(TPath::Init(RootPathId(), this))) {

0 commit comments

Comments
 (0)