Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion ydb/core/config/init/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ class TDefaultNodeBrokerClient
const TGrpcSslSettings& grpcSettings,
const TString addr,
const NYdb::NDiscovery::TNodeRegistrationSettings& settings,
const TString& nodeRegistrationToken,
const IEnv& env)
{
TCommandConfig::TServerEndpoint endpoint = TCommandConfig::ParseServerAddress(addr);
Expand All @@ -242,7 +243,9 @@ class TDefaultNodeBrokerClient
config.UseClientCertificate(certificate.c_str(), privateKey.c_str());
}
}
config.SetAuthToken(BUILTIN_ACL_ROOT);
if (nodeRegistrationToken) {
config.SetAuthToken(nodeRegistrationToken);
}
config.SetEndpoint(endpoint.Address);
auto connection = NYdb::TDriver(config);

Expand Down Expand Up @@ -313,6 +316,7 @@ class TDefaultNodeBrokerClient
const TGrpcSslSettings& grpcSettings,
const TVector<TString>& addrs,
const NYdb::NDiscovery::TNodeRegistrationSettings& settings,
const TString& nodeRegistrationToken,
const IEnv& env,
IInitLogger& logger)
{
Expand All @@ -326,6 +330,7 @@ class TDefaultNodeBrokerClient
grpcSettings,
addr,
settings,
nodeRegistrationToken,
env);
if (result.IsSuccess()) {
logger.Out() << "Success. Registered via discovery service as " << result.GetNodeId() << Endl;
Expand Down Expand Up @@ -387,6 +392,7 @@ class TDefaultNodeBrokerClient
grpcSettings,
addrs,
newRegSettings,
regSettings.NodeRegistrationToken,
env,
logger);

Expand Down
1 change: 1 addition & 0 deletions ydb/core/config/init/init.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ struct TNodeRegistrationSettings {
bool FixedNodeID;
ui32 InterconnectPort;
NActors::TNodeLocation Location;
TString NodeRegistrationToken;
};

class INodeRegistrationResult {
Expand Down
1 change: 1 addition & 0 deletions ydb/core/config/init/init_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,7 @@ class TInitialConfiguratorImpl
cf.FixedNodeID,
cf.InterconnectPort,
cf.CreateNodeLocation(),
AppConfig.GetAuthConfig().GetNodeRegistrationToken(),
};

auto result = NodeBrokerClient.RegisterDynamicNode(cf.GrpcSslSettings, addrs, settings, Env, Logger);
Expand Down
2 changes: 1 addition & 1 deletion ydb/core/driver_lib/run/kikimr_services_initializers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1646,7 +1646,7 @@ void TSecurityServicesInitializer::InitializeServices(NActors::TActorSystemSetup
.AuthConfig = Config.GetAuthConfig(),
.CertificateAuthValues = {
.ClientCertificateAuthorization = Config.GetClientCertificateAuthorization(),
.ServerCertificateFilePath = grpcConfig.GetCert(),
.ServerCertificateFilePath = grpcConfig.HasPathToCertificateFile() ? grpcConfig.GetPathToCertificateFile() : grpcConfig.GetCert(),
.Domain = Config.GetAuthConfig().GetCertificateAuthenticationDomain()
}
};
Expand Down
19 changes: 16 additions & 3 deletions ydb/core/grpc_services/grpc_request_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,22 @@ void TGRpcRequestProxyImpl::HandleUndelivery(TEvents::TEvUndelivered::TPtr& ev)

bool TGRpcRequestProxyImpl::IsAuthStateOK(const IRequestProxyCtx& ctx) {
const auto& state = ctx.GetAuthState();
return state.State == NYdbGrpc::TAuthState::AS_OK ||
state.State == NYdbGrpc::TAuthState::AS_FAIL && state.NeedAuth == false ||
state.NeedAuth == false && !ctx.GetYdbToken();
if (state.State == NYdbGrpc::TAuthState::AS_OK) {
return true;
}

const bool authorizationParamsAreSet = ctx.GetYdbToken() || !ctx.FindClientCertPropertyValues().empty();
if (!state.NeedAuth && !authorizationParamsAreSet) {
return true;
}

if (!state.NeedAuth && state.State == NYdbGrpc::TAuthState::AS_FAIL) {
if (AppData()->EnforceUserTokenCheckRequirement && authorizationParamsAreSet) {
return false;
}
return true;
}
return false;
}

