Skip to content

Commit 54e5a53

Browse files
authored
Merge pull request #7940 from UgnineSirdis/stable-24-2-mtls-features
Merge mTLS features to stable-24-2
2 parents 3e82d9a + e870050 commit 54e5a53

File tree

15 files changed

+471
-80
lines changed

15 files changed

+471
-80
lines changed

ydb/core/driver_lib/cli_utils/cli_cmds_server.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,8 @@ class TClientCommandServerBase : public TClientCommand {
944944
const TString &nodeHost,
945945
const TString &nodeAddress,
946946
const TString &nodeResolveHost,
947-
const TMaybe<TString>& path) {
947+
const TMaybe<TString>& path,
948+
const TString& nodeRegistrationToken) {
948949
TCommandConfig::TServerEndpoint endpoint = TCommandConfig::ParseServerAddress(addr);
949950
NYdb::TDriverConfig config;
950951
if (endpoint.EnableSsl.Defined()) {
@@ -957,7 +958,9 @@ class TClientCommandServerBase : public TClientCommand {
957958
config.UseClientCertificate(certificate.c_str(), privateKey.c_str());
958959
}
959960
}
960-
config.SetAuthToken(BUILTIN_ACL_ROOT);
961+
if (nodeRegistrationToken) {
962+
config.SetAuthToken(nodeRegistrationToken);
963+
}
961964
config.SetEndpoint(endpoint.Address);
962965
auto connection = NYdb::TDriver(config);
963966

@@ -1028,13 +1031,13 @@ class TClientCommandServerBase : public TClientCommand {
10281031
return {};
10291032
}
10301033

1031-
NYdb::NDiscovery::TNodeRegistrationResult RegisterDynamicNodeViaDiscoveryService(const TVector<TString>& addrs, const TString& domainName) {
1034+
NYdb::NDiscovery::TNodeRegistrationResult RegisterDynamicNodeViaDiscoveryService(const TVector<TString>& addrs, const TString& domainName, const TString& nodeRegistrationToken) {
10321035
NYdb::NDiscovery::TNodeRegistrationResult result;
10331036
const size_t maxNumberRecivedCallUnimplemented = 5;
10341037
size_t currentNumberRecivedCallUnimplemented = 0;
10351038
while (!result.IsSuccess() && currentNumberRecivedCallUnimplemented < maxNumberRecivedCallUnimplemented) {
10361039
for (const auto& addr : addrs) {
1037-
result = TryToRegisterDynamicNodeViaDiscoveryService(addr, domainName, NodeHost, NodeAddress, NodeResolveHost, GetSchemePath());
1040+
result = TryToRegisterDynamicNodeViaDiscoveryService(addr, domainName, NodeHost, NodeAddress, NodeResolveHost, GetSchemePath(), nodeRegistrationToken);
10381041
if (result.IsSuccess()) {
10391042
Cout << "Success. Registered via discovery service as " << result.GetNodeId() << Endl;
10401043
Cout << "Node name: ";
@@ -1217,7 +1220,7 @@ class TClientCommandServerBase : public TClientCommand {
12171220
if (!NodeResolveHost)
12181221
NodeResolveHost = NodeHost;
12191222

1220-
NYdb::NDiscovery::TNodeRegistrationResult result = RegisterDynamicNodeViaDiscoveryService(addrs, domainName);
1223+
NYdb::NDiscovery::TNodeRegistrationResult result = RegisterDynamicNodeViaDiscoveryService(addrs, domainName, AppConfig.GetAuthConfig().GetNodeRegistrationToken());
12211224
if (result.IsSuccess()) {
12221225
ProcessRegistrationDynamicNodeResult(result);
12231226
} else {

ydb/core/driver_lib/run/kikimr_services_initializers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,7 @@ void TSecurityServicesInitializer::InitializeServices(NActors::TActorSystemSetup
16291629
.AuthConfig = Config.GetAuthConfig(),
16301630
.CertificateAuthValues = {
16311631
.ClientCertificateAuthorization = Config.GetClientCertificateAuthorization(),
1632-
.ServerCertificateFilePath = grpcConfig.GetCert(),
1632+
.ServerCertificateFilePath = grpcConfig.HasPathToCertificateFile() ? grpcConfig.GetPathToCertificateFile() : grpcConfig.GetCert(),
16331633
.Domain = Config.GetAuthConfig().GetCertificateAuthenticationDomain()
16341634
}
16351635
};

ydb/core/grpc_services/rpc_whoami.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,29 @@ class TWhoAmIRPC : public TActorBootstrapped<TWhoAmIRPC> {
2121
: Request(request)
2222
{}
2323

24-
void Bootstrap(const TActorContext& ctx) {
24+
void Bootstrap() {
2525
//TODO: Do we realy realy need to make call to the ticket parser here???
2626
//we have done it already in grpc_request_proxy
2727
auto req = dynamic_cast<TEvWhoAmIRequest*>(Request.get());
2828
Y_ABORT_UNLESS(req, "Unexpected request type for TWhoAmIRPC");
29-
TMaybe<TString> authToken = req->GetYdbToken();
30-
if (authToken) {
31-
TMaybe<TString> database = Request->GetDatabaseName();
32-
ctx.Send(MakeTicketParserID(), new TEvTicketParser::TEvAuthorizeTicket({
33-
.Database = database ? database.GetRef() : TString(),
34-
.Ticket = authToken.GetRef(),
35-
.PeerName = Request->GetPeerName()
36-
}));
37-
Become(&TThis::StateWaitForTicket);
29+
TString ticket;
30+
if (TMaybe<TString> authToken = req->GetYdbToken()) {
31+
ticket = authToken.GetRef();
32+
} else if (TVector<TStringBuf> clientCert = Request->FindClientCert(); !clientCert.empty()) {
33+
ticket = TString(clientCert.front());
3834
} else {
3935
ReplyError("No token provided");
4036
PassAway();
37+
return;
4138
}
39+
40+
TMaybe<TString> database = Request->GetDatabaseName();
41+
Send(MakeTicketParserID(), new TEvTicketParser::TEvAuthorizeTicket({
42+
.Database = database ? database.GetRef() : TString(),
43+
.Ticket = ticket,
44+
.PeerName = Request->GetPeerName()
45+
}));
46+
Become(&TThis::StateWaitForTicket);
4247
}
4348

4449
STFUNC(StateWaitForTicket) {

ydb/core/protos/auth.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ message TAuthConfig {
5252
optional bool UseBuiltinDomain = 78 [default = true];
5353
optional string CertificateAuthenticationDomain = 80 [default = "cert"];
5454
optional bool EnableLoginAuthentication = 81 [default = true];
55+
optional string NodeRegistrationToken = 82 [default = "root@builtin"];
5556
}
5657

5758
message TUserRegistryConfig {

ydb/core/protos/config.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,8 +1751,15 @@ message TClientCertificateAuthorization {
17511751
repeated string Suffixes = 3;
17521752
}
17531753

1754+
// Matches subject alternative names (DNS) and Common Name (CN) in certificate
1755+
message TSubjectDns {
1756+
repeated string Values = 1;
1757+
repeated string Suffixes = 2;
1758+
}
1759+
17541760
message TClientCertificateDefinition {
17551761
repeated TSubjectTerm SubjectTerms = 1;
1762+
optional TSubjectDns SubjectDns = 5;
17561763
optional bool CanCheckNodeHostByCN = 2 [default = false];
17571764
repeated string MemberGroups = 3;
17581765
optional bool RequireSameIssuer = 4 [default = true];

ydb/core/security/certificate_check/cert_auth_processor.cpp

Lines changed: 100 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "cert_auth_processor.h"
22

33
#include <openssl/x509.h>
4+
#include <openssl/x509v3.h>
45
#include <openssl/pem.h>
56
#include <openssl/bio.h>
67
#include <openssl/objects.h>
@@ -100,6 +101,56 @@ TVector<std::pair<TString, TString>> X509CertificateReader::ReadIssuerTerms(cons
100101
return ReadTerms(name);
101102
}
102103

104+
static void FreeList(GENERAL_NAMES* list) {
105+
sk_GENERAL_NAME_pop_free(list, GENERAL_NAME_free);
106+
}
107+
108+
TVector<TString> X509CertificateReader::ReadSubjectDns(const X509Ptr& x509, const std::vector<std::pair<TString, TString>>& subjectTerms) {
109+
TVector<TString> result;
110+
// 1. Subject's common name (CN) must be a subject DNS name, so add it to DNS names of subject first
111+
for (const auto& [k, v] : subjectTerms) {
112+
if (k == "CN") {
113+
result.emplace_back(v);
114+
}
115+
}
116+
117+
using TGeneralNamesPtr = std::unique_ptr<GENERAL_NAMES, deleter_from_fn<&FreeList>>;
118+
TGeneralNamesPtr subjectAltNames((GENERAL_NAMES*)X509_get_ext_d2i(x509.get(), NID_subject_alt_name, NULL, NULL));
119+
if (!subjectAltNames) {
120+
return result;
121+
}
122+
const int subjectAltNamesCount = sk_GENERAL_NAME_num(subjectAltNames.get());
123+
if (subjectAltNamesCount <= 0) {
124+
return result;
125+
}
126+
127+
result.reserve(static_cast<size_t>(subjectAltNamesCount) + result.size());
128+
// 2. Additionally find subject alternative names with type=DNS
129+
for (int i = 0; i < subjectAltNamesCount; ++i) {
130+
const GENERAL_NAME* name = sk_GENERAL_NAME_value(subjectAltNames.get(), i);
131+
if (!name) {
132+
continue;
133+
}
134+
if (name->type == GEN_DNS) {
135+
const ASN1_STRING* value = name->d.dNSName;
136+
if (!value) {
137+
continue;
138+
}
139+
140+
const char* data = reinterpret_cast<const char*>(ASN1_STRING_get0_data(value));
141+
if (!data) {
142+
continue;
143+
}
144+
int size = ASN1_STRING_length(value);
145+
if (size <= 0) {
146+
continue;
147+
}
148+
result.emplace_back(data, static_cast<size_t>(size));
149+
}
150+
}
151+
return result;
152+
}
153+
103154
TString X509CertificateReader::GetFingerprint(const X509Ptr& x509) {
104155
static constexpr size_t FINGERPRINT_LENGTH = SHA_DIGEST_LENGTH;
105156
unsigned char fingerprint[FINGERPRINT_LENGTH];
@@ -109,14 +160,16 @@ TString X509CertificateReader::GetFingerprint(const X509Ptr& x509) {
109160
return HexEncode(fingerprint, FINGERPRINT_LENGTH);
110161
}
111162

112-
TCertificateAuthorizationParams::TCertificateAuthorizationParams(const TDN& dn, bool requireSameIssuer, const std::vector<TString>& groups)
163+
TCertificateAuthorizationParams::TCertificateAuthorizationParams(const TDN& dn, const std::optional<TRDN>& subjectDns, bool requireSameIssuer, const std::vector<TString>& groups)
113164
: SubjectDn(dn)
165+
, SubjectDns(subjectDns)
114166
, RequireSameIssuer(requireSameIssuer)
115167
, Groups(groups)
116168
{}
117169

118-
TCertificateAuthorizationParams::TCertificateAuthorizationParams(TDN&& dn, bool requireSameIssuer, std::vector<TString>&& groups)
170+
TCertificateAuthorizationParams::TCertificateAuthorizationParams(TDN&& dn, std::optional<TRDN>&& subjectDns, bool requireSameIssuer, std::vector<TString>&& groups)
119171
: SubjectDn(std::move(dn))
172+
, SubjectDns(std::move(subjectDns))
120173
, RequireSameIssuer(requireSameIssuer)
121174
, Groups(std::move(groups))
122175
{}
@@ -127,59 +180,44 @@ TCertificateAuthorizationParams::TDN& TCertificateAuthorizationParams::TDN::AddR
127180
}
128181

129182
TCertificateAuthorizationParams::operator bool() const {
130-
return SubjectDn;
183+
return SubjectDn || SubjectDns;
131184
}
132185

133-
bool TCertificateAuthorizationParams::CheckSubject(const std::unordered_map<TString, std::vector<TString>>& subjectDescription) const {
134-
bool isDescriptionMatched = false;
135-
for (const auto& rdn: SubjectDn.RDNs) {
136-
isDescriptionMatched = false;
186+
bool TCertificateAuthorizationParams::CheckSubject(const std::unordered_map<TString, std::vector<TString>>& subjectDescription, const std::vector<TString>& subjectDns) const {
187+
for (const TRDN& rdn: SubjectDn.RDNs) {
137188
auto fieldIt = subjectDescription.find(rdn.Attribute);
138189
if (fieldIt == subjectDescription.cend()) {
139-
break;
190+
return false;
140191
}
141192

142193
const auto& attributeValues = fieldIt->second;
143-
bool attributeMatched = false;
144-
for (const auto& attributeValue : attributeValues) {
145-
attributeMatched = false;
146-
for (const auto& value: rdn.Values) {
147-
if (value == attributeValue) {
148-
attributeMatched = true;
149-
break;
150-
}
151-
}
152-
if (!attributeMatched) {
153-
for (const auto& suffix: rdn.Suffixes) {
154-
if (attributeValue.EndsWith(suffix)) {
155-
attributeMatched = true;
156-
break;
157-
}
158-
}
159-
}
160-
if (!attributeMatched) {
194+
if (!rdn.Match(attributeValues)) {
195+
return false;
196+
}
197+
}
198+
199+
if (SubjectDns) {
200+
bool dnsMatched = false;
201+
for (const TString& dns : subjectDns) {
202+
if (SubjectDns->Match(dns)) {
203+
dnsMatched = true;
161204
break;
162205
}
163206
}
164-
if (!attributeMatched) {
165-
isDescriptionMatched = false;
166-
break;
207+
if (!dnsMatched) {
208+
return false;
167209
}
168-
isDescriptionMatched = true;
169210
}
170211

171-
if (isDescriptionMatched) {
172-
return true;
173-
}
174-
return false;
212+
return true;
175213
}
176214

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

181-
TCertificateAuthorizationParams::TRDN::TRDN(const TString& Attribute)
182-
:Attribute(Attribute)
219+
TCertificateAuthorizationParams::TRDN::TRDN(const TString& attribute)
220+
: Attribute(attribute)
183221
{}
184222

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

235+
bool TCertificateAuthorizationParams::TRDN::Match(const TString& value) const
236+
{
237+
for (const auto& v : Values) {
238+
if (value == v) {
239+
return true;
240+
}
241+
}
242+
for (const auto& s : Suffixes) {
243+
if (value.EndsWith(s)) {
244+
return true;
245+
}
246+
}
247+
248+
return false;
249+
}
250+
251+
bool TCertificateAuthorizationParams::TRDN::Match(const std::vector<TString>& values) const
252+
{
253+
for (const auto& value : values) {
254+
if (!Match(value)) {
255+
return false;
256+
}
257+
}
258+
return true;
259+
}
260+
197261
} //namespace NKikimr {

ydb/core/security/certificate_check/cert_auth_processor.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ struct TCertificateAuthorizationParams {
1515
TVector<TString> Values;
1616
TVector<TString> Suffixes;
1717

18-
TRDN(const TString& Attribute);
18+
TRDN(const TString& attribute);
1919
TRDN& AddValue(const TString& val);
2020
TRDN& AddSuffix(const TString& suffix);
21+
bool Match(const std::vector<TString>& values) const;
22+
bool Match(const TString& value) const;
2123
};
2224

2325
struct TDN {
@@ -27,11 +29,11 @@ struct TCertificateAuthorizationParams {
2729
operator bool () const;
2830
};
2931

30-
TCertificateAuthorizationParams(const TDN& dn = TDN(), bool requireSameIssuer = true, const std::vector<TString>& groups = {});
31-
TCertificateAuthorizationParams(TDN&& dn, bool requireSameIssuer = true, std::vector<TString>&& groups = {});
32+
TCertificateAuthorizationParams(const TDN& dn = TDN(), const std::optional<TRDN>& subjectDns = std::nullopt, bool requireSameIssuer = true, const std::vector<TString>& groups = {});
33+
TCertificateAuthorizationParams(TDN&& dn, std::optional<TRDN>&& subjectDns, bool requireSameIssuer = true, std::vector<TString>&& groups = {});
3234

3335
operator bool () const;
34-
bool CheckSubject(const std::unordered_map<TString, std::vector<TString>>& subjectDescription) const;
36+
bool CheckSubject(const std::unordered_map<TString, std::vector<TString>>& subjectDescription, const std::vector<TString>& subjectDns) const;
3537
void SetSubjectDn(const TDN& subjectDn) {
3638
SubjectDn = subjectDn;
3739
}
@@ -42,6 +44,7 @@ struct TCertificateAuthorizationParams {
4244

4345
bool CanCheckNodeByAttributeCN = false;
4446
TDN SubjectDn;
47+
std::optional<TRDN> SubjectDns;
4548
bool RequireSameIssuer = true;
4649
std::vector<TString> Groups;
4750
};
@@ -61,6 +64,7 @@ struct X509CertificateReader {
6164

6265
static X509Ptr ReadCertAsPEM(const TStringBuf& cert);
6366
static TVector<std::pair<TString, TString>> ReadSubjectTerms(const X509Ptr& x509);
67+
static TVector<TString> ReadSubjectDns(const X509Ptr& x509, const std::vector<std::pair<TString, TString>>& subjectTerms);
6468
static TVector<std::pair<TString, TString>> ReadAllSubjectTerms(const X509Ptr& x509);
6569
static TVector<std::pair<TString, TString>> ReadIssuerTerms(const X509Ptr& x509);
6670
static TString GetFingerprint(const X509Ptr& x509);

0 commit comments

Comments
 (0)