Skip to content

Commit

Permalink
fstec: Add rules for password strength (ydb-platform#11963)
Browse files Browse the repository at this point in the history
Co-authored-by: azevaykin <[email protected]>
  • Loading branch information
molotkov-and and azevaykin authored Dec 11, 2024
1 parent 961df40 commit e838a62
Show file tree
Hide file tree
Showing 21 changed files with 823 additions and 1 deletion.
6 changes: 6 additions & 0 deletions ydb/core/cms/console/console__replace_yaml_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "console_audit.h"

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

UnknownFieldsCollector = new NYamlConfig::TBasicUnknownFieldsCollector;

std::vector<TString> errors;
for (auto& [_, config] : resolved.Configs) {
auto cfg = NYamlConfig::YamlToProto(
config.second,
true,
true,
UnknownFieldsCollector);
NKikimr::NConfig::EValidationResult result = NKikimr::NConfig::ValidateConfig(cfg, errors);
if (result == NKikimr::NConfig::EValidationResult::Error) {
ythrow yexception() << errors.front();
}
}

const auto& deprecatedPaths = NKikimrConfig::TAppConfig::GetReservedChildrenPaths();
Expand Down
7 changes: 7 additions & 0 deletions ydb/core/config/init/init_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <ydb/core/protos/tenant_pool.pb.h>
#include <ydb/core/protos/compile_service_config.pb.h>
#include <ydb/core/protos/cms.pb.h>
#include <ydb/core/config/validation/validators.h>
#include <ydb/library/aclib/aclib.h>
#include <ydb/library/actors/core/log_iface.h>
#include <ydb/library/yaml_config/yaml_config.h>
Expand Down Expand Up @@ -1126,6 +1127,12 @@ class TInitialConfiguratorImpl

TenantName = FillTenantPoolConfig(CommonAppOptions);

std::vector<TString> errors;
EValidationResult result = ValidateConfig(AppConfig, errors);
if (result == EValidationResult::Error) {
ythrow yexception() << errors.front();
}

Logger.Out() << "configured" << Endl;

FillData(CommonAppOptions);
Expand Down
36 changes: 36 additions & 0 deletions ydb/core/config/validation/auth_config_validator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <ydb/core/protos/auth.pb.h>
#include <vector>
#include <util/generic/string.h>
#include "validators.h"


namespace NKikimr::NConfig {
namespace {

EValidationResult ValidatePasswordComplexity(const NKikimrProto::TPasswordComplexity& passwordComplexity, std::vector<TString>&msg) {
size_t minCountOfRequiredChars = passwordComplexity.GetMinLowerCaseCount() +
passwordComplexity.GetMinUpperCaseCount() +
passwordComplexity.GetMinNumbersCount() +
passwordComplexity.GetMinSpecialCharsCount();
if (passwordComplexity.GetMinLength() < minCountOfRequiredChars) {
msg = std::vector<TString>{"password_complexity: Min length of password cannot be less than "
"total min counts of lower case chars, upper case chars, numbers and special chars"};
return EValidationResult::Error;
}
return EValidationResult::Ok;
}

} // namespace

EValidationResult ValidateAuthConfig(const NKikimrProto::TAuthConfig& authConfig, std::vector<TString>& msg) {
EValidationResult validatePasswordComplexityResult = ValidatePasswordComplexity(authConfig.GetPasswordComplexity(), msg);
if (validatePasswordComplexityResult == EValidationResult::Error) {
return EValidationResult::Error;
}
if (msg.size() > 0) {
return EValidationResult::Warn;
}
return EValidationResult::Ok;
}

} // NKikimr::NConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <library/cpp/testing/unittest/registar.h>
#include <ydb/core/config/validation/validators.h>
#include <ydb/core/protos/auth.pb.h>
#include <vector>

using namespace NKikimr::NConfig;

Y_UNIT_TEST_SUITE(AuthConfigValidation) {
Y_UNIT_TEST(AcceptValidPasswordComplexity) {
NKikimrProto::TAuthConfig authConfig;
NKikimrProto::TPasswordComplexity* validPasswordComplexity = authConfig.MutablePasswordComplexity();

validPasswordComplexity->SetMinLength(8);
validPasswordComplexity->SetMinLowerCaseCount(2);
validPasswordComplexity->SetMinUpperCaseCount(2);
validPasswordComplexity->SetMinNumbersCount(2);
validPasswordComplexity->SetMinSpecialCharsCount(2);

std::vector<TString> error;
EValidationResult result = ValidateAuthConfig(authConfig, error);
UNIT_ASSERT_EQUAL(result, EValidationResult::Ok);
UNIT_ASSERT_C(error.empty(), "Should not be errors");
}

Y_UNIT_TEST(CannotAcceptInvalidPasswordComplexity) {
NKikimrProto::TAuthConfig authConfig;
NKikimrProto::TPasswordComplexity* invalidPasswordComplexity = authConfig.MutablePasswordComplexity();

// 8 < 2 + 2 + 2 + 3
invalidPasswordComplexity->SetMinLength(8);
invalidPasswordComplexity->SetMinLowerCaseCount(2);
invalidPasswordComplexity->SetMinUpperCaseCount(2);
invalidPasswordComplexity->SetMinNumbersCount(2);
invalidPasswordComplexity->SetMinSpecialCharsCount(3);

std::vector<TString> error;
EValidationResult result = ValidateAuthConfig(authConfig, error);
UNIT_ASSERT_EQUAL(result, EValidationResult::Error);
UNIT_ASSERT_VALUES_EQUAL(error.size(), 1);
UNIT_ASSERT_STRINGS_EQUAL(error.front(), "password_complexity: Min length of password cannot be less than "
"total min counts of lower case chars, upper case chars, numbers and special chars");
}
}
9 changes: 9 additions & 0 deletions ydb/core/config/validation/auth_config_validator_ut/ya.make
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
UNITTEST_FOR(ydb/core/config/validation)

SRC(
auth_config_validator_ut.cpp
)

YQL_LAST_ABI_VERSION()

END()
14 changes: 14 additions & 0 deletions ydb/core/config/validation/validators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,18 @@ EValidationResult ValidateStaticGroup(const NKikimrConfig::TAppConfig& current,
return EValidationResult::Ok;
}

EValidationResult ValidateConfig(const NKikimrConfig::TAppConfig& config, std::vector<TString>& msg) {
if (config.HasAuthConfig()) {
NKikimr::NConfig::EValidationResult result = NKikimr::NConfig::ValidateAuthConfig(config.GetAuthConfig(), msg);
if (result == NKikimr::NConfig::EValidationResult::Error) {
return EValidationResult::Error;
}
}
if (msg.size() > 0) {
return EValidationResult::Warn;
}

return EValidationResult::Ok;
}

} // namespace NKikimr::NConfig
14 changes: 14 additions & 0 deletions ydb/core/config/validation/validators.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