void TGRpcRequestProxyImpl::MaybeStartTracing(IRequestProxyCtx& ctx) {
Expand Down
19 changes: 16 additions & 3 deletions ydb/core/grpc_services/grpc_request_proxy_simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,22 @@ void TGRpcRequestProxySimple::HandleUndelivery(TEvents::TEvUndelivered::TPtr& ev

bool TGRpcRequestProxySimple::IsAuthStateOK(const IRequestProxyCtx& ctx) {
const auto& state = ctx.GetAuthState();
return state.State == NYdbGrpc::TAuthState::AS_OK ||
state.State == NYdbGrpc::TAuthState::AS_FAIL && state.NeedAuth == false ||
state.NeedAuth == false && !ctx.GetYdbToken();
if (state.State == NYdbGrpc::TAuthState::AS_OK) {
return true;
}

const bool authorizationParamsAreSet = ctx.GetYdbToken() || !ctx.FindClientCertPropertyValues().empty();
if (!state.NeedAuth && !authorizationParamsAreSet) {
return true;
}

if (!state.NeedAuth && state.State == NYdbGrpc::TAuthState::AS_FAIL) {
if (AppData()->EnforceUserTokenCheckRequirement && authorizationParamsAreSet) {
return false;
}
return true;
}
return false;
}

template<typename TEvent>
Expand Down
25 changes: 15 additions & 10 deletions ydb/core/grpc_services/rpc_whoami.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,29 @@ class TWhoAmIRPC : public TActorBootstrapped<TWhoAmIRPC> {
: Request(request)
{}

void Bootstrap(const TActorContext& ctx) {
void Bootstrap() {
//TODO: Do we realy realy need to make call to the ticket parser here???
//we have done it already in grpc_request_proxy
auto req = dynamic_cast<TEvWhoAmIRequest*>(Request.get());
Y_ABORT_UNLESS(req, "Unexpected request type for TWhoAmIRPC");
TMaybe<TString> authToken = req->GetYdbToken();
if (authToken) {
TMaybe<TString> database = Request->GetDatabaseName();
ctx.Send(MakeTicketParserID(), new TEvTicketParser::TEvAuthorizeTicket({
.Database = database ? database.GetRef() : TString(),
.Ticket = authToken.GetRef(),
.PeerName = Request->GetPeerName()
}));
Become(&TThis::StateWaitForTicket);
TString ticket;
if (TMaybe<TString> authToken = req->GetYdbToken()) {
ticket = authToken.GetRef();
} else if (TVector<TStringBuf> clientCert = Request->FindClientCert(); !clientCert.empty()) {
ticket = TString(clientCert.front());
} else {
ReplyError("No token provided");
PassAway();
return;
}

TMaybe<TString> database = Request->GetDatabaseName();
Send(MakeTicketParserID(), new TEvTicketParser::TEvAuthorizeTicket({
.Database = database ? database.GetRef() : TString(),
.Ticket = ticket,
.PeerName = Request->GetPeerName()
}));
Become(&TThis::StateWaitForTicket);
}

STFUNC(StateWaitForTicket) {
Expand Down
1 change: 1 addition & 0 deletions ydb/core/protos/auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ message TAuthConfig {
optional string AccessServiceType = 79 [default = "Yandex_v2"]; // For now the following values are supported: "Yandex_v2", "Nebius_v1"
optional string CertificateAuthenticationDomain = 80 [default = "cert"];
optional bool EnableLoginAuthentication = 81 [default = true];
optional string NodeRegistrationToken = 82 [default = "root@builtin", (Ydb.sensitive) = true];
}

message TUserRegistryConfig {
Expand Down
7 changes: 7 additions & 0 deletions ydb/core/protos/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1785,8 +1785,15 @@ message TClientCertificateAuthorization {
repeated string Suffixes = 3;
}

// Matches subject alternative names (DNS) and Common Name (CN) in certificate
message TSubjectDns {
repeated string Values = 1;
repeated string Suffixes = 2;
}

message TClientCertificateDefinition {
repeated TSubjectTerm SubjectTerms = 1;
optional TSubjectDns SubjectDns = 5;
optional bool CanCheckNodeHostByCN = 2 [default = false];
repeated string MemberGroups = 3;
optional bool RequireSameIssuer = 4 [default = true];
Expand Down
136 changes: 100 additions & 36 deletions ydb/core/security/certificate_check/cert_auth_processor.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "cert_auth_processor.h"

#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/objects.h>
Expand Down Expand Up @@ -100,6 +101,56 @@ TVector<std::pair<TString, TString>> X509CertificateReader::ReadIssuerTerms(cons
return ReadTerms(name);
}

static void FreeList(GENERAL_NAMES* list) {
sk_GENERAL_NAME_pop_free(list, GENERAL_NAME_free);
}

TVector<TString> X509CertificateReader::ReadSubjectDns(const X509Ptr& x509, const std::vector<std::pair<TString, TString>>& subjectTerms) {
TVector<TString> result;
// 1. Subject's common name (CN) must be a subject DNS name, so add it to DNS names of subject first
for (const auto& [k, v] : subjectTerms) {
if (k == "CN") {
result.emplace_back(v);
}
}

using TGeneralNamesPtr = std::unique_ptr<GENERAL_NAMES, deleter_from_fn<&FreeList>>;
TGeneralNamesPtr subjectAltNames((GENERAL_NAMES*)X509_get_ext_d2i(x509.get(), NID_subject_alt_name, NULL, NULL));
if (!subjectAltNames) {
return result;
}
const int subjectAltNamesCount = sk_GENERAL_NAME_num(subjectAltNames.get());
if (subjectAltNamesCount <= 0) {
return result;
}

result.reserve(static_cast<size_t>(subjectAltNamesCount) + result.size());
// 2. Additionally find subject alternative names with type=DNS
for (int i = 0; i < subjectAltNamesCount; ++i) {
const GENERAL_NAME* name = sk_GENERAL_NAME_value(subjectAltNames.get(), i);
if (!name) {
continue;
}
if (name->type == GEN_DNS) {
const ASN1_STRING* value = name->d.dNSName;
if (!value) {
continue;
}

const char* data = reinterpret_cast<const char*>(ASN1_STRING_get0_data(value));
if (!data) {
continue;
}
int size = ASN1_STRING_length(value);
if (size <= 0) {
continue;
}
result.emplace_back(data, static_cast<size_t>(size));
}
}
return result;
}

TString X509CertificateReader::GetFingerprint(const X509Ptr& x509) {
static constexpr size_t FINGERPRINT_LENGTH = SHA_DIGEST_LENGTH;
unsigned char fingerprint[FINGERPRINT_LENGTH];
Expand All @@ -109,14 +160,16 @@ TString X509CertificateReader::GetFingerprint(const X509Ptr& x509) {
return HexEncode(fingerprint, FINGERPRINT_LENGTH);
}

TCertificateAuthorizationParams::TCertificateAuthorizationParams(const TDN& dn, bool requireSameIssuer, const std::vector<TString>& groups)
TCertificateAuthorizationParams::TCertificateAuthorizationParams(const TDN& dn, const std::optional<TRDN>& subjectDns, bool requireSameIssuer, const std::vector<TString>& groups)
: SubjectDn(dn)
, SubjectDns(subjectDns)
, RequireSameIssuer(requireSameIssuer)
, Groups(groups)
{}

TCertificateAuthorizationParams::TCertificateAuthorizationParams(TDN&& dn, bool requireSameIssuer, std::vector<TString>&& groups)
TCertificateAuthorizationParams::TCertificateAuthorizationParams(TDN&& dn, std::optional<TRDN>&& subjectDns, bool requireSameIssuer, std::vector<TString>&& groups)
: SubjectDn(std::move(dn))
, SubjectDns(std::move(subjectDns))
, RequireSameIssuer(requireSameIssuer)
, Groups(std::move(groups))
{}
Expand All @@ -127,59 +180,44 @@ TCertificateAuthorizationParams::TDN& TCertificateAuthorizationParams::TDN::AddR
}

TCertificateAuthorizationParams::operator bool() const {
return SubjectDn;
return SubjectDn || SubjectDns;
}

bool TCertificateAuthorizationParams::CheckSubject(const std::unordered_map<TString, std::vector<TString>>& subjectDescription) const {
bool isDescriptionMatched = false;
for (const auto& rdn: SubjectDn.RDNs) {
isDescriptionMatched = false;
bool TCertificateAuthorizationParams::CheckSubject(const std::unordered_map<TString, std::vector<TString>>& subjectDescription, const std::vector<TString>& subjectDns) const {
for (const TRDN& rdn: SubjectDn.RDNs) {
auto fieldIt = subjectDescription.find(rdn.Attribute);
if (fieldIt == subjectDescription.cend()) {
break;
return false;
}

const auto& attributeValues = fieldIt->second;
bool attributeMatched = false;
for (const auto& attributeValue : attributeValues) {
attributeMatched = false;
for (const auto& value: rdn.Values) {
if (value == attributeValue) {
attributeMatched = true;
break;
}
}
if (!attributeMatched) {
for (const auto& suffix: rdn.Suffixes) {
if (attributeValue.EndsWith(suffix)) {
attributeMatched = true;
break;
}
}
}
if (!attributeMatched) {
if (!rdn.Match(attributeValues)) {
return false;
}
}

if (SubjectDns) {
bool dnsMatched = false;
for (const TString& dns : subjectDns) {
if (SubjectDns->Match(dns)) {
dnsMatched = true;
break;
}
}
if (!attributeMatched) {
isDescriptionMatched = false;
break;
if (!dnsMatched) {
return false;
}
isDescriptionMatched = true;
}

if (isDescriptionMatched) {
return true;
}
return false;
return true;
}

TCertificateAuthorizationParams::TDN::operator bool() const {
return !RDNs.empty();
}

TCertificateAuthorizationParams::TRDN::TRDN(const TString& Attribute)
:Attribute(Attribute)
TCertificateAuthorizationParams::TRDN::TRDN(const TString& attribute)
: Attribute(attribute)
{}

TCertificateAuthorizationParams::TRDN& TCertificateAuthorizationParams::TRDN::AddValue(const TString& val)
Expand All @@ -194,4 +232,30 @@ TCertificateAuthorizationParams::TRDN& TCertificateAuthorizationParams::TRDN::Ad
return *this;
}

bool TCertificateAuthorizationParams::TRDN::Match(const TString& value) const
{
for (const auto& v : Values) {
if (value == v) {
return true;
}
}
for (const auto& s : Suffixes) {
if (value.EndsWith(s)) {
return true;
}
}

return false;
}

bool TCertificateAuthorizationParams::TRDN::Match(const std::vector<TString>& values) const
{
for (const auto& value : values) {
if (!Match(value)) {
return false;
}
}
return true;
}

} //namespace NKikimr {
Loading