Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge mTLS features to stable-24-2 #7940

Merged
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
13 changes: 8 additions & 5 deletions ydb/core/driver_lib/cli_utils/cli_cmds_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,8 @@ class TClientCommandServerBase : public TClientCommand {
const TString &nodeHost,
const TString &nodeAddress,
const TString &nodeResolveHost,
const TMaybe<TString>& path) {
const TMaybe<TString>& path,
const TString& nodeRegistrationToken) {
TCommandConfig::TServerEndpoint endpoint = TCommandConfig::ParseServerAddress(addr);
NYdb::TDriverConfig config;
if (endpoint.EnableSsl.Defined()) {
Expand All @@ -957,7 +958,9 @@ class TClientCommandServerBase : public TClientCommand {
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 @@ -1028,13 +1031,13 @@ class TClientCommandServerBase : public TClientCommand {
return {};
}

NYdb::NDiscovery::TNodeRegistrationResult RegisterDynamicNodeViaDiscoveryService(const TVector<TString>& addrs, const TString& domainName) {
NYdb::NDiscovery::TNodeRegistrationResult RegisterDynamicNodeViaDiscoveryService(const TVector<TString>& addrs, const TString& domainName, const TString& nodeRegistrationToken) {
NYdb::NDiscovery::TNodeRegistrationResult result;
const size_t maxNumberRecivedCallUnimplemented = 5;
size_t currentNumberRecivedCallUnimplemented = 0;
while (!result.IsSuccess() && currentNumberRecivedCallUnimplemented < maxNumberRecivedCallUnimplemented) {
for (const auto& addr : addrs) {
result = TryToRegisterDynamicNodeViaDiscoveryService(addr, domainName, NodeHost, NodeAddress, NodeResolveHost, GetSchemePath());
result = TryToRegisterDynamicNodeViaDiscoveryService(addr, domainName, NodeHost, NodeAddress, NodeResolveHost, GetSchemePath(), nodeRegistrationToken);
if (result.IsSuccess()) {
Cout << "Success. Registered via discovery service as " << result.GetNodeId() << Endl;
Cout << "Node name: ";
Expand Down Expand Up @@ -1217,7 +1220,7 @@ class TClientCommandServerBase : public TClientCommand {
if (!NodeResolveHost)
NodeResolveHost = NodeHost;

NYdb::NDiscovery::TNodeRegistrationResult result = RegisterDynamicNodeViaDiscoveryService(addrs, domainName);
NYdb::NDiscovery::TNodeRegistrationResult result = RegisterDynamicNodeViaDiscoveryService(addrs, domainName, AppConfig.GetAuthConfig().GetNodeRegistrationToken());
if (result.IsSuccess()) {
ProcessRegistrationDynamicNodeResult(result);
} else {
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 @@ -1629,7 +1629,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
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 @@ -52,6 +52,7 @@ message TAuthConfig {
optional bool UseBuiltinDomain = 78 [default = true];
optional string CertificateAuthenticationDomain = 80 [default = "cert"];
optional bool EnableLoginAuthentication = 81 [default = true];
optional string NodeRegistrationToken = 82 [default = "root@builtin"];
}

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 @@ -1751,8 +1751,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 {
12 changes: 8 additions & 4 deletions ydb/core/security/certificate_check/cert_auth_processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ struct TCertificateAuthorizationParams {
TVector<TString> Values;
TVector<TString> Suffixes;

TRDN(const TString& Attribute);
TRDN(const TString& attribute);
TRDN& AddValue(const TString& val);
TRDN& AddSuffix(const TString& suffix);
bool Match(const std::vector<TString>& values) const;
bool Match(const TString& value) const;
};

struct TDN {
Expand All @@ -27,11 +29,11 @@ struct TCertificateAuthorizationParams {
operator bool () const;
};

TCertificateAuthorizationParams(const TDN& dn = TDN(), bool requireSameIssuer = true, const std::vector<TString>& groups = {});
TCertificateAuthorizationParams(TDN&& dn, bool requireSameIssuer = true, std::vector<TString>&& groups = {});
TCertificateAuthorizationParams(const TDN& dn = TDN(), const std::optional<TRDN>& subjectDns = std::nullopt, bool requireSameIssuer = true, const std::vector<TString>& groups = {});
TCertificateAuthorizationParams(TDN&& dn, std::optional<TRDN>&& subjectDns, bool requireSameIssuer = true, std::vector<TString>&& groups = {});

operator bool () const;
bool CheckSubject(const std::unordered_map<TString, std::vector<TString>>& subjectDescription) const;
bool CheckSubject(const std::unordered_map<TString, std::vector<TString>>& subjectDescription, const std::vector<TString>& subjectDns) const;
void SetSubjectDn(const TDN& subjectDn) {
SubjectDn = subjectDn;
}
Expand All @@ -42,6 +44,7 @@ struct TCertificateAuthorizationParams {

bool CanCheckNodeByAttributeCN = false;
TDN SubjectDn;
std::optional<TRDN> SubjectDns;
bool RequireSameIssuer = true;
std::vector<TString> Groups;
};
Expand All @@ -61,6 +64,7 @@ struct X509CertificateReader {

static X509Ptr ReadCertAsPEM(const TStringBuf& cert);
static TVector<std::pair<TString, TString>> ReadSubjectTerms(const X509Ptr& x509);
static TVector<TString> ReadSubjectDns(const X509Ptr& x509, const std::vector<std::pair<TString, TString>>& subjectTerms);
static TVector<std::pair<TString, TString>> ReadAllSubjectTerms(const X509Ptr& x509);
static TVector<std::pair<TString, TString>> ReadIssuerTerms(const X509Ptr& x509);
static TString GetFingerprint(const X509Ptr& x509);
Expand Down
Loading
Loading