#include <vector>

namespace NKikimrProto {

class TAuthConfig;

} // NKikimrProto

namespace NKikimr::NConfig {

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

EValidationResult ValidateAuthConfig(
const NKikimrProto::TAuthConfig& authConfig,
std::vector<TString>& msg);

EValidationResult ValidateConfig(
const NKikimrConfig::TAppConfig& config,
std::vector<TString>& msg);

} // namespace NKikimr::NConfig
3 changes: 2 additions & 1 deletion ydb/core/config/validation/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ LIBRARY()
SRCS(
validators.h
validators.cpp
auth_config_validator.cpp
)

PEERDIR(
Expand All @@ -13,5 +14,5 @@ END()

RECURSE_FOR_TESTS(
ut
auth_config_validator_ut
)

11 changes: 11 additions & 0 deletions ydb/core/protos/auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ message TAuthConfig {
optional string CertificateAuthenticationDomain = 80 [default = "cert"];
optional bool EnableLoginAuthentication = 81 [default = true];
optional string NodeRegistrationToken = 82 [default = "root@builtin", (Ydb.sensitive) = true];
optional TPasswordComplexity PasswordComplexity = 83;
}

message TUserRegistryConfig {
Expand Down Expand Up @@ -122,3 +123,13 @@ message TLdapAuthentication {
optional string Scheme = 11 [default = "ldap"];
optional TExtendedSettings ExtendedSettings = 12;
}

message TPasswordComplexity {
optional uint32 MinLength = 1;
optional uint32 MinLowerCaseCount = 2;
optional uint32 MinUpperCaseCount = 3;
optional uint32 MinNumbersCount = 4;
optional uint32 MinSpecialCharsCount = 5;
optional string SpecialChars = 6;
optional bool CanContainUsername = 7;
}
41 changes: 41 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
#include <ydb/core/base/tx_processing.h>
#include <ydb/core/protos/feature_flags.pb.h>
#include <ydb/core/protos/table_stats.pb.h> // for TStoragePoolsStats
#include <ydb/core/protos/auth.pb.h>
#include <ydb/core/engine/mkql_proto.h>
#include <ydb/core/sys_view/partition_stats/partition_stats.h>
#include <ydb/core/statistics/events.h>
#include <ydb/core/statistics/service/service.h>
#include <ydb/core/scheme/scheme_types_proto.h>
#include <ydb/core/tx/columnshard/bg_tasks/events/events.h>
#include <ydb/core/tx/scheme_board/events_schemeshard.h>
#include <ydb/library/login/password_checker/password_checker.h>
#include <yql/essentials/minikql/mkql_type_ops.h>
#include <yql/essentials/providers/common/proto/gateways_config.pb.h>
#include <util/random/random.h>
Expand Down Expand Up @@ -4440,6 +4442,15 @@ TSchemeShard::TSchemeShard(const TActorId &tablet, TTabletStorageInfo *info)
COUNTER_PQ_STATS_WRITTEN,
COUNTER_PQ_STATS_BATCH_LATENCY)
, AllowDataColumnForIndexTable(0, 0, 1)
, LoginProvider(NLogin::TPasswordComplexity({
.MinLength = AppData()->AuthConfig.GetPasswordComplexity().GetMinLength(),
.MinLowerCaseCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinLowerCaseCount(),
.MinUpperCaseCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinUpperCaseCount(),
.MinNumbersCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinNumbersCount(),
.MinSpecialCharsCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinSpecialCharsCount(),
.SpecialChars = AppData()->AuthConfig.GetPasswordComplexity().GetSpecialChars(),
.CanContainUsername = AppData()->AuthConfig.GetPasswordComplexity().GetCanContainUsername()
}))
{
TabletCountersPtr.Reset(new TProtobufTabletCounters<
ESimpleCounters_descriptor,
Expand Down Expand Up @@ -7128,6 +7139,10 @@ void TSchemeShard::ApplyConsoleConfigs(const NKikimrConfig::TAppConfig& appConfi
);
}

if (appConfig.HasAuthConfig()) {
ConfigureLoginProvider(appConfig.GetAuthConfig(), ctx);
}

if (IsSchemeShardConfigured()) {
StartStopCompactionQueues();
if (BackgroundCleaningQueue) {
Expand Down Expand Up @@ -7321,6 +7336,32 @@ void TSchemeShard::ConfigureBackgroundCleaningQueue(
<< ", InflightLimit# " << cleaningConfig.InflightLimit);
}

void TSchemeShard::ConfigureLoginProvider(
const ::NKikimrProto::TAuthConfig& config,
const TActorContext &ctx)
{
const auto& passwordComplexityConfig = config.GetPasswordComplexity();
NLogin::TPasswordComplexity passwordComplexity({
.MinLength = passwordComplexityConfig.GetMinLength(),
.MinLowerCaseCount = passwordComplexityConfig.GetMinLowerCaseCount(),
.MinUpperCaseCount = passwordComplexityConfig.GetMinUpperCaseCount(),
.MinNumbersCount = passwordComplexityConfig.GetMinNumbersCount(),
.MinSpecialCharsCount = passwordComplexityConfig.GetMinSpecialCharsCount(),
.SpecialChars = (passwordComplexityConfig.GetSpecialChars().empty() ? NLogin::TPasswordComplexity::VALID_SPECIAL_CHARS : passwordComplexityConfig.GetSpecialChars()),
.CanContainUsername = passwordComplexityConfig.GetCanContainUsername()
});
LoginProvider.UpdatePasswordCheckParameters(passwordComplexity);

LOG_NOTICE_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
"PasswordComplexity for LoginProvider configured: MinLength# " << passwordComplexity.MinLength
<< ", MinLowerCaseCount# " << passwordComplexity.MinLowerCaseCount
<< ", MinUpperCaseCount# " << passwordComplexity.MinUpperCaseCount
<< ", MinNumbersCount# " << passwordComplexity.MinNumbersCount
<< ", MinSpecialCharsCount# " << passwordComplexity.MinSpecialCharsCount
<< ", SpecialChars# " << (passwordComplexityConfig.GetSpecialChars().empty() ? NLogin::TPasswordComplexity::VALID_SPECIAL_CHARS : passwordComplexityConfig.GetSpecialChars())
<< ", CanContainUsername# " << (passwordComplexity.CanContainUsername ? "true" : "false"));
}

void TSchemeShard::StartStopCompactionQueues() {
// note, that we don't need to check current state of compaction queue
if (IsServerlessDomain(TPath::Init(RootPathId(), this))) {
Expand Down
4 changes: 4 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ class TSchemeShard
const NKikimrConfig::TBackgroundCleaningConfig& config,
const TActorContext &ctx);

void ConfigureLoginProvider(
const ::NKikimrProto::TAuthConfig& config,
const TActorContext &ctx);

void StartStopCompactionQueues();

void WaitForTableProfiles(ui64 importId, ui32 itemIdx);
Expand Down
Loading

0 comments on commit e838a62

Please sign in to comment.