diff options
author | molotkov-and <[email protected]> | 2022-11-18 12:50:29 +0300 |
---|---|---|
committer | molotkov-and <[email protected]> | 2022-11-18 12:50:29 +0300 |
commit | a45acb262bfb6f7d06d70f9f04a763d61e811966 (patch) | |
tree | 2e4eacaa4f1ecfba0bea894abf4888b7718712ad | |
parent | d563b5b3a6578243440353c5cfafdb56cc05fff3 (diff) |
Authorization of registration node
74 files changed, 1725 insertions, 111 deletions
diff --git a/library/cpp/grpc/client/grpc_common.h b/library/cpp/grpc/client/grpc_common.h index ffcdafe0458..d19e42d4acd 100644 --- a/library/cpp/grpc/client/grpc_common.h +++ b/library/cpp/grpc/client/grpc_common.h @@ -19,7 +19,7 @@ struct TGRpcClientConfig { ui64 MaxOutboundMessageSize = 0; // overrides MaxMessageSize for outgoing requests ui32 MaxInFlight = 0; bool EnableSsl = false; - TString SslCaCert; //Implicitly enables Ssl if not empty + grpc::SslCredentialsOptions SslCredentials; grpc_compression_algorithm CompressionAlgoritm = GRPC_COMPRESS_NONE; ui64 MemQuota = 0; std::unordered_map<TString, TString> StringChannelParams; @@ -34,14 +34,14 @@ struct TGRpcClientConfig { TGRpcClientConfig& operator=(TGRpcClientConfig&&) = default; TGRpcClientConfig(const TString& locator, TDuration timeout = TDuration::Max(), - ui64 maxMessageSize = DEFAULT_GRPC_MESSAGE_SIZE_LIMIT, ui32 maxInFlight = 0, TString caCert = "", - grpc_compression_algorithm compressionAlgorithm = GRPC_COMPRESS_NONE, bool enableSsl = false) + ui64 maxMessageSize = DEFAULT_GRPC_MESSAGE_SIZE_LIMIT, ui32 maxInFlight = 0, const TString& caCert = "", const TString& clientCert = "", + const TString& clientPrivateKey = "", grpc_compression_algorithm compressionAlgorithm = GRPC_COMPRESS_NONE, bool enableSsl = false) : Locator(locator) , Timeout(timeout) , MaxMessageSize(maxMessageSize) , MaxInFlight(maxInFlight) , EnableSsl(enableSsl) - , SslCaCert(caCert) + , SslCredentials{.pem_root_certs = caCert, .pem_private_key = clientPrivateKey, .pem_cert_chain = clientCert} , CompressionAlgoritm(compressionAlgorithm) {} }; @@ -74,8 +74,8 @@ inline std::shared_ptr<grpc::ChannelInterface> CreateChannelInterface(const TGRp if (!config.SslTargetNameOverride.empty()) { args.SetSslTargetNameOverride(config.SslTargetNameOverride); } - if (config.EnableSsl || config.SslCaCert) { - return grpc::CreateCustomChannel(config.Locator, grpc::SslCredentials(grpc::SslCredentialsOptions{config.SslCaCert, "", ""}), args); + if (config.EnableSsl || config.SslCredentials.pem_root_certs) { + return grpc::CreateCustomChannel(config.Locator, grpc::SslCredentials(config.SslCredentials), args); } else { return grpc::CreateCustomChannel(config.Locator, grpc::InsecureChannelCredentials(), args); } diff --git a/library/cpp/grpc/server/grpc_async_ctx_base.h b/library/cpp/grpc/server/grpc_async_ctx_base.h index 51356d4ce5a..079bce4102f 100644 --- a/library/cpp/grpc/server/grpc_async_ctx_base.h +++ b/library/cpp/grpc/server/grpc_async_ctx_base.h @@ -69,6 +69,16 @@ public: return values; } + TVector<TStringBuf> FindClientCert() const { + auto authContext = Context.auth_context(); + + TVector<TStringBuf> values; + for (auto& value: authContext->FindPropertyValues(GRPC_X509_PEM_CERT_PROPERTY_NAME)) { + values.emplace_back(value.data(), value.size()); + } + return values; + } + grpc_compression_level GetCompressionLevel() const { return Context.compression_level(); } diff --git a/library/cpp/grpc/server/grpc_request.h b/library/cpp/grpc/server/grpc_request.h index a3a5c291f07..c4b7e9c040e 100644 --- a/library/cpp/grpc/server/grpc_request.h +++ b/library/cpp/grpc/server/grpc_request.h @@ -170,6 +170,10 @@ public: return TBaseAsyncContext<TService>::GetPeerMetaValues(key); } + TVector<TStringBuf> FindClientCert() const override { + return TBaseAsyncContext<TService>::FindClientCert(); + } + grpc_compression_level GetCompressionLevel() const override { return TBaseAsyncContext<TService>::GetCompressionLevel(); } diff --git a/library/cpp/grpc/server/grpc_request_base.h b/library/cpp/grpc/server/grpc_request_base.h index 105f9515d0a..42b78ed7df1 100644 --- a/library/cpp/grpc/server/grpc_request_base.h +++ b/library/cpp/grpc/server/grpc_request_base.h @@ -82,6 +82,8 @@ public: //! Returns peer optional metavalue virtual TVector<TStringBuf> GetPeerMetaValues(TStringBuf key) const = 0; + virtual TVector<TStringBuf> FindClientCert() const = 0; + //! Returns request compression level virtual grpc_compression_level GetCompressionLevel() const = 0; diff --git a/library/cpp/grpc/server/grpc_server.cpp b/library/cpp/grpc/server/grpc_server.cpp index 7437b7a8f5e..97472206e20 100644 --- a/library/cpp/grpc/server/grpc_server.cpp +++ b/library/cpp/grpc/server/grpc_server.cpp @@ -3,6 +3,7 @@ #include <util/string/join.h> #include <util/generic/yexception.h> #include <util/system/thread.h> +#include <util/generic/map.h> #include <grpc++/resource_quota.h> #include <contrib/libs/grpc/src/core/lib/iomgr/socket_mutator.h> @@ -64,6 +65,11 @@ void TGRpcServer::Start() { grpc::SslServerCredentialsOptions sslOps; sslOps.pem_root_certs = std::move(Options_.SslData->Root); sslOps.pem_key_cert_pairs.push_back(keycert); + + if (Options_.SslData->DoRequestClientCertificate) { + sslOps.client_certificate_request = GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY; + } + credentials = grpc::SslServerCredentials(sslOps); } if (Options_.ExternalListener) { diff --git a/library/cpp/grpc/server/grpc_server.h b/library/cpp/grpc/server/grpc_server.h index d6814a90a0d..c9b48a6676a 100644 --- a/library/cpp/grpc/server/grpc_server.h +++ b/library/cpp/grpc/server/grpc_server.h @@ -25,6 +25,7 @@ struct TSslData { TString Cert; TString Key; TString Root; + bool DoRequestClientCertificate = false; }; struct IExternalListener diff --git a/ydb/core/client/server/CMakeLists.txt b/ydb/core/client/server/CMakeLists.txt index 3c8a48ea36f..ad5f5ca3c85 100644 --- a/ydb/core/client/server/CMakeLists.txt +++ b/ydb/core/client/server/CMakeLists.txt @@ -31,6 +31,7 @@ target_link_libraries(core-client-server PUBLIC ydb-core-engine core-engine-minikql ydb-core-grpc_services + core-grpc_services-base ydb-core-keyvalue core-kqp-common ydb-core-node_whiteboard @@ -51,6 +52,7 @@ target_link_libraries(core-client-server PUBLIC cpp-deprecated-atomic ) target_sources(core-client-server PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/core/client/server/dynamic_node_auth_processor.cpp ${CMAKE_SOURCE_DIR}/ydb/core/client/server/http_ping.cpp ${CMAKE_SOURCE_DIR}/ydb/core/client/server/msgbus_blobstorage_config.cpp ${CMAKE_SOURCE_DIR}/ydb/core/client/server/msgbus_bsadm.cpp diff --git a/ydb/core/client/server/dynamic_node_auth_processor.cpp b/ydb/core/client/server/dynamic_node_auth_processor.cpp new file mode 100644 index 00000000000..af086cc8ede --- /dev/null +++ b/ydb/core/client/server/dynamic_node_auth_processor.cpp @@ -0,0 +1,127 @@ +#include "dynamic_node_auth_processor.h" + +#include <contrib/libs/openssl/include/openssl/x509.h> +#include <contrib/libs/openssl/include/openssl/pem.h> +#include <contrib/libs/openssl/include/openssl/bio.h> +#include <contrib/libs/openssl/include/openssl/objects.h> +#include <contrib/libs/openssl/include/openssl/obj_mac.h> + +#include <util/generic/yexception.h> +#include <util/generic/map.h> +#include <util/generic/string.h> + +namespace NKikimr { + +X509CertificateReader::X509Ptr X509CertificateReader::ReadCertAsPEM(const TStringBuf& cert) { + auto bio = BIOPtr(BIO_new_mem_buf(cert.data(), cert.size())); + if (!bio) { + return {}; + } + + auto x509 = X509Ptr(PEM_read_bio_X509(bio.get(), NULL, NULL, NULL)); + if (!x509) { + return {}; + } + + return x509; +} + +std::pair<TString, TString> X509CertificateReader::GetTermFromX509Name(X509_NAME* name, int nid) { + if (name == nullptr) { + return {}; + } + + const char* sn = OBJ_nid2sn(nid); + if (sn == nullptr) { + return {}; + } + + char cnbuf[1024]; + int name_len = X509_NAME_get_text_by_NID(name, nid, cnbuf, sizeof(cnbuf)); + if (name_len == 0) { + return {}; + } + + return std::make_pair(TString(sn, strlen(sn)), TString(cnbuf, name_len)); +} + +TVector<std::pair<TString, TString>> X509CertificateReader::ReadSubjectTerms(const X509Ptr& x509) { + X509_NAME* name = X509_get_subject_name(x509.get()); // return internal pointer + + TVector<std::pair<TString, TString>> extractions = { + GetTermFromX509Name(name, NID_countryName), + GetTermFromX509Name(name, NID_stateOrProvinceName), + GetTermFromX509Name(name, NID_localityName), + GetTermFromX509Name(name, NID_organizationName), + GetTermFromX509Name(name, NID_organizationalUnitName), + GetTermFromX509Name(name, NID_commonName) + }; + + auto newEnd = std::remove(extractions.begin(), extractions.end(), std::pair<TString, TString>{}); + extractions.erase(newEnd, extractions.end()); + + return extractions; +} + +TDynamicNodeAuthorizationParams::TDistinguishedName& TDynamicNodeAuthorizationParams::TDistinguishedName::AddRelativeDistinguishedName(TRelativeDistinguishedName name) { + RelativeDistinguishedNames.push_back(std::move(name)); + return *this; +} + +TDynamicNodeAuthorizationParams::operator bool() const { + return bool(CertSubjectsDescriptions); +} + +bool TDynamicNodeAuthorizationParams::IsSubjectDescriptionMatched(const TMap<TString, TString>& subjectDescription) const { + for (const auto& description: CertSubjectsDescriptions) { + bool isDescriptionMatched = false; + for (const auto& name: description.RelativeDistinguishedNames) { + isDescriptionMatched = false; + auto fieldIt = subjectDescription.find(name.Attribute); + if (fieldIt == subjectDescription.cend()) { + break; + } + + const auto& attributeValue = fieldIt->second; + for (const auto& value: name.Values) { + if (value == attributeValue) { + isDescriptionMatched = true; + break; + } + } + for (const auto& suffix: name.Suffixes) { + if (attributeValue.EndsWith(suffix)) { + isDescriptionMatched = true; + break; + } + } + if (!isDescriptionMatched) { + break; + } + } + + if (isDescriptionMatched) { + return true; + } + } + + return false; +} + +TDynamicNodeAuthorizationParams::TRelativeDistinguishedName::TRelativeDistinguishedName(const TString& Attribute) + :Attribute(Attribute) +{} + +TDynamicNodeAuthorizationParams::TRelativeDistinguishedName& TDynamicNodeAuthorizationParams::TRelativeDistinguishedName::AddValue(const TString& val) +{ + Values.push_back(val); + return *this; +} + +TDynamicNodeAuthorizationParams::TRelativeDistinguishedName& TDynamicNodeAuthorizationParams::TRelativeDistinguishedName::AddSuffix(const TString& suffix) +{ + Suffixes.push_back(suffix); + return *this; +} + +} //namespace NKikimr { diff --git a/ydb/core/client/server/dynamic_node_auth_processor.h b/ydb/core/client/server/dynamic_node_auth_processor.h new file mode 100644 index 00000000000..d1532fbd14d --- /dev/null +++ b/ydb/core/client/server/dynamic_node_auth_processor.h @@ -0,0 +1,60 @@ +#pragma once +#include <contrib/libs/openssl/include/openssl/x509.h> + +#include <util/generic/string.h> +#include <util/generic/vector.h> + +namespace NKikimr { + +struct TDynamicNodeAuthorizationParams { + struct TRelativeDistinguishedName { + TString Attribute; + TVector<TString> Values; + TVector<TString> Suffixes; + + TRelativeDistinguishedName(const TString& Attribute); + TRelativeDistinguishedName& AddValue(const TString& val); + TRelativeDistinguishedName& AddSuffix(const TString& suffix); + }; + + struct TDistinguishedName { + TVector<TRelativeDistinguishedName> RelativeDistinguishedNames; + + TDistinguishedName& AddRelativeDistinguishedName(TRelativeDistinguishedName name); + }; + + operator bool () const; + bool IsSubjectDescriptionMatched(const TMap<TString, TString>& subjectDescription) const; + TDynamicNodeAuthorizationParams& AddCertSubjectDescription(const TDistinguishedName& description) { + CertSubjectsDescriptions.push_back(description); + return *this; + } + + bool IsHostMatchAttributeCN(const TString&) const { + return true; + } + + bool CanCheckNodeByAttributeCN = false; + TVector<TDistinguishedName> CertSubjectsDescriptions; +}; + + +struct X509CertificateReader { + template <auto fn> + struct deleter_from_fn { + template <typename T> + constexpr void operator()(T* arg) const { + fn(arg); + } + }; + + using X509Ptr = std::unique_ptr<X509, deleter_from_fn<&::X509_free>>; + using BIOPtr = std::unique_ptr<BIO, deleter_from_fn<&::BIO_free>>; + + static X509Ptr ReadCertAsPEM(const TStringBuf& cert); + static TVector<std::pair<TString, TString>> ReadSubjectTerms(const X509Ptr& x509); +private: + static std::pair<TString, TString> GetTermFromX509Name(X509_NAME* name, int nid); +}; + +} //namespace NKikimr diff --git a/ydb/core/client/server/grpc_server.cpp b/ydb/core/client/server/grpc_server.cpp index 019ad3d1291..f4ed1c87007 100644 --- a/ydb/core/client/server/grpc_server.cpp +++ b/ydb/core/client/server/grpc_server.cpp @@ -269,6 +269,10 @@ public: return GetPeerName(); } + TVector<TStringBuf> FindClientCert() const override { + return TGrpcBaseAsyncContext::FindClientCert(); + } + private: void* GetGRpcTag() { return static_cast<IQueueEvent*>(this); @@ -507,7 +511,7 @@ void TGRpcService::SetupIncomingRequests() { // dynamic node registration ADD_REQUEST(RegisterNode, TNodeRegistrationRequest, TNodeRegistrationResponse, { NMsgBusProxy::TBusMessageContext msg(ctx->BindBusContext(NMsgBusProxy::MTYPE_CLIENT_NODE_REGISTRATION_REQUEST)); - RegisterRequestActor(CreateMessageBusRegisterNode(msg)); + RegisterRequestActor(CreateMessageBusRegisterNode(msg, DynamicNodeAuthorizationParams)); }) // CMS request diff --git a/ydb/core/client/server/grpc_server.h b/ydb/core/client/server/grpc_server.h index e668a67322f..e08ffdffb83 100644 --- a/ydb/core/client/server/grpc_server.h +++ b/ydb/core/client/server/grpc_server.h @@ -1,4 +1,5 @@ #pragma once +#include "dynamic_node_auth_processor.h" #include <ydb/core/protos/grpc.grpc.pb.h> @@ -47,6 +48,8 @@ public: //! Bind MessageBus context to the request. virtual NMsgBusProxy::TBusMessageContext BindBusContext(int type) = 0; + virtual TVector<TStringBuf> FindClientCert() const = 0; + //! Returns peer address virtual TString GetPeer() const = 0; }; @@ -58,6 +61,10 @@ class TGRpcService public: TGRpcService(); + void SetDynamicNodeAuthParams(const TDynamicNodeAuthorizationParams& dynamicNodeAuthorizationParams) { + DynamicNodeAuthorizationParams = dynamicNodeAuthorizationParams; + } + void InitService(grpc::ServerCompletionQueue* cq, NGrpc::TLoggerPtr logger) override; void SetGlobalLimiterHandle(NGrpc::TGlobalLimiter* limiter) override; @@ -93,6 +100,7 @@ private: // In flight request management. NGrpc::TGlobalLimiter* Limiter_ = nullptr; + TDynamicNodeAuthorizationParams DynamicNodeAuthorizationParams = {}; }; } diff --git a/ydb/core/client/server/msgbus_server.cpp b/ydb/core/client/server/msgbus_server.cpp index 0a415da70b0..2611241b5e3 100644 --- a/ydb/core/client/server/msgbus_server.cpp +++ b/ydb/core/client/server/msgbus_server.cpp @@ -15,6 +15,7 @@ public: virtual NBus::TBusMessage* GetMessage() = 0; virtual NBus::TBusMessage* ReleaseMessage() = 0; virtual void SendReplyMove(NBus::TBusMessageAutoPtr response) = 0; + virtual TVector<TStringBuf> FindClientCert() const = 0; virtual THolder<TMessageBusSessionIdentHolder::TImpl> CreateSessionIdentHolder() = 0; }; @@ -57,6 +58,11 @@ public: return GetMessage()->GetHeader()->Id; } + TVector<TStringBuf> FindClientCert() const override { + return {}; + } + + THolder<TMessageBusSessionIdentHolder::TImpl> CreateSessionIdentHolder() override; }; @@ -191,6 +197,10 @@ public: SendReply(response.Get()); } + TVector<TStringBuf> FindClientCert() const override { + return RequestContext->FindClientCert(); + }; + THolder<TMessageBusSessionIdentHolder::TImpl> CreateSessionIdentHolder() override; }; @@ -236,6 +246,8 @@ void TBusMessageContext::Swap(TBusMessageContext &msg) { std::swap(Impl, msg.Impl); } +TVector<TStringBuf> TBusMessageContext::FindClientCert() const { return Impl->FindClientCert(); } + THolder<TMessageBusSessionIdentHolder::TImpl> TBusMessageContext::CreateSessionIdentHolder() { Y_VERIFY(Impl); return Impl->CreateSessionIdentHolder(); @@ -248,6 +260,8 @@ public: virtual void SendReply(NBus::TBusMessage *resp) = 0; virtual void SendReplyMove(NBus::TBusMessageAutoPtr resp) = 0; virtual ui64 GetTotalTimeout() const = 0; + virtual TVector<TStringBuf> FindClientCert() const = 0; + }; class TMessageBusSessionIdentHolder::TImplMessageBus @@ -287,6 +301,10 @@ public: ui64 GetTotalTimeout() const override { return Session->GetConfig()->TotalTimeout; } + + TVector<TStringBuf> FindClientCert() const override { + return {}; + } }; THolder<TMessageBusSessionIdentHolder::TImpl> TBusMessageContext::TImplMessageBus::CreateSessionIdentHolder() { @@ -324,6 +342,10 @@ public: auto context = std::move(Context); } + TVector<TStringBuf> FindClientCert() const override { + return Context->FindClientCert(); + } + ui64 GetTotalTimeout() const override { return 90000; } @@ -363,6 +385,10 @@ void TMessageBusSessionIdentHolder::SendReplyMove(NBus::TBusMessageAutoPtr resp) Impl->SendReplyMove(resp); } +TVector<TStringBuf> TMessageBusSessionIdentHolder::FindClientCert() const { + return Impl->FindClientCert(); +} + void TBusMessageWatcher::NotifyForget() { if (MessageWatcher) { diff --git a/ydb/core/client/server/msgbus_server.h b/ydb/core/client/server/msgbus_server.h index 85483132a79..7aa5402b766 100644 --- a/ydb/core/client/server/msgbus_server.h +++ b/ydb/core/client/server/msgbus_server.h @@ -1,4 +1,5 @@ #pragma once +#include "dynamic_node_auth_processor.h" #include <library/cpp/actors/core/actorsystem.h> #include <library/cpp/actors/core/actor_bootstrapped.h> #include <ydb/public/lib/base/defs.h> @@ -59,6 +60,7 @@ public: ~TMessageBusSessionIdentHolder(); void SendReply(NBus::TBusMessage *resp); void SendReplyMove(NBus::TBusMessageAutoPtr resp); + TVector<TStringBuf> FindClientCert() const; template <typename U /* <: TBusMessage */> void SendReplyAutoPtr(TAutoPtr<U>& resp) { SendReplyMove(resp); } @@ -84,6 +86,7 @@ public: NBus::TBusMessage* ReleaseMessage(); void SendReplyMove(NBus::TBusMessageAutoPtr response); void Swap(TBusMessageContext& msg); + TVector<TStringBuf> FindClientCert() const; private: friend class TMessageBusSessionIdentHolder; @@ -296,7 +299,7 @@ IActor* CreateMessageBusBlobStorageConfig(TBusMessageContext &msg); IActor* CreateMessageBusDrainNode(TBusMessageContext &msg); IActor* CreateMessageBusFillNode(TBusMessageContext &msg); IActor* CreateMessageBusResolveNode(TBusMessageContext &msg); -IActor* CreateMessageBusRegisterNode(TBusMessageContext &msg); +IActor* CreateMessageBusRegisterNode(TBusMessageContext &msg, const TDynamicNodeAuthorizationParams& dynamicNodeAuthorizationParams); IActor* CreateMessageBusCmsRequest(TBusMessageContext &msg); IActor* CreateMessageBusSqsRequest(TBusMessageContext &msg); IActor* CreateMessageBusWhoAmI(TBusMessageContext &msg); diff --git a/ydb/core/client/server/msgbus_server_node_registration.cpp b/ydb/core/client/server/msgbus_server_node_registration.cpp index 68dbe201619..3b57aafcb48 100644 --- a/ydb/core/client/server/msgbus_server_node_registration.cpp +++ b/ydb/core/client/server/msgbus_server_node_registration.cpp @@ -1,4 +1,5 @@ #include "msgbus_servicereq.h" +#include "grpc_server.h" #include <library/cpp/actors/core/actor_bootstrapped.h> #include <library/cpp/actors/core/hfunc.h> @@ -25,14 +26,19 @@ public: return NKikimrServices::TActivity::MSGBUS_COMMON; } - TNodeRegistrationActor(NKikimrClient::TNodeRegistrationRequest &request, NMsgBusProxy::TBusMessageContext &msg) + TNodeRegistrationActor(NKikimrClient::TNodeRegistrationRequest &request, NMsgBusProxy::TBusMessageContext &msg, const NKikimr::TDynamicNodeAuthorizationParams& dynamicNodeAuthorizationParams) : TMessageBusSessionIdentHolder(msg) , Request(request) + , DynamicNodeAuthorizationParams(dynamicNodeAuthorizationParams) { } void Bootstrap(const TActorContext &ctx) { + if (!IsNodeAuthorized()) { + SendReplyAndDie(ctx); + } + auto dinfo = AppData(ctx)->DomainsInfo; ui32 group; @@ -62,6 +68,7 @@ public: TAutoPtr<TEvNodeBroker::TEvRegistrationRequest> request = new TEvNodeBroker::TEvRegistrationRequest; + request->Record.SetHost(Request.GetHost()); request->Record.SetPort(Request.GetPort()); request->Record.SetResolveHost(Request.GetResolveHost()); @@ -165,17 +172,49 @@ public: } private: + bool IsNodeAuthorized() { + auto* appdata = AppData(); + if (appdata && appdata->FeatureFlags.GetEnableDynamicNodeAuthorization() && DynamicNodeAuthorizationParams) { + const auto& nodeAuthValues = FindClientCert(); + if (nodeAuthValues.empty()) { + Response.MutableStatus()->SetCode(TStatus::UNAUTHORIZED); + Response.MutableStatus()->SetReason("Cannot authorize node. Node has not provided certificate"); + return false; + } + const auto& pemCert = nodeAuthValues.front(); + TMap<TString, TString> subjectDescription; + X509CertificateReader::X509Ptr x509cert = X509CertificateReader::ReadCertAsPEM(pemCert); + for(const auto& term: X509CertificateReader::ReadSubjectTerms(x509cert)) { + subjectDescription.insert(term); + } + + if (!DynamicNodeAuthorizationParams.IsSubjectDescriptionMatched(subjectDescription)) { + Response.MutableStatus()->SetCode(TStatus::UNAUTHORIZED); + Response.MutableStatus()->SetReason("Cannot authorize node by certificate"); + return false; + } + auto host = Request.GetHost(); + if (!DynamicNodeAuthorizationParams.IsHostMatchAttributeCN(host)) { + Response.MutableStatus()->SetCode(TStatus::UNAUTHORIZED); + Response.MutableStatus()->SetReason("Cannot authorize node with host: " + host); + return false; + } + } + return true; + } + NKikimrClient::TNodeRegistrationRequest Request; NKikimrClient::TNodeRegistrationResponse Response; TActorId NodeBrokerPipe; + const TDynamicNodeAuthorizationParams DynamicNodeAuthorizationParams; }; } // namespace -IActor *CreateMessageBusRegisterNode(NMsgBusProxy::TBusMessageContext &msg) { +IActor *CreateMessageBusRegisterNode(NMsgBusProxy::TBusMessageContext &msg, const NKikimr::TDynamicNodeAuthorizationParams& dynamicNodeAuthorizationParams) { NKikimrClient::TNodeRegistrationRequest &record = static_cast<TBusNodeRegistrationRequest*>(msg.GetMessage())->Record; - return new TNodeRegistrationActor(record, msg); + return new TNodeRegistrationActor(record, msg, dynamicNodeAuthorizationParams); } } // namespace NMsgBusProxy diff --git a/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp b/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp index 2de76f26b75..1dd0c64b1bf 100644 --- a/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp +++ b/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp @@ -826,7 +826,7 @@ public: ClientConfig.MaxMessageSize = p->MaxMessageSize; ClientConfig.MaxInFlight = p->MaxInFlight; ClientConfig.EnableSsl = p->EnableSsl; - ClientConfig.SslCaCert = p->SslCaCert; + ClientConfig.SslCredentials.pem_root_certs = p->SslCredentials.pem_root_certs; } } } diff --git a/ydb/core/driver_lib/cli_base/cli_cmds_root.cpp b/ydb/core/driver_lib/cli_base/cli_cmds_root.cpp index 946fb0ca504..e4b8a10b2f2 100644 --- a/ydb/core/driver_lib/cli_base/cli_cmds_root.cpp +++ b/ydb/core/driver_lib/cli_base/cli_cmds_root.cpp @@ -121,7 +121,7 @@ public: if (config.EnableSsl) { auto *p = std::get_if<NGrpc::TGRpcClientConfig>(&CommandConfig.ClientConfig.GetRef()); p->EnableSsl = config.EnableSsl; - p->SslCaCert = config.CaCerts; + p->SslCredentials.pem_root_certs = config.CaCerts; } } diff --git a/ydb/core/driver_lib/cli_base/cli_grpc.h b/ydb/core/driver_lib/cli_base/cli_grpc.h index 61d09c9f70e..574ac731ea1 100644 --- a/ydb/core/driver_lib/cli_base/cli_grpc.h +++ b/ydb/core/driver_lib/cli_base/cli_grpc.h @@ -95,7 +95,7 @@ public: ClientConfig.MaxMessageSize = p->MaxMessageSize; ClientConfig.MaxInFlight = p->MaxInFlight; ClientConfig.EnableSsl = p->EnableSsl; - ClientConfig.SslCaCert = p->SslCaCert; + ClientConfig.SslCredentials.pem_root_certs = p->SslCredentials.pem_root_certs; } } } diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp index 802557cc942..92ebf7ab8c6 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp @@ -62,7 +62,7 @@ public: if (config.EnableSsl) { auto *p = std::get_if<NGrpc::TGRpcClientConfig>(&CommandConfig.ClientConfig.GetRef()); p->EnableSsl = config.EnableSsl; - p->SslCaCert = config.CaCerts; + p->SslCredentials.pem_root_certs = config.CaCerts; } break; case TCommandConfig::EServerType::MessageBus: diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_server.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_server.cpp index 8e24edef2ec..34c1e505449 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_server.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_server.cpp @@ -854,8 +854,9 @@ protected: { // static node if (NodeBrokerAddresses.empty() && !NodeBrokerPort) { - if (!NodeId) + if (!NodeId) { ythrow yexception() << "Either --node [NUM|'static'] or --node-broker[-port] should be specified"; + } if (!HierarchicalCfg && RunConfig.PathToConfigCacheFile) LoadCachedConfigsForStaticNode(); @@ -1113,8 +1114,13 @@ private: grpcConfig.LoadBalancingPolicy = "round_robin"; if (endpoint.EnableSsl.Defined()) { grpcConfig.EnableSsl = endpoint.EnableSsl.GetRef(); - if (!PathToInterconnectCaFile.Empty()) { - grpcConfig.SslCaCert = ReadFromFile(PathToInterconnectCaFile, "CA certificates"); + auto& sslCredentials = grpcConfig.SslCredentials; + if (PathToGrpcCaFile) { + sslCredentials.pem_root_certs = ReadFromFile(PathToGrpcCaFile, "CA certificates"); + } + if (PathToGrpcCertFile && PathToGrpcPrivateKeyFile) { + sslCredentials.pem_cert_chain = ReadFromFile(PathToGrpcCertFile, "Client certificates"); + sslCredentials.pem_private_key = ReadFromFile(PathToGrpcPrivateKeyFile, "Client certificates key"); } } return NClient::TKikimr(grpcConfig); diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp index ff3738a48c7..8a7ce8272d5 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp @@ -142,7 +142,7 @@ public: ClientConfig.MaxMessageSize = p->MaxMessageSize; ClientConfig.MaxInFlight = p->MaxInFlight; ClientConfig.EnableSsl = p->EnableSsl; - ClientConfig.SslCaCert = p->SslCaCert; + ClientConfig.SslCredentials.pem_root_certs = p->SslCredentials.pem_root_certs; } } } diff --git a/ydb/core/driver_lib/run/CMakeLists.txt b/ydb/core/driver_lib/run/CMakeLists.txt index ecc840965dd..0680f3a50c1 100644 --- a/ydb/core/driver_lib/run/CMakeLists.txt +++ b/ydb/core/driver_lib/run/CMakeLists.txt @@ -57,6 +57,7 @@ target_link_libraries(run PUBLIC cli_utils ydb-core-formats ydb-core-grpc_services + core-grpc_services-base ydb-core-health_check ydb-core-http_proxy core-kesus-proxy @@ -145,4 +146,5 @@ target_sources(run PRIVATE ${CMAKE_SOURCE_DIR}/ydb/core/driver_lib/run/run.cpp ${CMAKE_SOURCE_DIR}/ydb/core/driver_lib/run/service_initializer.cpp ${CMAKE_SOURCE_DIR}/ydb/core/driver_lib/run/version.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/driver_lib/run/cert_auth_props.cpp ) diff --git a/ydb/core/driver_lib/run/cert_auth_props.cpp b/ydb/core/driver_lib/run/cert_auth_props.cpp new file mode 100644 index 00000000000..d01c3140819 --- /dev/null +++ b/ydb/core/driver_lib/run/cert_auth_props.cpp @@ -0,0 +1,28 @@ +#include "cert_auth_props.h" + +namespace NKikimr { + +TDynamicNodeAuthorizationParams GetDynamicNodeAuthorizationParams(const NKikimrConfig::TClientCertificateAuthorization &clientSertificateAuth) { + TDynamicNodeAuthorizationParams certAuthConf; + if (!clientSertificateAuth.HasDynamicNodeAuthorization()) { + return certAuthConf; + } + + const auto& dynNodeAuth = clientSertificateAuth.GetDynamicNodeAuthorization(); + TDynamicNodeAuthorizationParams::TDistinguishedName distinguishedName; + for (const auto& term: dynNodeAuth.GetSubjectTerms()) { + auto name = TDynamicNodeAuthorizationParams::TRelativeDistinguishedName(term.GetShortName()); + for (const auto& value: term.GetValues()) { + name.AddValue(value); + } + for (const auto& suffix: term.GetSuffixes()) { + name.AddSuffix(suffix); + } + distinguishedName.AddRelativeDistinguishedName(std::move(name)); + } + certAuthConf.AddCertSubjectDescription(distinguishedName); + certAuthConf.CanCheckNodeByAttributeCN = dynNodeAuth.GetCanCheckNodeHostByCN(); + return certAuthConf; +} + +} //namespace NKikimr diff --git a/ydb/core/driver_lib/run/cert_auth_props.h b/ydb/core/driver_lib/run/cert_auth_props.h new file mode 100644 index 00000000000..9415c229a25 --- /dev/null +++ b/ydb/core/driver_lib/run/cert_auth_props.h @@ -0,0 +1,11 @@ +#pragma once + +#include <ydb/core/client/server/dynamic_node_auth_processor.h> +#include <ydb/core/protos/config.pb.h> +#include <util/generic/string.h> + +namespace NKikimr { + +TDynamicNodeAuthorizationParams GetDynamicNodeAuthorizationParams(const NKikimrConfig::TClientCertificateAuthorization& clientSertificateAuth); + +} //namespace NKikimr diff --git a/ydb/core/driver_lib/run/run.cpp b/ydb/core/driver_lib/run/run.cpp index dd498a22d74..b8c385f7c50 100644 --- a/ydb/core/driver_lib/run/run.cpp +++ b/ydb/core/driver_lib/run/run.cpp @@ -1,5 +1,6 @@ #include "run.h" #include "dummy.h" +#include "cert_auth_props.h" #include "service_initializer.h" #include "kikimr_services_initializers.h" @@ -668,6 +669,9 @@ void TKikimrRunner::InitializeGRpc(const TKikimrRunConfig& runConfig) { if (hasLegacy) { // start legacy service auto grpcService = new NGRpcProxy::TGRpcService(); + if (!opts.SslData.Empty()) { + grpcService->SetDynamicNodeAuthParams(GetDynamicNodeAuthorizationParams(appConfig.GetClientCertificateAuthorization())); + } auto future = grpcService->Prepare(ActorSystem.Get(), NMsgBusProxy::CreatePersQueueMetaCacheV2Id(), NMsgBusProxy::CreateMsgBusProxyId(), Counters); auto startCb = [grpcService](NThreading::TFuture<void> result) { if (result.HasException()) { @@ -866,6 +870,7 @@ void TKikimrRunner::InitializeGRpc(const TKikimrRunConfig& runConfig) { sslData.Root = ReadFile(pathToCaFile); sslData.Cert = ReadFile(pathToCertificateFile); sslData.Key = ReadFile(pathToPrivateKeyFile); + sslData.DoRequestClientCertificate = appConfig.GetFeatureFlags().GetEnableDynamicNodeAuthorization() && appConfig.GetClientCertificateAuthorization().HasDynamicNodeAuthorization(); sslOpts.SetSslData(sslData); GRpcServers.push_back({ "grpcs", new NGrpc::TGRpcServer(sslOpts) }); diff --git a/ydb/core/grpc_services/base/base.h b/ydb/core/grpc_services/base/base.h index e76529d07be..ab69d7beb16 100644 --- a/ydb/core/grpc_services/base/base.h +++ b/ydb/core/grpc_services/base/base.h @@ -268,6 +268,8 @@ public: virtual TInstant GetDeadline() const = 0; // Meta value from request virtual const TMaybe<TString> GetPeerMetaValues(const TString&) const = 0; + // Auth property from connection + virtual TVector<TStringBuf> FindClientCert() const = 0; // Returns path and resource for rate limiter virtual TMaybe<NRpcService::TRlPath> GetRlPath() const = 0; // Raise issue on the context @@ -509,6 +511,11 @@ public: return TMaybe<TString>{}; } + TVector<TStringBuf> FindClientCert() const override { + Y_FAIL("Unimplemented"); + return {}; + } + void SetDiskQuotaExceeded(bool) override { } @@ -774,6 +781,11 @@ public: return ToMaybe(Ctx_->GetPeerMetaValues(key)); } + TVector<TStringBuf> FindClientCert() const override { + Y_FAIL("Unimplemented"); + return {}; + } + void SetDiskQuotaExceeded(bool) override { } @@ -966,6 +978,10 @@ public: return ToMaybe(Ctx_->GetPeerMetaValues(key)); } + TVector<TStringBuf> FindClientCert() const override { + return Ctx_->FindClientCert(); + } + void SetDiskQuotaExceeded(bool disk) override { if (!QuotaExceeded) { QuotaExceeded = google::protobuf::Arena::CreateMessage<Ydb::QuotaExceeded>(GetArena()); diff --git a/ydb/core/grpc_services/local_rpc/local_rpc.h b/ydb/core/grpc_services/local_rpc/local_rpc.h index b70b9b420ca..e6a87677c6f 100644 --- a/ydb/core/grpc_services/local_rpc/local_rpc.h +++ b/ydb/core/grpc_services/local_rpc/local_rpc.h @@ -56,6 +56,11 @@ public: return TMaybe<TString>{}; } + TVector<TStringBuf> FindClientCert() const override { + Y_FAIL("Unimplemented"); + return {}; + } + void ReplyWithYdbStatus(Ydb::StatusIds::StatusCode status) override { TResp resp; NGRpcService::TCommonResponseFiller<TResp, true>::Fill(resp, IssueManager.GetIssues(), CostInfo.get(), status); diff --git a/ydb/core/health_check/health_check.cpp b/ydb/core/health_check/health_check.cpp index 78a11347d1c..3cb47e4fc9e 100644 --- a/ydb/core/health_check/health_check.cpp +++ b/ydb/core/health_check/health_check.cpp @@ -333,7 +333,7 @@ public: void SetOverallStatus(Ydb::Monitoring::StatusFlag::Status status) { OverallStatus = status; } - + void InheritFrom(TSelfCheckResult& lower) { if (lower.GetOverallStatus() >= OverallStatus) { OverallStatus = lower.GetOverallStatus(); @@ -559,7 +559,7 @@ public: storagePoolName = STATIC_STORAGE_POOL_NAME; } StoragePoolState[storagePoolName].Groups.emplace(group.groupid()); - + if (!IsSpecificDatabaseFilter()) { DatabaseState[DomainPath].StoragePoolNames.emplace_back(storagePoolName); } @@ -1665,14 +1665,14 @@ public: struct TMergeIssuesContext { std::unordered_map<ETags, TList<TSelfCheckContext::TIssueRecord>> recordsMap; std::unordered_set<TString> removeIssuesIds; - + TMergeIssuesContext(TList<TSelfCheckContext::TIssueRecord>& records) { for (auto it = records.begin(); it != records.end(); ) { auto move = it++; recordsMap[move->Tag].splice(recordsMap[move->Tag].end(), records, move); } } - + void RemoveUnlinkIssues(TList<TSelfCheckContext::TIssueRecord>& records) { bool isRemovingIssuesIteration = true; while (isRemovingIssuesIteration) { @@ -1780,7 +1780,7 @@ public: ++it; } } - + if (similar.size() <= MERGING_IGNORE_SIZE) { Mergeed.splice(Mergeed.end(), similar); } @@ -1846,7 +1846,7 @@ public: default: break; } - + auto donorReasons = it->IssueLog.mutable_reason(); for (auto donorReasonIt = donorReasons->begin(); donorReasonIt != donorReasons->end(); donorReasonIt++) { if (!mainReasonIds.contains(*donorReasonIt)) { @@ -1859,7 +1859,7 @@ public: it = similar.erase(it); } - similar.begin()->IssueLog.set_count(ids.size()); + similar.begin()->IssueLog.set_count(ids.size()); similar.begin()->IssueLog.set_listed(ids.size()); } @@ -2147,7 +2147,7 @@ public: Ydb::Monitoring::StatusFlag::Status Status = Ydb::Monitoring::StatusFlag::GREY; bool HasDegraded = false; std::unordered_set<std::pair<TString, TString>> IssueIds; - + TOverallStateContext(Ydb::Monitoring::SelfCheckResult* result) { Result = result; } diff --git a/ydb/core/http_proxy/discovery_actor.cpp b/ydb/core/http_proxy/discovery_actor.cpp index 674ed7af794..59014f28779 100644 --- a/ydb/core/http_proxy/discovery_actor.cpp +++ b/ydb/core/http_proxy/discovery_actor.cpp @@ -28,7 +28,7 @@ namespace NKikimr::NHttpProxy { grpcConf.Locator = Settings.DiscoveryEndpoint; if (Settings.CaCert) { grpcConf.EnableSsl = true; - grpcConf.SslCaCert = *Settings.CaCert; + grpcConf.SslCredentials.pem_root_certs = *Settings.CaCert; } Connection = GrpcClient.CreateGRpcServiceConnection<TProtoService>(grpcConf); } diff --git a/ydb/core/persqueue/actor_persqueue_client_iface.h b/ydb/core/persqueue/actor_persqueue_client_iface.h index 76c1a8545ac..3f6edbe357c 100644 --- a/ydb/core/persqueue/actor_persqueue_client_iface.h +++ b/ydb/core/persqueue/actor_persqueue_client_iface.h @@ -96,7 +96,7 @@ public: .DiscoveryEndpoint(TStringBuilder() << config.GetEndpoint() << ":" << config.GetEndpointPort()) .DiscoveryMode(NYdb::EDiscoveryMode::Async) .CredentialsProviderFactory(credentialsProviderFactory) - .EnableSsl(config.GetUseSecureConnection()); + .SslCredentials(NYdb::TSslCredentials(config.GetUseSecureConnection())); if (config.HasDatabase()) { clientSettings.Database(config.GetDatabase()); } diff --git a/ydb/core/protos/config.proto b/ydb/core/protos/config.proto index f77e99ed4c6..60e7fe2487d 100644 --- a/ydb/core/protos/config.proto +++ b/ydb/core/protos/config.proto @@ -746,6 +746,7 @@ message TFeatureFlags { optional bool EnableBorrowedSplitCompaction = 76 [default = true]; optional bool EnableChangefeedInitialScan = 77 [default = false]; optional bool EnableKqpScanQuerySourceRead = 78 [default = false]; + optional bool EnableDynamicNodeAuthorization = 79 [default = false]; } @@ -1617,6 +1618,21 @@ message TConfigVersion { repeated TConfigItemVersion Items = 1; } +message TClientCertificateAuthorization { + message TSubjectTerm { + optional string ShortName = 1; + repeated string Values = 2; + repeated string Suffixes = 3; + } + + message TDynamicNodeDefinition { + repeated TSubjectTerm SubjectTerms = 1; + optional bool CanCheckNodeHostByCN = 2 [default = false]; + } + + optional TDynamicNodeDefinition DynamicNodeAuthorization = 1; +} + message TAppConfig { optional TActorSystemConfig ActorSystemConfig = 1; optional TLogConfig LogConfig = 2; @@ -1674,6 +1690,7 @@ message TAppConfig { optional TMetadataProviderConfig MetadataProviderConfig = 59; optional TBackgroundTasksConfig BackgroundTasksConfig = 60; optional TAuditConfig AuditConfig = 61; + optional TClientCertificateAuthorization ClientCertificateAuthorization = 62; optional NYq.NConfig.TConfig YandexQueryConfig = 50; // TODO: remove after migration to FederatedQueryConfig diff --git a/ydb/core/protos/console_config.proto b/ydb/core/protos/console_config.proto index ac7ecbf9ae1..da590e23a51 100644 --- a/ydb/core/protos/console_config.proto +++ b/ydb/core/protos/console_config.proto @@ -114,6 +114,7 @@ message TConfigItem { CompactionConfigItem = 52; HttpProxyConfigItem = 53; SchemeShardConfigItem = 54; + ClientCertificateAuthorizationConfigItem = 55; NamedConfigsItem = 100; ClusterYamlConfigItem = 101; diff --git a/ydb/core/protos/node_broker.proto b/ydb/core/protos/node_broker.proto index ee0908902c6..84b418c4b09 100644 --- a/ydb/core/protos/node_broker.proto +++ b/ydb/core/protos/node_broker.proto @@ -48,6 +48,7 @@ message TStatus { ERROR = 2; ERROR_TEMP = 3; WRONG_REQUEST = 4; + UNAUTHORIZED = 5; } optional ECode Code = 1; diff --git a/ydb/core/public_http/grpc_request_context_wrapper.h b/ydb/core/public_http/grpc_request_context_wrapper.h index 25605e2c20c..9dc718e633a 100644 --- a/ydb/core/public_http/grpc_request_context_wrapper.h +++ b/ydb/core/public_http/grpc_request_context_wrapper.h @@ -34,6 +34,7 @@ public: virtual TInstant Deadline() const; virtual TSet<TStringBuf> GetPeerMetaKeys() const; virtual TVector<TStringBuf> GetPeerMetaValues(TStringBuf key) const; + virtual TVector<TStringBuf> FindClientCert() const {return {};} virtual grpc_compression_level GetCompressionLevel() const { return GRPC_COMPRESS_LEVEL_NONE; } virtual google::protobuf::Arena* GetArena(); diff --git a/ydb/core/testlib/CMakeLists.txt b/ydb/core/testlib/CMakeLists.txt index 636e2c0d694..ca2ac9ef8d2 100644 --- a/ydb/core/testlib/CMakeLists.txt +++ b/ydb/core/testlib/CMakeLists.txt @@ -26,6 +26,7 @@ target_link_libraries(ydb-core-testlib PUBLIC cpp-regex-pcre cpp-testing-gmock_in_unittest cpp-testing-unittest + run ydb-core-base core-blobstorage-base core-blobstorage-pdisk diff --git a/ydb/core/testlib/test_client.cpp b/ydb/core/testlib/test_client.cpp index d7a2136c2a6..6b4c2c68241 100644 --- a/ydb/core/testlib/test_client.cpp +++ b/ydb/core/testlib/test_client.cpp @@ -71,12 +71,14 @@ #include <ydb/core/mind/tenant_pool.h> #include <ydb/core/mind/tenant_slot_broker.h> #include <ydb/core/mind/tenant_node_enumeration.h> +#include <ydb/core/mind/node_broker.h> #include <ydb/core/kesus/tablet/events.h> #include <ydb/core/sys_view/service/sysview_service.h> #include <ydb/library/yql/minikql/mkql_function_registry.h> #include <ydb/library/yql/minikql/invoke_builtins/mkql_builtins.h> #include <ydb/library/yql/public/issue/yql_issue_message.h> #include <ydb/core/engine/mkql_engine_flat.h> +#include <ydb/core/driver_lib/run/cert_auth_props.h> #include <library/cpp/testing/unittest/registar.h> #include <ydb/core/kesus/proxy/proxy.h> @@ -228,6 +230,11 @@ namespace Tests { Runtime->GetAppData(nodeIdx).DataStreamsAuthFactory = Settings->DataStreamsAuthFactory.get(); Runtime->GetAppData(nodeIdx).PersQueueMirrorReaderFactory = Settings->PersQueueMirrorReaderFactory.get(); + Runtime->GetAppData(nodeIdx).DynamicNameserviceConfig = new TDynamicNameserviceConfig; + auto dnConfig = Runtime->GetAppData(nodeIdx).DynamicNameserviceConfig; + dnConfig->MaxStaticNodeId = 1023; + dnConfig->MaxDynamicNodeId = 1024 + 100; + SetupConfigurators(nodeIdx); SetupProxies(nodeIdx); } @@ -291,6 +298,10 @@ namespace Tests { system->Register(NGRpcService::CreateGrpcEndpointPublishActor(desc.Get()), TMailboxType::ReadAsFilled, appData.UserPoolId); } + if (!options.SslData.Empty()) { + grpcService->SetDynamicNodeAuthParams(NKikimr::GetDynamicNodeAuthorizationParams(Settings->AppConfig.GetClientCertificateAuthorization())); + } + auto future = grpcService->Prepare( system, NMsgBusProxy::CreatePersQueueMetaCacheV2Id(), @@ -428,8 +439,10 @@ namespace Tests { CreateTestBootstrapper(*Runtime, CreateTestTabletInfo(ChangeStateStorage(Hive, domainId), TTabletTypes::Hive), &CreateDefaultHive); CreateTestBootstrapper(*Runtime, CreateTestTabletInfo(MakeBSControllerID(domainId), TTabletTypes::BSController), &CreateFlatBsController); CreateTestBootstrapper(*Runtime, CreateTestTabletInfo(MakeTenantSlotBrokerID(domainId), TTabletTypes::TenantSlotBroker), &NTenantSlotBroker::CreateTenantSlotBroker); - if (Settings->EnableConsole) + if (Settings->EnableConsole) { CreateTestBootstrapper(*Runtime, CreateTestTabletInfo(MakeConsoleID(domainId), TTabletTypes::Console), &NConsole::CreateConsole); + } + CreateTestBootstrapper(*Runtime, CreateTestTabletInfo(MakeNodeBrokerID(domainId), TTabletTypes::NodeBroker), &NNodeBroker::CreateNodeBroker); } void TServer::SetupStorage() { diff --git a/ydb/core/testlib/test_client.h b/ydb/core/testlib/test_client.h index 649cb5d0f7a..94996587249 100644 --- a/ydb/core/testlib/test_client.h +++ b/ydb/core/testlib/test_client.h @@ -115,6 +115,7 @@ namespace Tests { TStoragePoolKinds StoragePoolTypes; TVector<NKikimrKqp::TKqpSetting> KqpSettings; bool EnableConsole = true; + bool EnableNodeBroker = false; bool EnableConfigsDispatcher = true; bool UseRealThreads = true; bool EnableKqpSpilling = false; @@ -153,6 +154,7 @@ namespace Tests { TServerSettings& AddStoragePool(const TString& poolKind, const TString& poolName = {}, ui32 numGroups = 1, ui32 encryptionMode = 0); TServerSettings& SetKqpSettings(const TVector<NKikimrKqp::TKqpSetting>& settings) { KqpSettings = settings; return *this; } TServerSettings& SetEnableConsole(bool value) { EnableConsole = value; return *this; } + TServerSettings& SetEnableNodeBroker(bool value) { EnableNodeBroker = value; return *this; } TServerSettings& SetEnableConfigsDispatcher(bool value) { EnableConfigsDispatcher = value; return *this; } TServerSettings& SetUseRealThreads(bool value) { UseRealThreads = value; return *this; } TServerSettings& SetAppConfig(const NKikimrConfig::TAppConfig value) { AppConfig = value; return *this; } diff --git a/ydb/core/yq/libs/health/health.cpp b/ydb/core/yq/libs/health/health.cpp index aa8b2dca60b..ab8f637c67f 100644 --- a/ydb/core/yq/libs/health/health.cpp +++ b/ydb/core/yq/libs/health/health.cpp @@ -25,7 +25,7 @@ public: , Client(yqSharedResources->CoreYdbDriver, NYdb::TCommonClientSettings() .DiscoveryEndpoint("localhost:" + ToString(Config.GetPort())) - .EnableSsl(Config.GetSecure()) + .SslCredentials(NYdb::TSslCredentials(Config.GetSecure())) .Database(Config.GetDatabase())) , YqSharedResources(yqSharedResources) { diff --git a/ydb/core/yq/libs/private_client/internal_service.cpp b/ydb/core/yq/libs/private_client/internal_service.cpp index ce18110764e..23b8d313903 100644 --- a/ydb/core/yq/libs/private_client/internal_service.cpp +++ b/ydb/core/yq/libs/private_client/internal_service.cpp @@ -37,7 +37,7 @@ public: NYdb::TCommonClientSettings() .DiscoveryEndpoint(privateApiConfig.GetTaskServiceEndpoint()) .CredentialsProviderFactory(credentialsProviderFactory({.SaKeyFile = privateApiConfig.GetSaKeyFile(), .IamEndpoint = privateApiConfig.GetIamEndpoint()})) - .EnableSsl(privateApiConfig.GetSecureTaskService()) + .SslCredentials(NYdb::TSslCredentials(privateApiConfig.GetSecureTaskService())) .Database(privateApiConfig.GetTaskServiceDatabase() ? privateApiConfig.GetTaskServiceDatabase() : TMaybe<TString>()), counters) { diff --git a/ydb/core/yq/libs/read_rule/read_rule_creator.cpp b/ydb/core/yq/libs/read_rule/read_rule_creator.cpp index 0ebdbc4f70b..1ee5fa00774 100644 --- a/ydb/core/yq/libs/read_rule/read_rule_creator.cpp +++ b/ydb/core/yq/libs/read_rule/read_rule_creator.cpp @@ -188,7 +188,7 @@ private: .DiscoveryEndpoint(Topic.GetEndpoint()) .CredentialsProviderFactory(std::move(credentialsProvider)) .DiscoveryMode(NYdb::EDiscoveryMode::Async) - .EnableSsl(Topic.GetUseSsl()); + .SslCredentials(NYdb::TSslCredentials(Topic.GetUseSsl())); } private: diff --git a/ydb/core/yq/libs/read_rule/read_rule_deleter.cpp b/ydb/core/yq/libs/read_rule/read_rule_deleter.cpp index 32927270d3f..3db6600584a 100644 --- a/ydb/core/yq/libs/read_rule/read_rule_deleter.cpp +++ b/ydb/core/yq/libs/read_rule/read_rule_deleter.cpp @@ -162,7 +162,7 @@ private: .DiscoveryEndpoint(Topic.cluster_endpoint()) .CredentialsProviderFactory(std::move(credentialsProvider)) .DiscoveryMode(NYdb::EDiscoveryMode::Async) - .EnableSsl(Topic.use_ssl()); + .SslCredentials(NYdb::TSslCredentials(Topic.use_ssl())); } private: diff --git a/ydb/core/yq/libs/shared_resources/db_pool.cpp b/ydb/core/yq/libs/shared_resources/db_pool.cpp index 9af52e67e19..8204117bd26 100644 --- a/ydb/core/yq/libs/shared_resources/db_pool.cpp +++ b/ydb/core/yq/libs/shared_resources/db_pool.cpp @@ -319,7 +319,7 @@ TDbPool::TPtr TDbPoolMap::GetOrCreate(EDbPoolId dbPoolId, ui32 sessionsCount, co clientSettings.CredentialsProviderFactory(CredentialsProviderFactory(credSettings)); - clientSettings.EnableSsl(Config.GetStorage().GetUseSsl()); + clientSettings.SslCredentials(NYdb::TSslCredentials(Config.GetStorage().GetUseSsl())); TableClient = MakeHolder<NYdb::NTable::TTableClient>(Driver, clientSettings); } diff --git a/ydb/core/yq/libs/ydb/ydb.cpp b/ydb/core/yq/libs/ydb/ydb.cpp index 3403d825e88..c70e1089bda 100644 --- a/ydb/core/yq/libs/ydb/ydb.cpp +++ b/ydb/core/yq/libs/ydb/ydb.cpp @@ -198,14 +198,12 @@ TSettings GetClientSettings(const NConfig::TYdbStorageConfig& config, settings.CredentialsProviderFactory(credProviderFactory(credSettings)); if (config.GetUseLocalMetadataService()) { - settings.EnableSsl(true); + settings.SslCredentials(TSslCredentials(true)); } if (config.GetCertificateFile()) { auto cert = StripString(TFileInput(config.GetCertificateFile()).ReadAll()); - settings - .EnableSsl(true) - .CaCert(cert); + settings.SslCredentials(TSslCredentials(true, cert)); } return settings; diff --git a/ydb/library/ycloud/impl/grpc_service_client.h b/ydb/library/ycloud/impl/grpc_service_client.h index c89515d8654..d2837a69264 100644 --- a/ydb/library/ycloud/impl/grpc_service_client.h +++ b/ydb/library/ycloud/impl/grpc_service_client.h @@ -74,7 +74,7 @@ public: const auto& requestId = ev->Get()->RequestId; if (!Connection) { BLOG_GRPC_D(Prefix(requestId) << "Connect to " - << ((Config.EnableSsl || !Config.SslCaCert.empty()) ? "grpcs://" : "grpc://") + << ((Config.EnableSsl || !Config.SslCredentials.pem_root_certs.empty()) ? "grpcs://" : "grpc://") << Config.Locator); Connection = Client.CreateGRpcServiceConnection<TGrpcService>(Config); } diff --git a/ydb/library/yql/providers/common/token_accessor/client/factory.cpp b/ydb/library/yql/providers/common/token_accessor/client/factory.cpp index 0a7e9b6af4e..1993ab2e92c 100644 --- a/ydb/library/yql/providers/common/token_accessor/client/factory.cpp +++ b/ydb/library/yql/providers/common/token_accessor/client/factory.cpp @@ -27,7 +27,7 @@ public: { GrpcClientConfig.Locator = tokenAccessorEndpoint; GrpcClientConfig.EnableSsl = useSsl; - GrpcClientConfig.SslCaCert = sslCaCert; + GrpcClientConfig.SslCredentials.pem_root_certs = sslCaCert; Connections.reserve(connectionPoolSize); for (ui32 i = 0; i < connectionPoolSize; ++i) { Connections.push_back(Client->CreateGRpcServiceConnection<TokenAccessorService>(GrpcClientConfig)); diff --git a/ydb/library/yql/providers/common/token_accessor/client/token_accessor_client.cpp b/ydb/library/yql/providers/common/token_accessor/client/token_accessor_client.cpp index b11c1d0f47b..f29acf6fa34 100644 --- a/ydb/library/yql/providers/common/token_accessor/client/token_accessor_client.cpp +++ b/ydb/library/yql/providers/common/token_accessor/client/token_accessor_client.cpp @@ -182,7 +182,7 @@ std::shared_ptr<NYdb::ICredentialsProvider> CreateTokenAccessorCredentialsProvid NGrpc::TGRpcClientConfig grpcConf; grpcConf.Locator = tokenAccessorEndpoint; grpcConf.EnableSsl = useSsl; - grpcConf.SslCaCert = sslCaCert; + grpcConf.SslCredentials.pem_root_certs = sslCaCert; std::shared_ptr<NGrpc::TServiceConnection<TokenAccessorService>> connection = client->CreateGRpcServiceConnection<TokenAccessorService>(grpcConf); return CreateTokenAccessorCredentialsProvider(std::move(client), std::move(connection), serviceAccountId, serviceAccountIdSignature, refreshPeriod, requestTimeout); diff --git a/ydb/library/yql/providers/common/token_accessor/client/token_accessor_client_factory.cpp b/ydb/library/yql/providers/common/token_accessor/client/token_accessor_client_factory.cpp index da77d08ff66..d44cc248994 100644 --- a/ydb/library/yql/providers/common/token_accessor/client/token_accessor_client_factory.cpp +++ b/ydb/library/yql/providers/common/token_accessor/client/token_accessor_client_factory.cpp @@ -55,7 +55,7 @@ std::shared_ptr<NYdb::ICredentialsProviderFactory> CreateTokenAccessorCredential NGrpc::TGRpcClientConfig grpcConf; grpcConf.Locator = tokenAccessorEndpoint; grpcConf.EnableSsl = useSsl; - grpcConf.SslCaCert = sslCaCert; + grpcConf.SslCredentials.pem_root_certs = sslCaCert; std::shared_ptr<NGrpc::TServiceConnection<TokenAccessorService>> connection = client->CreateGRpcServiceConnection<TokenAccessorService>(grpcConf); return CreateTokenAccessorCredentialsProviderFactory(std::move(client), std::move(connection), serviceAccountId, serviceAccountIdSignature, refreshPeriod, requestTimeout); @@ -70,7 +70,7 @@ std::shared_ptr<NYdb::ICredentialsProviderFactory> CreateTokenAccessorCredential const TDuration& requestTimeout ) { - return std::make_shared<TTokenAccessorCredentialsProviderFactory>(std::move(client), std::move(connection), serviceAccountId, serviceAccountIdSignature, refreshPeriod, requestTimeout); + return std::make_shared<TTokenAccessorCredentialsProviderFactory>(std::move(client), std::move(connection), serviceAccountId, serviceAccountIdSignature, refreshPeriod, requestTimeout); } } diff --git a/ydb/library/yql/providers/pq/async_io/dq_pq_read_actor.cpp b/ydb/library/yql/providers/pq/async_io/dq_pq_read_actor.cpp index 1fb54221a00..ad097c9525d 100644 --- a/ydb/library/yql/providers/pq/async_io/dq_pq_read_actor.cpp +++ b/ydb/library/yql/providers/pq/async_io/dq_pq_read_actor.cpp @@ -131,7 +131,7 @@ public: NYdb::NPersQueue::TPersQueueClientSettings opts; opts.Database(SourceParams.GetDatabase()) .DiscoveryEndpoint(SourceParams.GetEndpoint()) - .EnableSsl(SourceParams.GetUseSsl()) + .SslCredentials(NYdb::TSslCredentials(SourceParams.GetUseSsl())) .CredentialsProviderFactory(CredentialsProviderFactory); return opts; diff --git a/ydb/library/yql/providers/pq/async_io/dq_pq_write_actor.cpp b/ydb/library/yql/providers/pq/async_io/dq_pq_write_actor.cpp index ce7879f45a3..1b853fc8769 100644 --- a/ydb/library/yql/providers/pq/async_io/dq_pq_write_actor.cpp +++ b/ydb/library/yql/providers/pq/async_io/dq_pq_write_actor.cpp @@ -239,7 +239,7 @@ private: return NYdb::NPersQueue::TPersQueueClientSettings() .Database(SinkParams.GetDatabase()) .DiscoveryEndpoint(SinkParams.GetEndpoint()) - .EnableSsl(SinkParams.GetUseSsl()) + .SslCredentials(NYdb::TSslCredentials(SinkParams.GetUseSsl())) .CredentialsProviderFactory(CredentialsProviderFactory); } diff --git a/ydb/library/yql/providers/pq/gateway/native/yql_pq_session.cpp b/ydb/library/yql/providers/pq/gateway/native/yql_pq_session.cpp index 18d1ae0e476..61bd28b50d2 100644 --- a/ydb/library/yql/providers/pq/gateway/native/yql_pq_session.cpp +++ b/ydb/library/yql/providers/pq/gateway/native/yql_pq_session.cpp @@ -20,7 +20,7 @@ NYdb::NPersQueue::TPersQueueClientSettings GetYdbPqClientOptions(const TString& opts .DiscoveryEndpoint(cfg.GetEndpoint()) .Database(database) - .EnableSsl(cfg.GetUseSsl()) + .SslCredentials(NYdb::TSslCredentials(cfg.GetUseSsl())) .CredentialsProviderFactory(credentialsProviderFactory); return opts; @@ -31,7 +31,7 @@ NYdb::TCommonClientSettings GetDsClientOptions(const TString& database, const NY opts .DiscoveryEndpoint(cfg.GetEndpoint()) .Database(database) - .EnableSsl(cfg.GetUseSsl()) + .SslCredentials(NYdb::TSslCredentials(cfg.GetUseSsl())) .CredentialsProviderFactory(credentialsProviderFactory); return opts; diff --git a/ydb/library/yql/providers/ydb/actors/yql_ydb_read_actor.cpp b/ydb/library/yql/providers/ydb/actors/yql_ydb_read_actor.cpp index b10d6b4c1fd..844685d952a 100644 --- a/ydb/library/yql/providers/ydb/actors/yql_ydb_read_actor.cpp +++ b/ydb/library/yql/providers/ydb/actors/yql_ydb_read_actor.cpp @@ -87,7 +87,7 @@ public: , Path(path), Columns(columns), KeyColumnTypes(keyColumnTypes) , MaxRows(maxRowsInRequest), MaxBytes(maxBytesInRequest) , EndKey(keyTo) - , Connection(driver, ::NYdb::TCommonClientSettings().Database(database).DiscoveryEndpoint(endpoint).CredentialsProviderFactory(credentialsProviderFactory).DiscoveryMode(::NYdb::EDiscoveryMode::Async).EnableSsl(secure)) + , Connection(driver, ::NYdb::TCommonClientSettings().Database(database).DiscoveryEndpoint(endpoint).CredentialsProviderFactory(credentialsProviderFactory).DiscoveryMode(::NYdb::EDiscoveryMode::Async).SslCredentials(::NYdb::TSslCredentials(secure))) , LastReadKey(keyFrom.empty() ? NKikimr::TSerializedCellVec::Serialize(TVector<NKikimr::TCell>(KeyColumnTypes.size())) : keyFrom) , LastReadKeyInclusive(false) , Retried(0U) diff --git a/ydb/library/yql/providers/ydb/comp_nodes/yql_kik_scan.cpp b/ydb/library/yql/providers/ydb/comp_nodes/yql_kik_scan.cpp index 1e69f3346fd..074901f74fa 100644 --- a/ydb/library/yql/providers/ydb/comp_nodes/yql_kik_scan.cpp +++ b/ydb/library/yql/providers/ydb/comp_nodes/yql_kik_scan.cpp @@ -111,7 +111,7 @@ using TBaseComputation = TMutableComputationNode<TKikScan<Async>>; , MaxBytes(maxBytesInRequest) , EndKey(keyTo) , Settings(settings) - , Connection(driver, NYdb::TCommonClientSettings().Database(database).DiscoveryEndpoint(endpoint).AuthToken(token).DiscoveryMode(NYdb::EDiscoveryMode::Async).EnableSsl(secure)) + , Connection(driver, NYdb::TCommonClientSettings().Database(database).DiscoveryEndpoint(endpoint).AuthToken(token).DiscoveryMode(NYdb::EDiscoveryMode::Async).SslCredentials(NYdb::TSslCredentials(secure))) , LastReadKey(keyFrom.empty() ? NKikimr::TSerializedCellVec::Serialize(TVector<NKikimr::TCell>(KeyColumnTypes.size())) : keyFrom) , LastReadKeyInclusive(false) , Retried(0U) diff --git a/ydb/library/yql/providers/ydb/provider/yql_ydb_load_meta.cpp b/ydb/library/yql/providers/ydb/provider/yql_ydb_load_meta.cpp index 1473e356e7c..0d735d0b98d 100644 --- a/ydb/library/yql/providers/ydb/provider/yql_ydb_load_meta.cpp +++ b/ydb/library/yql/providers/ydb/provider/yql_ydb_load_meta.cpp @@ -153,7 +153,7 @@ public: const auto ins = Clients_->emplace(cluster, std::pair<TMetaClient, std::optional<TCreateSnapshotHandleResult>>{{Driver_, NYdb::TCommonClientSettings() .Database(config.Database) .DiscoveryEndpoint(config.Endpoint) - .EnableSsl(config.Secure) + .SslCredentials(NYdb::TSslCredentials(config.Secure)) .CredentialsProviderFactory(credentialsProviderFactory) .DiscoveryMode(NYdb::EDiscoveryMode::Async)}, std::nullopt}); diff --git a/ydb/public/lib/experimental/ydb_clickhouse_internal.cpp b/ydb/public/lib/experimental/ydb_clickhouse_internal.cpp index 2566c84ec2a..5e137a1c879 100644 --- a/ydb/public/lib/experimental/ydb_clickhouse_internal.cpp +++ b/ydb/public/lib/experimental/ydb_clickhouse_internal.cpp @@ -159,7 +159,7 @@ TScanIterator::TScanIterator(const TDriver& driver, const TString &database, con , MaxRows(maxRowsInRequest) , MaxBytes(maxBytesInRequest) , Settings(settings) - , Connection(driver, NYdb::TCommonClientSettings().Database(database).AuthToken(token).DiscoveryEndpoint(endpoint).DiscoveryMode(NYdb::EDiscoveryMode::Async).EnableSsl(ssl)) + , Connection(driver, NYdb::TCommonClientSettings().Database(database).AuthToken(token).DiscoveryEndpoint(endpoint).DiscoveryMode(NYdb::EDiscoveryMode::Async).SslCredentials(NYdb::TSslCredentials(ssl))) , LastReadKey(keyFrom.empty() ? NKikimr::TSerializedCellVec::Serialize(TVector<NKikimr::TCell>(KeyColumnTypes.size())) : keyFrom) , LastReadKeyInclusive(false) , EndKey(keyTo) diff --git a/ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h b/ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h new file mode 100644 index 00000000000..016a01dcdd4 --- /dev/null +++ b/ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h @@ -0,0 +1,26 @@ +#pragma once + +#include "type_switcher.h" + +namespace NYdb { + +struct TSslCredentials { + bool IsEnabled = false; + TStringType CaCert; + TStringType Cert; + TStringType PrivateKey; + + TSslCredentials() = default; + + TSslCredentials(const bool isEnabled, const TStringType& caCert = "", const TStringType& cert = "", const TStringType& privateKey = "") + : IsEnabled(isEnabled), CaCert(caCert), Cert(cert), PrivateKey(privateKey) {} + + bool operator==(const TSslCredentials& other) const { + return IsEnabled == other.IsEnabled && + CaCert == other.CaCert && + Cert == other.Cert && + PrivateKey == other.PrivateKey; + } +}; + +} // namespace NYdb diff --git a/ydb/public/sdk/cpp/client/impl/ydb_internal/db_driver_state/state.cpp b/ydb/public/sdk/cpp/client/impl/ydb_internal/db_driver_state/state.cpp index 43397b40169..afa6919c062 100644 --- a/ydb/public/sdk/cpp/client/impl/ydb_internal/db_driver_state/state.cpp +++ b/ydb/public/sdk/cpp/client/impl/ydb_internal/db_driver_state/state.cpp @@ -2,6 +2,7 @@ #include "state.h" #include <ydb/public/sdk/cpp/client/ydb_types/credentials/credentials.h> +#include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h> #include <ydb/public/sdk/cpp/client/impl/ydb_internal/logger/log.h> #include <library/cpp/string_utils/quote/quote.h> @@ -19,15 +20,13 @@ TDbDriverState::TDbDriverState( const TStringType& database, const TStringType& discoveryEndpoint, EDiscoveryMode discoveryMode, - bool enableSsl, - const TStringType& caCert, + const TSslCredentials& sslCredentials, IInternalClient* client ) : Database(database) , DiscoveryEndpoint(discoveryEndpoint) , DiscoveryMode(discoveryMode) - , EnableSsl(enableSsl) - , CaCert(caCert) + , SslCredentials(sslCredentials) , Client(client) , EndpointPool([this, client]() mutable { // this callback will be called just after shared_ptr initialization @@ -135,8 +134,7 @@ TDbDriverStatePtr TDbDriverStateTracker::GetDriverState( TStringType database, TStringType discoveryEndpoint, EDiscoveryMode discoveryMode, - bool enableSsl, - TStringType caCert, + const TSslCredentials& sslCredentials, std::shared_ptr<ICredentialsProviderFactory> credentialsProviderFactory ) { TStringType clientIdentity; @@ -144,7 +142,7 @@ TDbDriverStatePtr TDbDriverStateTracker::GetDriverState( clientIdentity = credentialsProviderFactory->GetClientIdentity(); } Quote(database); - const TStateKey key{database, discoveryEndpoint, clientIdentity, discoveryMode, enableSsl, caCert}; + const TStateKey key{database, discoveryEndpoint, clientIdentity, discoveryMode, sslCredentials}; { std::shared_lock lock(Lock_); auto state = States_.find(key); @@ -190,8 +188,7 @@ TDbDriverStatePtr TDbDriverStateTracker::GetDriverState( database, discoveryEndpoint, discoveryMode, - enableSsl, - caCert, + sslCredentials, DiscoveryClient_), deleter); diff --git a/ydb/public/sdk/cpp/client/impl/ydb_internal/db_driver_state/state.h b/ydb/public/sdk/cpp/client/impl/ydb_internal/db_driver_state/state.h index 96b4c17e0e5..0246f7166b1 100644 --- a/ydb/public/sdk/cpp/client/impl/ydb_internal/db_driver_state/state.h +++ b/ydb/public/sdk/cpp/client/impl/ydb_internal/db_driver_state/state.h @@ -5,6 +5,7 @@ #include <ydb/public/sdk/cpp/client/impl/ydb_internal/internal_header.h> #include <ydb/public/sdk/cpp/client/impl/ydb_internal/internal_client/client.h> +#include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h> #include <ydb/public/sdk/cpp/client/ydb_types/core_facility/core_facility.h> namespace NYdb { @@ -30,8 +31,7 @@ public: const TStringType& database, const TStringType& discoveryEndpoint, EDiscoveryMode discoveryMode, - bool enableSsl, - const TStringType& caCert, + const TSslCredentials& sslCredentials, IInternalClient* client ); @@ -52,8 +52,7 @@ public: const TStringType Database; const TStringType DiscoveryEndpoint; const EDiscoveryMode DiscoveryMode; - const bool EnableSsl; - const TStringType CaCert; + const TSslCredentials SslCredentials; std::shared_ptr<ICredentialsProvider> CredentialsProvider; IInternalClient* Client; TEndpointPool EndpointPool; @@ -73,16 +72,18 @@ public: // Tracker allows to get driver state by database and credentials class TDbDriverStateTracker { - using TStateKey = std::tuple<TStringType, TStringType, TStringType, EDiscoveryMode, bool, TStringType>; + using TStateKey = std::tuple<TStringType, TStringType, TStringType, EDiscoveryMode, TSslCredentials>; struct TStateKeyHash { size_t operator()(const TStateKey& k) const noexcept { THash<TStringType> strHash; const size_t h0 = strHash(std::get<0>(k)); const size_t h1 = strHash(std::get<1>(k)); const size_t h2 = strHash(std::get<2>(k)); - const size_t h3 = ((size_t)std::get<3>(k) << 1) + (size_t)std::get<4>(k); - const size_t h5 = strHash(std::get<5>(k)); - return (h0 ^ h1 ^ h2 ^ h3 ^ h5); + const auto& sslCredentials = std::get<4>(k); + const size_t h3 = (static_cast<size_t>(std::get<3>(k)) << 1) + static_cast<size_t>(sslCredentials.IsEnabled); + const size_t h5 = strHash(sslCredentials.CaCert); + const size_t h6 = strHash(sslCredentials.Cert); + return (h0 ^ h1 ^ h2 ^ h3 ^ h5 ^ h6); } }; public: @@ -91,8 +92,7 @@ public: TStringType database, TStringType DiscoveryEndpoint, EDiscoveryMode discoveryMode, - bool enableSsl, - TStringType caCert, + const TSslCredentials& sslCredentials, std::shared_ptr<ICredentialsProviderFactory> credentialsProviderFactory ); NThreading::TFuture<void> SendNotification( diff --git a/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/grpc_connections.cpp b/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/grpc_connections.cpp index db3247a41c5..f6e90a622f1 100644 --- a/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/grpc_connections.cpp +++ b/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/grpc_connections.cpp @@ -138,8 +138,7 @@ TGRpcConnectionsImpl::TGRpcConnectionsImpl(std::shared_ptr<IConnectionsParams> p : MetricRegistryPtr_(nullptr) , ResponseQueue_(CreateThreadPool(params->GetClientThreadsNum())) , DefaultDiscoveryEndpoint_(params->GetEndpoint()) - , EnableSsl_(params->IsSslEnabled()) - , CaCert_(params->GetCaCert()) + , SslCredentials_(params->GetSslCredentials()) , DefaultDatabase_(params->GetDatabase()) , DefaultCredentialsProviderFactory_(params->GetCredentialsProviderFactory()) , StateTracker_(this) @@ -182,8 +181,7 @@ TGRpcConnectionsImpl::TGRpcConnectionsImpl(std::shared_ptr<IConnectionsParams> p DefaultDatabase_, DefaultDiscoveryEndpoint_, DefaultDiscoveryMode_, - EnableSsl_, - CaCert_, + SslCredentials_, DefaultCredentialsProviderFactory_ ); } @@ -270,16 +268,14 @@ TDbDriverStatePtr TGRpcConnectionsImpl::GetDriverState( const TMaybe<TStringType>& database, const TMaybe<TStringType>& discoveryEndpoint, const TMaybe<EDiscoveryMode>& discoveryMode, - const TMaybe<bool>& enableSsl, - const TMaybe<TStringType>& caCert, + const TMaybe<TSslCredentials>& sslCredentials, const TMaybe<std::shared_ptr<ICredentialsProviderFactory>>& credentialsProviderFactory ) { return StateTracker_.GetDriverState( database ? database.GetRef() : DefaultDatabase_, discoveryEndpoint ? discoveryEndpoint.GetRef() : DefaultDiscoveryEndpoint_, discoveryMode ? discoveryMode.GetRef() : DefaultDiscoveryMode_, - enableSsl ? enableSsl.GetRef() : EnableSsl_, - caCert ? caCert.GetRef() : CaCert_, + sslCredentials ? sslCredentials.GetRef() : SslCredentials_, credentialsProviderFactory ? credentialsProviderFactory.GetRef() : DefaultCredentialsProviderFactory_); } diff --git a/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/grpc_connections.h b/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/grpc_connections.h index e07ab5a9e4e..4f27a5a25a7 100644 --- a/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/grpc_connections.h +++ b/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/grpc_connections.h @@ -1,6 +1,7 @@ #pragma once #include <ydb/public/sdk/cpp/client/impl/ydb_internal/internal_header.h> +#include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h> #include "actions.h" #include "params.h" @@ -60,8 +61,7 @@ public: const TMaybe<TStringType>& database, const TMaybe<TStringType>& discoveryEndpoint, const TMaybe<EDiscoveryMode>& discoveryMode, - const TMaybe<bool>& enableSsl, - const TMaybe<TStringType>& caCert, + const TMaybe<TSslCredentials>& sslCredentials, const TMaybe<std::shared_ptr<ICredentialsProviderFactory>>& credentialsProviderFactory ); IQueueClientContextPtr CreateContext() override; @@ -80,8 +80,10 @@ public: TRpcRequestSettings::TEndpointPolicy endpointPolicy) { auto clientConfig = NGrpc::TGRpcClientConfig(dbState->DiscoveryEndpoint); - clientConfig.EnableSsl = dbState->EnableSsl; - clientConfig.SslCaCert = dbState->CaCert; + const auto& sslCredentials = dbState->SslCredentials; + clientConfig.SslCredentials = {.pem_root_certs = sslCredentials.CaCert, .pem_private_key = sslCredentials.PrivateKey, .pem_cert_chain = sslCredentials.Cert}; + clientConfig.EnableSsl = sslCredentials.IsEnabled; + clientConfig.MemQuota = MemoryQuota_; if (MaxMessageSize_ > 0) { @@ -703,8 +705,7 @@ private: std::unique_ptr<IThreadPool> ResponseQueue_; const TStringType DefaultDiscoveryEndpoint_; - const bool EnableSsl_; - const TStringType CaCert_; + const TSslCredentials SslCredentials_; const TStringType DefaultDatabase_; std::shared_ptr<ICredentialsProviderFactory> DefaultCredentialsProviderFactory_; TDbDriverStateTracker StateTracker_; diff --git a/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/params.h b/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/params.h index 9de2911a4f2..033274c8fa5 100644 --- a/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/params.h +++ b/ydb/public/sdk/cpp/client/impl/ydb_internal/grpc_connections/params.h @@ -1,6 +1,7 @@ #pragma once #include <ydb/public/sdk/cpp/client/impl/ydb_internal/internal_header.h> +#include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h> namespace NYdb { @@ -11,8 +12,7 @@ public: virtual size_t GetNetworkThreadsNum() const = 0; virtual size_t GetClientThreadsNum() const = 0; virtual size_t GetMaxQueuedResponses() const = 0; - virtual bool IsSslEnabled() const = 0; - virtual TStringType GetCaCert() const = 0; + virtual TSslCredentials GetSslCredentials() const = 0; virtual TStringType GetDatabase() const = 0; virtual std::shared_ptr<ICredentialsProviderFactory> GetCredentialsProviderFactory() const = 0; virtual EDiscoveryMode GetDiscoveryMode() const = 0; diff --git a/ydb/public/sdk/cpp/client/ydb_common_client/impl/client.h b/ydb/public/sdk/cpp/client/ydb_common_client/impl/client.h index b1ce76ce3b4..6b26c7b0494 100644 --- a/ydb/public/sdk/cpp/client/ydb_common_client/impl/client.h +++ b/ydb/public/sdk/cpp/client/ydb_common_client/impl/client.h @@ -5,6 +5,7 @@ #undef INCLUDE_YDB_INTERNAL_H #include <ydb/public/sdk/cpp/client/ydb_types/exceptions/exceptions.h> +#include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h> #include <memory> @@ -18,11 +19,10 @@ public: const TMaybe<TString>& database, const TMaybe<TString>& discoveryEndpoint, const TMaybe<EDiscoveryMode>& discoveryMode, - const TMaybe<bool>& enableSsl, - const TMaybe<TString>& caCert, + const TMaybe<TSslCredentials>& sslCredentials, const TMaybe<std::shared_ptr<ICredentialsProviderFactory>>& credentialsProviderFactory) : Connections_(std::move(connections)) - , DbDriverState_(Connections_->GetDriverState(database, discoveryEndpoint, discoveryMode, enableSsl, caCert, credentialsProviderFactory)) + , DbDriverState_(Connections_->GetDriverState(database, discoveryEndpoint, discoveryMode, sslCredentials, credentialsProviderFactory)) { Y_VERIFY(DbDriverState_); } @@ -36,8 +36,7 @@ public: settings.Database_, settings.DiscoveryEndpoint_, settings.DiscoveryMode_, - settings.EnableSsl_, - settings.CaCert_, + settings.SslCredentials_, settings.CredentialsProviderFactory_ ) ) diff --git a/ydb/public/sdk/cpp/client/ydb_common_client/settings.cpp b/ydb/public/sdk/cpp/client/ydb_common_client/settings.cpp index 8bffcd7a5ae..9baa0c1328d 100644 --- a/ydb/public/sdk/cpp/client/ydb_common_client/settings.cpp +++ b/ydb/public/sdk/cpp/client/ydb_common_client/settings.cpp @@ -13,7 +13,7 @@ TCommonClientSettings GetClientSettingsFromConnectionString(const TStringType& c auto connectionInfo = ParseConnectionString(connectionString); settings.Database(connectionInfo.Database); settings.DiscoveryEndpoint(connectionInfo.Endpoint); - settings.EnableSsl(connectionInfo.EnableSsl); + settings.SslCredentials(TSslCredentials(connectionInfo.EnableSsl)); return settings; } diff --git a/ydb/public/sdk/cpp/client/ydb_common_client/settings.h b/ydb/public/sdk/cpp/client/ydb_common_client/settings.h index d7333926390..e73f504e8d8 100644 --- a/ydb/public/sdk/cpp/client/ydb_common_client/settings.h +++ b/ydb/public/sdk/cpp/client/ydb_common_client/settings.h @@ -5,10 +5,13 @@ #include <ydb/public/sdk/cpp/client/ydb_types/ydb.h> #include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/type_switcher.h> +#include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h> #include <functional> namespace NYdb { +using TCertificateAndPrivateKey = std::pair<TStringType, TStringType>; + struct TCommonClientSettings { using TSelf = TCommonClientSettings; @@ -29,10 +32,8 @@ struct TCommonClientSettings { FLUENT_SETTING_OPTIONAL(std::shared_ptr<ICredentialsProviderFactory>, CredentialsProviderFactory); //! Allows to override discovery mode FLUENT_SETTING_OPTIONAL(EDiscoveryMode, DiscoveryMode); - //! Allows to override current Ssl mode - FLUENT_SETTING_OPTIONAL(bool, EnableSsl); - //! Allows to override current Ssl cert - FLUENT_SETTING_OPTIONAL(TStringType, CaCert); + //! Allows to override current Ssl credentials + FLUENT_SETTING_OPTIONAL(TSslCredentials, SslCredentials); }; template<class TDerived> @@ -49,8 +50,7 @@ struct TCommonClientSettingsBase : public TCommonClientSettings { COMMON_CLIENT_SETTINGS_TO_DERIVED(TMaybe<TStringType>, AuthToken); COMMON_CLIENT_SETTINGS_TO_DERIVED(std::shared_ptr<ICredentialsProviderFactory>, CredentialsProviderFactory); COMMON_CLIENT_SETTINGS_TO_DERIVED(EDiscoveryMode, DiscoveryMode); - COMMON_CLIENT_SETTINGS_TO_DERIVED(bool, EnableSsl); - COMMON_CLIENT_SETTINGS_TO_DERIVED(TStringType, CaCert); + COMMON_CLIENT_SETTINGS_TO_DERIVED(TSslCredentials, SslCredentials); #undef COMMON_CLIENT_SETTINGS_TO_DERIVED diff --git a/ydb/public/sdk/cpp/client/ydb_driver/driver.cpp b/ydb/public/sdk/cpp/client/ydb_driver/driver.cpp index fae0334abfd..d7be6064b76 100644 --- a/ydb/public/sdk/cpp/client/ydb_driver/driver.cpp +++ b/ydb/public/sdk/cpp/client/ydb_driver/driver.cpp @@ -9,6 +9,7 @@ #include <library/cpp/logger/log.h> #include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/parser.h> #include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/getenv.h> +#include <ydb/public/sdk/cpp/client/impl/ydb_internal/common/ssl_credentials.h> #include <util/stream/file.h> #include <ydb/public/sdk/cpp/client/resources/ydb_ca.h> @@ -32,8 +33,7 @@ public: size_t GetNetworkThreadsNum() const override { return NetworkThreadsNum; } size_t GetClientThreadsNum() const override { return ClientThreadsNum; } size_t GetMaxQueuedResponses() const override { return MaxQueuedResponses; } - bool IsSslEnabled() const override { return EnableSsl; } - TStringType GetCaCert() const override { return CaCert; } + TSslCredentials GetSslCredentials() const override { return SslCredentials; } TStringType GetDatabase() const override { return Database; } std::shared_ptr<ICredentialsProviderFactory> GetCredentialsProviderFactory() const override { return CredentialsProviderFactory; } EDiscoveryMode GetDiscoveryMode() const override { return DiscoveryMode; } @@ -54,8 +54,7 @@ public: size_t NetworkThreadsNum = 2; size_t ClientThreadsNum = 0; size_t MaxQueuedResponses = 0; - bool EnableSsl = false; - TStringType CaCert; + TSslCredentials SslCredentials; TStringType Database; std::shared_ptr<ICredentialsProviderFactory> CredentialsProviderFactory = CreateInsecureCredentialsProviderFactory(); EDiscoveryMode DiscoveryMode = EDiscoveryMode::Sync; @@ -85,7 +84,7 @@ TDriverConfig::TDriverConfig(const TStringType& connectionString) auto connectionInfo = ParseConnectionString(connectionString); SetEndpoint(connectionInfo.Endpoint); SetDatabase(connectionInfo.Database); - Impl_->EnableSsl = connectionInfo.EnableSsl; + Impl_->SslCredentials.IsEnabled = connectionInfo.EnableSsl; } } @@ -110,8 +109,14 @@ TDriverConfig& TDriverConfig::SetMaxClientQueueSize(size_t sz) { } TDriverConfig& TDriverConfig::UseSecureConnection(const TStringType& cert) { - Impl_->EnableSsl = true; - Impl_->CaCert = cert; + Impl_->SslCredentials.IsEnabled = true; + Impl_->SslCredentials.CaCert = cert; + return *this; +} + +TDriverConfig& TDriverConfig::UseClientCertificate(const TStringType& clientCert, const TStringType& clientPrivateKey) { + Impl_->SslCredentials.Cert = clientCert; + Impl_->SslCredentials.PrivateKey = clientPrivateKey; return *this; } diff --git a/ydb/public/sdk/cpp/client/ydb_driver/driver.h b/ydb/public/sdk/cpp/client/ydb_driver/driver.h index e8027bfad7d..f4589bd367a 100644 --- a/ydb/public/sdk/cpp/client/ydb_driver/driver.h +++ b/ydb/public/sdk/cpp/client/ydb_driver/driver.h @@ -51,6 +51,7 @@ public: //! caCerts - The buffer containing the PEM encoding of the server root certificates. //! If this parameter is empty, the default roots will be used. TDriverConfig& UseSecureConnection(const TStringType& caCerts = TStringType()); + TDriverConfig& UseClientCertificate(const TStringType& clientCert, const TStringType& clientPrivateKey); //! Set token, this option can be overridden for client by ClientSettings TDriverConfig& SetAuthToken(const TStringType& token); //! Set database, this option can be overridden for client by ClientSettings diff --git a/ydb/public/sdk/cpp/client/ydb_persqueue_core/impl/persqueue_impl.h b/ydb/public/sdk/cpp/client/ydb_persqueue_core/impl/persqueue_impl.h index f223b5bc461..cb74163ffc0 100644 --- a/ydb/public/sdk/cpp/client/ydb_persqueue_core/impl/persqueue_impl.h +++ b/ydb/public/sdk/cpp/client/ydb_persqueue_core/impl/persqueue_impl.h @@ -24,7 +24,7 @@ public: // Async discovery mode is used because this client is created inside SDK threads. // See YDB-1231 and YDB-1232. TImpl(const TString& clusterEndpoint, std::shared_ptr<TGRpcConnectionsImpl> connections, const TPersQueueClientSettings& settings) - : TClientImplCommon(std::move(connections), settings.Database_, clusterEndpoint, EDiscoveryMode::Async, settings.EnableSsl_, settings.CaCert_, settings.CredentialsProviderFactory_) + : TClientImplCommon(std::move(connections), settings.Database_, clusterEndpoint, EDiscoveryMode::Async, settings.SslCredentials_, settings.CredentialsProviderFactory_) , Settings(settings) , CustomEndpoint(clusterEndpoint) { diff --git a/ydb/services/datastreams/datastreams_ut.cpp b/ydb/services/datastreams/datastreams_ut.cpp index 839adb01c34..10e5116790d 100644 --- a/ydb/services/datastreams/datastreams_ut.cpp +++ b/ydb/services/datastreams/datastreams_ut.cpp @@ -91,7 +91,7 @@ public: TString location = TStringBuilder() << "localhost:" << grpc; auto driverConfig = TDriverConfig().SetEndpoint(location).SetLog(CreateLogBackend("cerr", TLOG_DEBUG)); if (secure) { - driverConfig.UseSecureConnection(NYdbSslTestData::CaCrt); + driverConfig.UseSecureConnection(TString(NYdbSslTestData::CaCrt)); } else { driverConfig.SetDatabase("/Root/"); } diff --git a/ydb/services/ydb/CMakeLists.txt b/ydb/services/ydb/CMakeLists.txt index 487894646ad..4c7ba850237 100644 --- a/ydb/services/ydb/CMakeLists.txt +++ b/ydb/services/ydb/CMakeLists.txt @@ -6,6 +6,7 @@ # original buildsystem will not be accepted. +find_package(OpenSSL REQUIRED) add_subdirectory(index_ut) add_subdirectory(sdk_credprovider_ut) add_subdirectory(ut) @@ -29,6 +30,7 @@ target_link_libraries(ydb-services-ydb PUBLIC api-grpc-draft api-protos yql-public-types + OpenSSL::OpenSSL ) target_sources(ydb-services-ydb PRIVATE ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_clickhouse_internal.cpp diff --git a/ydb/services/ydb/cert_gen.cpp b/ydb/services/ydb/cert_gen.cpp new file mode 100644 index 00000000000..141b599fc64 --- /dev/null +++ b/ydb/services/ydb/cert_gen.cpp @@ -0,0 +1,499 @@ +#include "cert_gen.h" + +#include <contrib/libs/openssl/include/openssl/bio.h> +#include <contrib/libs/openssl/include/openssl/err.h> +#include <contrib/libs/openssl/include/openssl/evp.h> +#include <contrib/libs/openssl/include/openssl/pem.h> +#include <contrib/libs/openssl/include/openssl/rsa.h> +#include <contrib/libs/openssl/include/openssl/x509.h> +#include <contrib/libs/openssl/include/openssl/x509v3.h> +#include <contrib/libs/openssl/include/openssl/x509_vfy.h> + + +#include <array> +#include <cstring> +#include <iostream> +#include <memory> +#include <stdexcept> + + +using namespace NTest; + +namespace { + +constexpr size_t maxCertSize = 17 * 1024; +constexpr size_t errStringBufSize = 1024; + +#define CHECK(expr, msg) \ + if (!expr) { \ + char errString[errStringBufSize]{0}; \ + ERR_error_string_n(ERR_get_error(), errString, errStringBufSize); \ + std::cerr << msg << std::endl; \ + std::cerr << errString << std::endl; \ + exit (1); \ + } + +template <auto fn> +struct deleter_from_fn { + template <typename T> + constexpr void operator()(T* arg) const { + fn(arg); + } +}; + +using PKeyPtr = std::unique_ptr<EVP_PKEY, deleter_from_fn<&::EVP_PKEY_free>>; +using X509Ptr = std::unique_ptr<X509, deleter_from_fn<&::X509_free>>; +using RSAPtr = std::unique_ptr<RSA, deleter_from_fn<&::RSA_free>>; +using BNPtr = std::unique_ptr<BIGNUM, deleter_from_fn<&::BN_free>>; +using BIOPtr = std::unique_ptr<BIO, deleter_from_fn<&::BIO_free>>; +using ExtPrt = std::unique_ptr<X509_EXTENSION, deleter_from_fn<&::X509_EXTENSION_free>>; +using X509REQPtr = std::unique_ptr<X509_REQ, deleter_from_fn<&::X509_REQ_free>>; +using X509StorePtr = std::unique_ptr<X509_STORE, deleter_from_fn<&::X509_STORE_free>>; +using X509StoreCtxPtr = std::unique_ptr<X509_STORE_CTX, deleter_from_fn<&::X509_STORE_CTX_free>>; + +void FillExtFromProps0(X509V3_CTX* ctx, STACK_OF(X509_EXTENSION) *exts, const TProps& props); +void FillExtFromProps1(X509V3_CTX* ctx, STACK_OF(X509_EXTENSION) *exts, const TProps& props); + +void VerifyCert(const X509Ptr& cert, const X509Ptr& cacert) { + auto store = X509StorePtr(X509_STORE_new()); + X509_STORE_add_cert(store.get(), cacert.get()); + + auto ctx = X509StoreCtxPtr(X509_STORE_CTX_new()); + X509_STORE_CTX_init(ctx.get(), store.get(), cert.get(), NULL); + + int result = X509_verify_cert(ctx.get()); + CHECK(result == 1, "VerifyCert failed."); +} + +PKeyPtr GenerateKeys() { + /* EVP_PKEY structure is for storing an algorithm-independent private key in memory. */ + auto pkey = PKeyPtr(EVP_PKEY_new()); + CHECK(pkey, "Unable to create pkey structure."); + + /* Generate a RSA key and assign it to pkey. + * RSA_generate_key is deprecated. + */ + auto bne = BNPtr(BN_new()); + CHECK(bne, "Unable to create bignum structure."); + + BN_set_word(bne.get(), RSA_F4); + + auto rsa = RSAPtr(RSA_new()); + CHECK(rsa, "Unable to create rsa structure."); + + RSA_generate_key_ex(rsa.get(), 2048, bne.get(), nullptr); + + EVP_PKEY_assign_RSA(pkey.get(), rsa.get()); + + rsa.release(); // mem is grabbed by pkkey + + return std::move(pkey); +} + +int FillNameFromProps(X509_NAME* name, const TProps& props) { + if (!name) { + return 1; + } + + if (!props.Coutry.empty()) { + X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (const unsigned char*)props.Coutry.c_str(), -1, -1, 0); + } + + if (!props.State.empty()) { + X509_NAME_add_entry_by_txt(name, SN_stateOrProvinceName, MBSTRING_ASC, (const unsigned char*)props.State.c_str(), -1, -1, 0); + } + + if (!props.Location.empty()) { + X509_NAME_add_entry_by_txt(name, SN_localityName, MBSTRING_ASC, (const unsigned char*)props.Location.c_str(), -1, -1, 0); + } + + if (!props.Organization.empty()) { + X509_NAME_add_entry_by_txt(name, SN_organizationName, MBSTRING_ASC, (const unsigned char*)props.Organization.c_str(), -1, -1, 0); + } + + if (!props.Unit.empty()) { + X509_NAME_add_entry_by_txt(name, SN_organizationalUnitName, MBSTRING_ASC, (const unsigned char*)props.Unit.c_str(), -1, -1, 0); + } + + if (!props.CommonName.empty()) { + X509_NAME_add_entry_by_txt(name, SN_commonName, MBSTRING_ASC, (const unsigned char*)props.CommonName.c_str(), -1, -1, 0); + } + + return 0; +} + +/* Generates a self-signed x509 certificate. */ +X509Ptr GenerateSelfSignedCertificate(PKeyPtr& pkey, const TProps& props) { + /* Allocate memory for the X509 structure. */ + auto x509 = X509Ptr(X509_new()); + CHECK(x509, "Unable to create X509 structure."); + + X509_set_version(x509.get(), 3); + + /* Set the serial number. */ + constexpr int Serial = 1; + ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), Serial); + + /* This certificate is valid from now until exactly one year from now. */ + X509_gmtime_adj(X509_get_notBefore(x509.get()), 0); + X509_gmtime_adj(X509_get_notAfter(x509.get()), props.SecondsValid); + + int errNo = 0; + /* Set the public key for our certificate. */ + errNo = X509_set_pubkey(x509.get(), pkey.get()); + CHECK(x509,"Error setting public key for X509 structure."); + + /* We want to copy the subject name to the issuer name. */ + X509_NAME* name = X509_get_subject_name(x509.get()); + CHECK(name,"Error getting subject name from X509 structure."); + + /* Set the country code and common name. */ + errNo = FillNameFromProps(name, props); + CHECK(errNo == 0,"Error setting names."); + + errNo = X509_set_subject_name(x509.get(), name); + CHECK(errNo, "Error setting subject name."); + + errNo = X509_set_issuer_name(x509.get(), name); + CHECK(errNo, "Error setting issier name."); + + { // fill ext properties witch don't requere filled issuer indentity + X509V3_CTX ctx; + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, x509.get(), x509.get(), nullptr, nullptr, 0); + + STACK_OF (X509_EXTENSION)* exts = sk_X509_EXTENSION_new_null(); + CHECK(exts, "Unable to create STACK_OF X509_EXTENSION structure."); + + FillExtFromProps0(&ctx, exts, props); + + int count = X509v3_get_ext_count(exts); + for (int i = 0; i < count; ++i) { + auto tmpExt = X509v3_get_ext(exts, i); + errNo = X509_add_ext(x509.get(), tmpExt, -1); + CHECK(errNo, "Error adding ext value"); + tmpExt = nullptr; + } + + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + } + + { // fill ext properties witch requere filled issuer indentity, NID_authority_key_identifier + X509V3_CTX ctx; + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, x509.get(), x509.get(), nullptr, nullptr, 0); + + STACK_OF (X509_EXTENSION)* exts = sk_X509_EXTENSION_new_null(); + CHECK(exts, "Unable to create STACK_OF X509_EXTENSION structure."); + + FillExtFromProps1(&ctx, exts, props); + + int count = X509v3_get_ext_count(exts); + for (int i = 0; i < count; ++i) { + auto tmpExt = X509v3_get_ext(exts, i); + errNo = X509_add_ext(x509.get(), tmpExt, -1); + CHECK(errNo, "Error adding ext value"); + tmpExt = nullptr; + } + + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + } + + + /* Actually sign the certificate with our key. */ + errNo = X509_sign(x509.get(), pkey.get(), EVP_sha1()); + CHECK(errNo, "Error signing certificate."); + + return std::move(x509); +} + +std::string WriteAsPEM(PKeyPtr& pkey) { + std::array<char, maxCertSize> buf{0}; + + auto bio = BIOPtr(BIO_new(BIO_s_mem())); + CHECK(bio, "Unable to create BIO structure."); + + PEM_write_bio_RSAPrivateKey(bio.get(), EVP_PKEY_get0_RSA(pkey.get()), nullptr, nullptr, 0, nullptr, nullptr); + + BIO_read(bio.get(), buf.data(), maxCertSize - 1); + + return std::string(buf.data()); +} + +std::string WriteAsPEM(X509Ptr& cert) { + std::array<char, maxCertSize> buf{0}; + + auto bio = BIOPtr(BIO_new(BIO_s_mem())); + CHECK(bio, "Unable to create BIO structure."); + + PEM_write_bio_X509(bio.get(), cert.get()); + + BIO_read(bio.get(), buf.data(), maxCertSize - 1); + + return std::string(buf.data()); +} + +X509Ptr ReadCertAsPEM(const std::string& cert) { + auto bio = BIOPtr(BIO_new_mem_buf(cert.data(), cert.size())); + CHECK(bio,"Unable to create BIO structure."); + + auto x509 = X509Ptr(PEM_read_bio_X509(bio.get(), NULL, NULL, NULL)); + CHECK(x509, "failed to load certificate"); + + return std::move(x509); +} + +PKeyPtr ReadPrivateKeyAsPEM(const std::string& key) { + auto bio = BIOPtr(BIO_new_mem_buf(key.data(), key.size())); + CHECK(bio,"Unable to create BIO structure."); + + auto pkey = PKeyPtr(PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL)); + CHECK(pkey, "failed to private key certificate"); + + return std::move(pkey); +} + +void add_ext(X509V3_CTX* ctx, STACK_OF(X509_EXTENSION)* exts, int nid, const char *value) { + X509_EXTENSION *ex; + ex = X509V3_EXT_conf_nid(NULL, ctx, nid, value); + CHECK(ex, "failed to add ext value " << value); + sk_X509_EXTENSION_push(exts, ex); +} + +void FillExtFromProps0(X509V3_CTX* ctx, STACK_OF(X509_EXTENSION) *exts, const TProps& props) { + CHECK(ctx, "no context is provided"); + + if (!props.AltNames.empty()) { + bool first = true; + std::string concat; + for (const auto& an: props.AltNames) { + if (an.empty()) { + continue; + } + if (!first) { + concat += ","; + } else { + first = false; + } + + concat += an; + } + add_ext(ctx, exts, NID_subject_alt_name, concat.c_str()); + } + + if (!props.BasicConstraints.empty()) { + add_ext(ctx, exts, NID_basic_constraints, props.BasicConstraints.c_str()); + } + + if (!props.KeyUsage.empty()) { + add_ext(ctx, exts, NID_key_usage, props.KeyUsage.c_str()); + } + + if (!props.ExtKeyUsage.empty()) { + add_ext(ctx, exts, NID_ext_key_usage, props.ExtKeyUsage.c_str()); + } + + if (!props.SubjectKeyIdentifier.empty()) { + add_ext(ctx, exts, NID_subject_key_identifier, props.SubjectKeyIdentifier.c_str()); + } + + if (!props.NsComment.empty()) { + add_ext(ctx, exts, NID_netscape_comment, props.NsComment.c_str()); + } +} + +void FillExtFromProps1(X509V3_CTX* ctx, STACK_OF(X509_EXTENSION) *exts, const TProps& props) { + CHECK(ctx, "no context is provided"); + + if (!props.AuthorityKeyIdentifier.empty()) { + add_ext(ctx, exts, NID_authority_key_identifier, props.AuthorityKeyIdentifier.c_str()); + } +} + +X509REQPtr GenerateRequest(PKeyPtr& pkey, const TProps& props) { + auto request = X509REQPtr(X509_REQ_new()); + CHECK(request, "Error creating new X509_REQ structure."); + + int errNo = 0; + + errNo = X509_REQ_set_pubkey(request.get(), pkey.get()); + CHECK(errNo, "Error setting public key for X509_REQ structure."); + + X509_NAME* name = X509_REQ_get_subject_name(request.get()); + CHECK(name, "Error setting public key for X509_REQ structure."); + + errNo = FillNameFromProps(name, props); + CHECK(errNo == 0,"Error setting names."); + + { + X509V3_CTX ctx; + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, nullptr, nullptr, request.get(), nullptr, 0); + + STACK_OF (X509_EXTENSION)* exts = sk_X509_EXTENSION_new_null(); + FillExtFromProps0(&ctx, exts, props); + X509_REQ_add_extensions(request.get(), exts); + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + } + + errNo = X509_REQ_sign(request.get(), pkey.get(), EVP_md5()); + CHECK(errNo, "Error MD5 signing X509_REQ structure."); + + return std::move(request); +} + +X509Ptr SingRequest(X509REQPtr& request, X509Ptr& rootCert, PKeyPtr& rootKey, const TProps& props) { + auto* pktmp = X509_REQ_get0_pubkey(request.get()); // X509_REQ_get0_pubkey returns the key, that shouldn't freed + CHECK(pktmp, "Error unpacking public key from request."); + + int errNo = 0; + errNo = X509_REQ_verify(request.get(), pktmp); + CHECK(errNo > 0, "Error verification request signature."); + + auto x509 = X509Ptr(X509_new()); + CHECK(x509, "Unable to create X509 structure."); + + X509_set_version(x509.get(), 3); + + /* Set the serial number. */ + constexpr int Serial = 2; + ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), Serial); + + /* This certificate is valid from now until exactly one year from now. */ + X509_gmtime_adj(X509_get_notBefore(x509.get()), 0); + X509_gmtime_adj(X509_get_notAfter(x509.get()), props.SecondsValid); + + X509_set_pubkey(x509.get(), pktmp); + + X509_set_subject_name(x509.get(), X509_REQ_get_subject_name(request.get())); + X509_set_issuer_name(x509.get(), X509_get_subject_name(rootCert.get())); + + { + + X509V3_CTX ctx; + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, rootCert.get(), x509.get(), request.get(), nullptr, 0); + + // fill ext properties witch don't requere filled issuer indentity, actually copy them from request + STACK_OF(X509_EXTENSION)* exts = X509_REQ_get_extensions(request.get()); + CHECK(exts, "Unable to get STACK_OF X509_EXTENSION structure from request."); + + // fill ext properties witch requere filled issuer indentity, NID_authority_key_identifier + FillExtFromProps1(&ctx, exts, props); + + int count = X509v3_get_ext_count(exts); + for (int i = 0; i < count; ++i) { + auto tmpExt = X509v3_get_ext(exts, i); + errNo = X509_add_ext(x509.get(), tmpExt, -1); + CHECK(errNo, "Error adding ext value"); + tmpExt = nullptr; + } + + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + } + + /* Actually sign the certificate with our key. */ + errNo = X509_sign(x509.get(), rootKey.get(), EVP_sha1()); + CHECK(errNo, "Error signing certificate."); + + pktmp = nullptr; + + return std::move(x509); +} + +} + +namespace NTest { + +TCertAndKey GenerateCA(const TProps& props) { + auto keys = GenerateKeys(); + auto cert = GenerateSelfSignedCertificate(keys, props); + + TCertAndKey result; + result.Certificate = WriteAsPEM(cert); + result.PrivateKey = WriteAsPEM(keys); + + return result; +} + +TCertAndKey GenerateSignedCert(const TCertAndKey& rootCA, const TProps& props) { + auto keys = GenerateKeys(); + auto request = GenerateRequest(keys, props); + + auto rootCert = ReadCertAsPEM(rootCA.Certificate); + auto rootKey = ReadPrivateKeyAsPEM(rootCA.PrivateKey); + auto cert = SingRequest(request, rootCert, rootKey, props); // NID_authority_key_identifier must see ca + + TCertAndKey result; + result.Certificate = WriteAsPEM(cert); + result.PrivateKey = WriteAsPEM(keys); + + return result; +} + +void VerifyCert(const std::string& cert, const std::string& caCert) { + auto rootCert = ReadCertAsPEM(caCert); + auto otherCert = ReadCertAsPEM(cert); + + VerifyCert(otherCert, rootCert); +} + +TProps TProps::AsCA() { + TProps props; + + props.SecondsValid = 3*365 * 24 * 60 *60; // 3 years + props.Coutry = "RU"; + props.State = "MSK"; + props.Location = "MSK"; + props.Organization = "YA"; + props.Unit = "UtTest"; + props.CommonName = "testCA"; + + props.AltNames = {"IP:127.0.0.1", "DNS:localhost"}; + + props.BasicConstraints = "critical,CA:TRUE"; + props.AuthorityKeyIdentifier = "keyid:always,issuer"; + props.SubjectKeyIdentifier = "hash"; + props.KeyUsage = "critical,keyCertSign,cRLSign"; + props.ExtKeyUsage = ""; + props.NsComment = "Test Generated Certificate for self-signed CA"; + + return props; +} + +TProps TProps::AsServer() { + TProps props = AsCA(); + + props.CommonName = "localhost"; + props.AltNames.push_back("DNS:*.yandex.ru"); + + props.BasicConstraints = "CA:FALSE"; + props.AuthorityKeyIdentifier = "keyid,issuer"; + props.KeyUsage = "digitalSignature,nonRepudiation,keyEncipherment"; + props.ExtKeyUsage = "serverAuth"; + props.NsComment = "Test Generated Certificate for test Server"; + + return props; +} + +TProps TProps::AsClient() { + TProps props = AsServer(); + + props.ExtKeyUsage = "clientAuth"; + props.NsComment = "Test Generated Certificate for test Client"; + + return props; +} + +TProps TProps::AsClientServer() { + TProps props = AsClient(); + + props.ExtKeyUsage = "serverAuth,clientAuth"; + props.NsComment = "Test Generated Certificate for test Client/Server"; + + return props; +} + +TProps& TProps::WithValid(TDuration duration) { SecondsValid = duration.Seconds(); return *this; } + +} diff --git a/ydb/services/ydb/cert_gen.h b/ydb/services/ydb/cert_gen.h new file mode 100644 index 00000000000..3de82e2e0e4 --- /dev/null +++ b/ydb/services/ydb/cert_gen.h @@ -0,0 +1,45 @@ +#pragma once + +#include <util/datetime/base.h> + +#include <string> +#include <vector> + +namespace NTest { + + struct TCertAndKey { + std::string Certificate; + std::string PrivateKey; + }; + + struct TProps { + long SecondsValid = 0; + std::string Coutry; // C + std::string State; // ST + std::string Location; // L + std::string Organization; // O + std::string Unit; // OU + std::string CommonName; // CN + + std::vector<std::string> AltNames; // X509v3 Subject Alternative Name + + std::string BasicConstraints; // X509v3 Basic Constraints + std::string AuthorityKeyIdentifier; // X509v3 Authority Key Identifier + std::string SubjectKeyIdentifier; // X509v3 Subject Key Identifier + std::string KeyUsage; // X509v3 Key Usage + std::string ExtKeyUsage; // X509v3 Extended Key Usage + std::string NsComment; // Netscape Comment + + static TProps AsCA(); + static TProps AsServer(); + static TProps AsClient(); + static TProps AsClientServer(); + + TProps& WithValid(TDuration duration); + }; + + TCertAndKey GenerateCA(const TProps& props); + TCertAndKey GenerateSignedCert(const TCertAndKey& ca, const TProps& props); + void VerifyCert(const std::string& cert, const std::string& caCert); +}; + diff --git a/ydb/services/ydb/ut/CMakeLists.darwin.txt b/ydb/services/ydb/ut/CMakeLists.darwin.txt index 1fb9b5abfd9..52ffe38ec62 100644 --- a/ydb/services/ydb/ut/CMakeLists.darwin.txt +++ b/ydb/services/ydb/ut/CMakeLists.darwin.txt @@ -27,6 +27,8 @@ target_link_libraries(ydb-services-ydb-ut PUBLIC library-cpp-svnversion kqp-ut-common core-testlib-default + core-grpc_services-base + ydb-core-testlib yql-minikql-dom yql-minikql-jsonpath public-lib-experimental @@ -55,6 +57,7 @@ target_sources(ydb-services-ydb-ut PRIVATE ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_index_table_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_import_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_ut.cpp + ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_client_certs_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_s3_internal_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_scripting_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_table_ut.cpp @@ -66,6 +69,7 @@ target_sources(ydb-services-ydb-ut PRIVATE ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_monitoring_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ut/json_udf.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ut/re2_udf.cpp + ${CMAKE_SOURCE_DIR}/ydb/services/ydb/cert_gen.cpp ) add_test( NAME diff --git a/ydb/services/ydb/ut/CMakeLists.linux-aarch64.txt b/ydb/services/ydb/ut/CMakeLists.linux-aarch64.txt index 346b86dd319..49616e6f0ab 100644 --- a/ydb/services/ydb/ut/CMakeLists.linux-aarch64.txt +++ b/ydb/services/ydb/ut/CMakeLists.linux-aarch64.txt @@ -27,6 +27,8 @@ target_link_libraries(ydb-services-ydb-ut PUBLIC library-cpp-svnversion kqp-ut-common core-testlib-default + core-grpc_services-base + ydb-core-testlib yql-minikql-dom yql-minikql-jsonpath public-lib-experimental @@ -57,6 +59,7 @@ target_sources(ydb-services-ydb-ut PRIVATE ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_index_table_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_import_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_ut.cpp + ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_client_certs_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_s3_internal_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_scripting_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_table_ut.cpp @@ -68,6 +71,7 @@ target_sources(ydb-services-ydb-ut PRIVATE ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_monitoring_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ut/json_udf.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ut/re2_udf.cpp + ${CMAKE_SOURCE_DIR}/ydb/services/ydb/cert_gen.cpp ) add_test( NAME diff --git a/ydb/services/ydb/ut/CMakeLists.linux.txt b/ydb/services/ydb/ut/CMakeLists.linux.txt index 82a7bd8b799..27ff76b7b92 100644 --- a/ydb/services/ydb/ut/CMakeLists.linux.txt +++ b/ydb/services/ydb/ut/CMakeLists.linux.txt @@ -29,6 +29,8 @@ target_link_libraries(ydb-services-ydb-ut PUBLIC library-cpp-svnversion kqp-ut-common core-testlib-default + core-grpc_services-base + ydb-core-testlib yql-minikql-dom yql-minikql-jsonpath public-lib-experimental @@ -59,6 +61,7 @@ target_sources(ydb-services-ydb-ut PRIVATE ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_index_table_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_import_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_ut.cpp + ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_client_certs_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_s3_internal_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_scripting_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_table_ut.cpp @@ -70,6 +73,7 @@ target_sources(ydb-services-ydb-ut PRIVATE ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ydb_monitoring_ut.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ut/json_udf.cpp ${CMAKE_SOURCE_DIR}/ydb/services/ydb/ut/re2_udf.cpp + ${CMAKE_SOURCE_DIR}/ydb/services/ydb/cert_gen.cpp ) add_test( NAME diff --git a/ydb/services/ydb/ydb_client_certs_ut.cpp b/ydb/services/ydb/ydb_client_certs_ut.cpp new file mode 100644 index 00000000000..6fcc7a203a0 --- /dev/null +++ b/ydb/services/ydb/ydb_client_certs_ut.cpp @@ -0,0 +1,623 @@ +#include <library/cpp/testing/unittest/tests_data.h> +#include <library/cpp/testing/unittest/registar.h> + +#include <grpc++/client_context.h> +#include <grpc++/create_channel.h> + +#include <ydb/core/base/storage_pools.h> +#include <ydb/core/base/location.h> +#include <ydb/core/protos/flat_scheme_op.pb.h> +#include <ydb/core/scheme/scheme_tablecell.h> +#include <ydb/core/testlib/test_client.h> +#include <ydb/core/driver_lib/cli_config_base/config_base.h> +#include <ydb/core/client/server/dynamic_node_auth_processor.h> + +#include <ydb/public/api/grpc/ydb_scheme_v1.grpc.pb.h> +#include <ydb/public/api/grpc/ydb_operation_v1.grpc.pb.h> +#include <ydb/public/api/grpc/ydb_table_v1.grpc.pb.h> +#include <ydb/public/api/grpc/draft/dummy.grpc.pb.h> +#include <ydb/public/api/protos/ydb_table.pb.h> + +#include <library/cpp/grpc/client/grpc_client_low.h> + +#include <google/protobuf/any.h> + +#include <ydb/library/yql/core/issue/yql_issue.h> +#include <ydb/library/yql/public/issue/yql_issue.h> +#include <ydb/library/yql/public/issue/yql_issue_message.h> + +#include <ydb/public/sdk/cpp/client/ydb_params/params.h> +#include <ydb/public/sdk/cpp/client/ydb_result/result.h> +#include <ydb/public/sdk/cpp/client/ydb_scheme/scheme.h> +#include <ydb/public/sdk/cpp/client/ydb_table/table.h> +#include <ydb/public/sdk/cpp/client/resources/ydb_resources.h> + +#include <ydb/public/lib/deprecated/kicli/kicli.h> + +#include "ydb_common_ut.h" +#include "cert_gen.h" + +#include <util/generic/ymath.h> + +namespace NKikimr { + +using namespace Tests; +using namespace NYdb; +using namespace NYdb::NTable; +using namespace NYdb::NScheme; + +struct TKikimrTestWithServerCert : TKikimrTestWithAuthAndSsl { + static constexpr bool SSL = true; + + static const NTest::TCertAndKey& GetCACertAndKey() { + static const NTest::TCertAndKey ca = NTest::GenerateCA(NTest::TProps::AsCA()); + return ca; + } + + static const NTest::TCertAndKey& GetServertCert() { + static const NTest::TCertAndKey server = NTest::GenerateSignedCert(GetCACertAndKey(), NTest::TProps::AsServer()); + return server; + } + + static TString GetCaCrt() { + return GetCACertAndKey().Certificate.c_str(); + } + + static TString GetServerCrt() { + return GetServertCert().Certificate.c_str(); + } + + static TString GetServerKey() { + return GetServertCert().PrivateKey.c_str(); + } +}; + +NKikimrConfig::TClientCertificateAuthorization::TSubjectTerm MakeSubjectTerm(TString name, const TVector<TString>& values, const TVector<TString>& suffixes = {}) { + NKikimrConfig::TClientCertificateAuthorization::TSubjectTerm term; + term.SetShortName(name); + for (const auto& val: values) { + *term.MutableValues()->Add() = val; + } + for (const auto& suf: suffixes) { + *term.MutableSuffixes()->Add() = suf; + } + return term; +} + +using TKikimrServerWithOutCertVerification = TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithServerCert>; + +struct TKikimrServerWithCertVerification: public TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithServerCert> { + using TBase = TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithServerCert>; + + TKikimrServerWithCertVerification() + : TBase(GetAppConfig()) + {} + + static NKikimrConfig::TAppConfig GetAppConfig() { + auto config = NKikimrConfig::TAppConfig(); + + config.MutableFeatureFlags()->SetEnableDynamicNodeAuthorization(true); + + auto& dynNodeDefinition = *config.MutableClientCertificateAuthorization()->MutableDynamicNodeAuthorization(); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("C", {"RU"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("ST", {"MSK"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("L", {"MSK"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("O", {"YA"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("OU", {"UtTest"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("CN", {"localhost"}, {".yandex.ru"}); + + return config; + } +}; + +struct TKikimrServerWithCertVerificationAndWrongIndentity : public TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithServerCert> { + using TBase = TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithServerCert>; + + TKikimrServerWithCertVerificationAndWrongIndentity() + : TBase(GetAppConfig()) + {} + + static NKikimrConfig::TAppConfig GetAppConfig() { + auto config = NKikimrConfig::TAppConfig(); + + config.MutableFeatureFlags()->SetEnableDynamicNodeAuthorization(true); + + auto& dynNodeDefinition = *config.MutableClientCertificateAuthorization()->MutableDynamicNodeAuthorization(); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("C", {"WRONG"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("ST", {"MSK"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("L", {"MSK"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("O", {"YA"}); + *dynNodeDefinition.AddSubjectTerms() = MakeSubjectTerm("OU", {"UtTest"}); + + return config; + } +}; + +Y_UNIT_TEST_SUITE(TGRpcClientCerts) { + +Y_UNIT_TEST(TestGenerateAndVerify) { + using namespace NTest; + TCertAndKey ca = GenerateCA(TProps::AsCA()); + + TCertAndKey server = GenerateSignedCert(ca, TProps::AsServer()); + VerifyCert(server.Certificate, ca.Certificate); + + TCertAndKey client = GenerateSignedCert(ca, TProps::AsClient()); + VerifyCert(client.Certificate, ca.Certificate); + + TCertAndKey clientServer = GenerateSignedCert(ca, TProps::AsClientServer()); + VerifyCert(clientServer.Certificate, ca.Certificate); +} + +Y_UNIT_TEST(TestClientCertAuthorizationParamsMatch) { + { + TDynamicNodeAuthorizationParams authParams; + TDynamicNodeAuthorizationParams::TDistinguishedName dn; + dn.AddRelativeDistinguishedName(TDynamicNodeAuthorizationParams::TRelativeDistinguishedName("C").AddValue("RU")) + .AddRelativeDistinguishedName(TDynamicNodeAuthorizationParams::TRelativeDistinguishedName("ST").AddValue("MSK")) + .AddRelativeDistinguishedName(TDynamicNodeAuthorizationParams::TRelativeDistinguishedName("L").AddValue("MSK")) + .AddRelativeDistinguishedName(TDynamicNodeAuthorizationParams::TRelativeDistinguishedName("O").AddValue("YA")) + .AddRelativeDistinguishedName(TDynamicNodeAuthorizationParams::TRelativeDistinguishedName("OU").AddValue("UtTest")) + .AddRelativeDistinguishedName(TDynamicNodeAuthorizationParams::TRelativeDistinguishedName("CN").AddValue("localhost").AddSuffix(".yandex.ru")); + authParams.AddCertSubjectDescription(dn); + + { + TMap<TString, TString> subjectTerms; + subjectTerms["C"] = "RU"; + subjectTerms["ST"] = "MSK"; + subjectTerms["L"] = "MSK"; + subjectTerms["O"] = "YA"; + subjectTerms["OU"] = "UtTest"; + subjectTerms["CN"] = "localhost"; + + UNIT_ASSERT(authParams.IsSubjectDescriptionMatched(subjectTerms)); + } + + { + TMap<TString, TString> subjectTerms; + subjectTerms["C"] = "RU"; + subjectTerms["ST"] = "MSK"; + subjectTerms["L"] = "MSK"; + subjectTerms["O"] = "YA"; + subjectTerms["OU"] = "UtTest"; + subjectTerms["CN"] = "test.yandex.ru"; + + UNIT_ASSERT(authParams.IsSubjectDescriptionMatched(subjectTerms)); + } + + { + TMap<TString, TString> subjectTerms; + subjectTerms["C"] = "RU"; + subjectTerms["ST"] = "MSK"; + subjectTerms["L"] = "MSK"; + subjectTerms["O"] = "YA"; + subjectTerms["OU"] = "UtTest"; + subjectTerms["CN"] = "test.yandex.ru"; + subjectTerms["ELSE"] = "WhatEver"; + + UNIT_ASSERT(authParams.IsSubjectDescriptionMatched(subjectTerms)); + } + + { + TMap<TString, TString> subjectTerms; + subjectTerms["C"] = "WRONG"; + subjectTerms["ST"] = "MSK"; + subjectTerms["L"] = "MSK"; + subjectTerms["O"] = "YA"; + subjectTerms["OU"] = "UtTest"; + subjectTerms["CN"] = "test.yandex.ru"; + + UNIT_ASSERT(!authParams.IsSubjectDescriptionMatched(subjectTerms)); + } + + { + TMap<TString, TString> subjectTerms; + subjectTerms["C"] = "RU"; + subjectTerms["ST"] = "MSK"; + subjectTerms["L"] = "MSK"; + subjectTerms["O"] = "YA"; + subjectTerms["OU"] = "UtTest"; + subjectTerms["CN"] = "test.not-yandex.ru"; + + UNIT_ASSERT(!authParams.IsSubjectDescriptionMatched(subjectTerms)); + } + + { + TMap<TString, TString> subjectTerms; + //subjectTerms["C"] = "RU"; + subjectTerms["ST"] = "MSK"; + subjectTerms["L"] = "MSK"; + subjectTerms["O"] = "YA"; + subjectTerms["OU"] = "UtTest"; + subjectTerms["CN"] = "test.yandex.ru"; + + UNIT_ASSERT(!authParams.IsSubjectDescriptionMatched(subjectTerms)); + } + } +} + +Y_UNIT_TEST(TestAllCertIsOk) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + const NTest::TCertAndKey& clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer()); + + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .UseSecureConnection(caCert.Certificate.c_str()) + .UseClientCertificate(clientServerCert.Certificate.c_str(),clientServerCert.PrivateKey.c_str()) + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(!sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); + UNIT_ASSERT_EQUAL(sessionValue.GetStatus(), EStatus::SUCCESS); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + connection.Stop(true); +} + +Y_UNIT_TEST(TestWrongCertIndentity) { + TKikimrServerWithCertVerificationAndWrongIndentity server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + const NTest::TCertAndKey& clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer()); + + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .UseSecureConnection(caCert.Certificate.c_str()) + .UseClientCertificate(clientServerCert.Certificate.c_str(), clientServerCert.PrivateKey.c_str()) + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(!sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); // do not authorize table service through cert + UNIT_ASSERT_EQUAL(sessionValue.GetStatus(), EStatus::SUCCESS); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + connection.Stop(true); +} + +Y_UNIT_TEST(TestIncorrectUsageClientCertFails) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + const NTest::TCertAndKey& serverCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsServer()); // client or client-server is allowed, not just server + + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .UseSecureConnection(caCert.Certificate.c_str()) + .UseClientCertificate(serverCert.Certificate.c_str(), serverCert.PrivateKey.c_str()) + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + connection.Stop(true); +} + +Y_UNIT_TEST(TestCorruptedCertFails) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer()); + if (clientServerCert.Certificate[50] != 'a') { + clientServerCert.Certificate[50] = 'a'; + } else { + clientServerCert.Certificate[50] = 'b'; + } + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .UseSecureConnection(caCert.Certificate.c_str()) + .UseClientCertificate(clientServerCert.Certificate.c_str(), clientServerCert.PrivateKey.c_str()) + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + connection.Stop(true); +} + +Y_UNIT_TEST(TestCorruptedKeyFails) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer()); + if (clientServerCert.PrivateKey[20] != 'a') { + clientServerCert.PrivateKey[20] = 'a'; + } else { + clientServerCert.Certificate[20] = 'b'; + } + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .UseSecureConnection(caCert.Certificate.c_str()) + .UseClientCertificate(clientServerCert.Certificate.c_str(), clientServerCert.PrivateKey.c_str()) + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + connection.Stop(true); +} + +Y_UNIT_TEST(TestExpiredCertFails) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer().WithValid(TDuration::Seconds(2))); + + // wait intil cert expires + Sleep(TDuration::Seconds(10)); + + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .UseSecureConnection(caCert.Certificate.c_str()) + .UseClientCertificate(clientServerCert.Certificate.c_str(), clientServerCert.PrivateKey.c_str()) + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + connection.Stop(true); +} + +Y_UNIT_TEST(TestServerWithoutCertVerificationAndExpiredCertWorks) { + TKikimrServerWithOutCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer().WithValid(TDuration::Seconds(2))); + + // wait intil cert expires + Sleep(TDuration::Seconds(10)); + + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .UseSecureConnection(caCert.Certificate.c_str()) + .UseClientCertificate(clientServerCert.Certificate.c_str(), clientServerCert.PrivateKey.c_str()) + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(!sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); + UNIT_ASSERT_EQUAL(sessionValue.GetStatus(), EStatus::SUCCESS); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + connection.Stop(true); +} + +Y_UNIT_TEST(TestClientWithoutCertPassed) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .UseSecureConnection(caCert.Certificate.c_str()) + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(!sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); + UNIT_ASSERT_EQUAL(sessionValue.GetStatus(), EStatus::SUCCESS); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + connection.Stop(true); +} + +NClient::TKikimr GetKikimr(const TString& addr, const NTest::TCertAndKey& caCert, const NTest::TCertAndKey& clientServerCert) { + NGrpc::TGRpcClientConfig grpcConfig(addr, TDuration::Seconds(15)); + grpcConfig.EnableSsl = true; + grpcConfig.SslCredentials = {.pem_root_certs = caCert.Certificate.c_str(), + .pem_private_key = clientServerCert.PrivateKey.c_str(), + .pem_cert_chain = clientServerCert.Certificate.c_str()}; + grpcConfig.LoadBalancingPolicy = "round_robin"; + + return NClient::TKikimr(grpcConfig); +} + +THolder<NClient::TRegistrationResult> TryToRegisterDynamicNode( + NClient::TKikimr& kikimr, + const TString &domainName, + const TString &nodeHost, + const TString &nodeAddress, + const TString &nodeResolveHost, + ui16 interconnectPort) +{ + auto registrant = kikimr.GetNodeRegistrant(); + + NActorsInterconnect::TNodeLocation location; + location.SetDataCenter("DataCenter"); + location.SetRack("Rack"); + location.SetUnit("Body"); + TNodeLocation loc(location); + + NActorsInterconnect::TNodeLocation legacy; + legacy.SetDataCenterNum(DataCenterFromString("DataCenter")); + legacy.SetRoomNum(0); + legacy.SetRackNum(RackFromString("Rack")); + legacy.SetBodyNum(2); + loc.InheritLegacyValue(TNodeLocation(legacy)); + + return MakeHolder<NClient::TRegistrationResult> + (registrant.SyncRegisterNode(ToString(domainName), + nodeHost, + interconnectPort, + nodeAddress, + nodeResolveHost, + std::move(loc), + false)); +} + +Y_UNIT_TEST(TestServerWithCertVerificationClientWithCertCallsRegisterNode) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer()); + + NClient::TKikimr kikimr = GetKikimr(location, caCert, clientServerCert); + + Cerr << "Trying to register node" << Endl; + + auto resp = TryToRegisterDynamicNode(kikimr, "Root", "localhost", "localhost", "localhost", GetRandomPort()); + UNIT_ASSERT_C(resp->IsSuccess(), resp->GetErrorMessage()); + + Cerr << "Register node result " << resp->Record().ShortUtf8DebugString() << Endl; +} + +Y_UNIT_TEST(TestServerWithCertVerificationClientWithoutCertCallsRegisterNodeFails) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey noCert; + + NClient::TKikimr kikimr = GetKikimr(location, caCert, noCert); + + Cerr << "Trying to register node" << Endl; + + auto resp = TryToRegisterDynamicNode(kikimr, "Root", "localhost", "localhost", "localhost", GetRandomPort()); + UNIT_ASSERT_C(!resp->IsSuccess(), resp->GetErrorMessage()); + UNIT_ASSERT_STRINGS_EQUAL(resp->GetErrorMessage(), "Cannot authorize node. Node has not provided certificate"); + + Cerr << "Register node result " << resp->Record().ShortUtf8DebugString() << Endl; +} + +Y_UNIT_TEST(TestServerWithoutCertVerificationClientWithCertCallsRegisterNode) { + TKikimrServerWithOutCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer()); + + NClient::TKikimr kikimr = GetKikimr(location, caCert, clientServerCert); + + Cerr << "Trying to register node" << Endl; + + auto resp = TryToRegisterDynamicNode(kikimr, "Root", "localhost", "localhost", "localhost", GetRandomPort()); + UNIT_ASSERT_C(resp->IsSuccess(), resp->GetErrorMessage()); + + Cerr << "Register node result " << resp->Record().ShortUtf8DebugString() << Endl; +} + +Y_UNIT_TEST(TestServerWithoutCertVerificationClientWithoutCertCallsRegisterNode) { + TKikimrServerWithOutCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey noCert; + + NClient::TKikimr kikimr = GetKikimr(location, caCert, noCert); + + Cerr << "Trying to register node" << Endl; + + auto resp = TryToRegisterDynamicNode(kikimr, "Root", "localhost", "localhost", "localhost", GetRandomPort()); + UNIT_ASSERT_C(resp->IsSuccess(), resp->GetErrorMessage()); + + Cerr << "Register node result " << resp->Record().ShortUtf8DebugString() << Endl; +} + +Y_UNIT_TEST(TestServerWithWrongIndentityClientWithCertCallsRegisterNodeFails) { + TKikimrServerWithCertVerificationAndWrongIndentity server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + const NTest::TCertAndKey& caCert = TKikimrTestWithServerCert::GetCACertAndKey(); + NTest::TCertAndKey clientServerCert = NTest::GenerateSignedCert(caCert, NTest::TProps::AsClientServer()); + + NClient::TKikimr kikimr = GetKikimr(location, caCert, clientServerCert); + + Cerr << "Trying to register node" << Endl; + + auto resp = TryToRegisterDynamicNode(kikimr, "Root", "localhost", "localhost", "localhost", GetRandomPort()); + UNIT_ASSERT_C(!resp->IsSuccess(), resp->GetErrorMessage()); + UNIT_ASSERT_STRINGS_EQUAL(resp->GetErrorMessage(), "Cannot authorize node by certificate"); + + Cerr << "Register node result " << resp->Record().ShortUtf8DebugString() << Endl; +} + +Y_UNIT_TEST(TestInsecureClient) { + TKikimrServerWithCertVerification server; + ui16 grpc = server.GetPort(); + TString location = TStringBuilder() << "localhost:" << grpc; + + auto connection = NYdb::TDriver( + TDriverConfig() + .SetAuthToken("test_user@builtin") + .SetEndpoint(location)); + + auto client = NYdb::NTable::TTableClient(connection); + std::function<void(const TAsyncCreateSessionResult& future)> createSessionHandler = + [client] (const TAsyncCreateSessionResult& future) mutable { + const auto& sessionValue = future.GetValue(); + UNIT_ASSERT_C(sessionValue.IsTransportError(), sessionValue.GetIssues().ToString()); + }; + + client.CreateSession().Apply(createSessionHandler).Wait(); + + connection.Stop(true); +} + +} + +} // namespace NKikimr diff --git a/ydb/services/ydb/ydb_common_ut.h b/ydb/services/ydb/ydb_common_ut.h index 98dd10c49f7..e8b4fa75bdf 100644 --- a/ydb/services/ydb/ydb_common_ut.h +++ b/ydb/services/ydb/ydb_common_ut.h @@ -25,6 +25,12 @@ struct TKikimrTestSettings { static constexpr bool AUTH = false; static constexpr bool PrecreatePools = true; static constexpr bool EnableSystemViews = true; + + static TString GetCaCrt() { return NYdbSslTestData::CaCrt; } + static TString GetServerCrt() { return NYdbSslTestData::ServerCrt; } + static TString GetServerKey() { return NYdbSslTestData::ServerKey; } + + static NKikimr::TDynamicNodeAuthorizationParams GetCertAuthParams() {return {}; } }; struct TKikimrTestWithAuth : TKikimrTestSettings { @@ -114,9 +120,12 @@ public: } grpcOption.SetPort(grpc); if (TestSettings::SSL) { - NGrpc::TSslData sslData{NYdbSslTestData::ServerCrt, - NYdbSslTestData::ServerKey, - NYdbSslTestData::CaCrt}; + NGrpc::TSslData sslData; + sslData.Cert = TestSettings::GetServerCrt(); + sslData.Key = TestSettings::GetServerKey(); + sslData.Root =TestSettings::GetCaCrt(); + sslData.DoRequestClientCertificate = appConfig.GetFeatureFlags().GetEnableDynamicNodeAuthorization() && appConfig.GetClientCertificateAuthorization().HasDynamicNodeAuthorization(); + grpcOption.SetSslData(sslData); } Server_->EnableGRpc(grpcOption); |