Skip to content

Commit c5b531e

Browse files
authored
Client certificates options in ydb cli (ydb-platform#8406)
1 parent 1547cff commit c5b531e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+962
-31
lines changed

ydb/apps/dstool/lib/common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,8 @@ def add_host_access_options(self, parser, with_endpoint=True):
219219
g.add_argument('--grpc-port', type=int, default=2135, metavar='PORT', help='GRPC port to use for procedure invocation')
220220
g.add_argument('--mon-port', type=int, default=8765, metavar='PORT', help='HTTP monitoring port for viewer JSON access')
221221
g.add_argument('--token-file', type=FileType(encoding='ascii'), metavar='PATH', help='Path to token file')
222-
g.add_argument('--ca-file', metavar='PATH', dest='cafile', type=str, help='Path to a file containing the PEM encoding of the server root certificates for tls connections.')
222+
g.add_argument('--ca-file', metavar='PATH', dest='cafile', type=str, help='File containing PEM encoded root certificates for SSL/TLS connections. '
223+
'If this parameter is empty, the default roots will be used.')
223224
g.add_argument('--http-timeout', type=int, default=5, help='Timeout for blocking socket I/O operations during HTTP(s) queries')
224225
g.add_argument('--insecure', action='store_true', help='Allow insecure HTTPS fetching')
225226
g.add_argument('--use-ip', action='store_true', help='Use IP addresses instead of hostnames when connecting to endpoints')

ydb/apps/ydb/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
* Add options for client certificates in SSL/TLS connections.
12
* Add `ydb admin node config init` command to initialize directory with node config files.
23
* Add `ydb admin cluster config generate` command to generate dynamic config from static config on cluster.
34
* Fixed memory leak in tpcds generator.

ydb/core/driver_lib/cli_base/cli_cmds_db.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,8 @@ class TClientCommandSchemaTableOptions : public TClientCommand {
883883
ClientConfig.MaxInFlight = CommandConfig.ClientConfig.MaxInFlight;
884884
ClientConfig.EnableSsl = CommandConfig.ClientConfig.EnableSsl;
885885
ClientConfig.SslCredentials.pem_root_certs = CommandConfig.ClientConfig.SslCredentials.pem_root_certs;
886+
ClientConfig.SslCredentials.pem_cert_chain = CommandConfig.ClientConfig.SslCredentials.pem_cert_chain;
887+
ClientConfig.SslCredentials.pem_private_key = CommandConfig.ClientConfig.SslCredentials.pem_private_key;
886888
}
887889

888890
template<typename T>

ydb/core/driver_lib/cli_base/cli_cmds_root.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ class TClientCommandRootLite : public TClientCommandRootKikimrBase {
186186
throw TMisuseException() << message;
187187
}
188188
ParseCaCerts(config);
189+
ParseClientCert(config);
189190
config.Address = Address;
190191

191192
if (!hostname) {
@@ -195,6 +196,10 @@ class TClientCommandRootLite : public TClientCommandRootKikimrBase {
195196
if (config.EnableSsl) {
196197
CommandConfig.ClientConfig.EnableSsl = config.EnableSsl;
197198
CommandConfig.ClientConfig.SslCredentials.pem_root_certs = config.CaCerts;
199+
if (config.ClientCert) {
200+
CommandConfig.ClientConfig.SslCredentials.pem_cert_chain = config.ClientCert;
201+
CommandConfig.ClientConfig.SslCredentials.pem_private_key = config.ClientCertPrivateKey;
202+
}
198203
}
199204
}
200205

ydb/core/driver_lib/cli_base/cli_grpc.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class TClientGRpcCommand : public TClientCommand {
9494
ClientConfig.MaxInFlight = CommandConfig.ClientConfig.MaxInFlight;
9595
ClientConfig.EnableSsl = CommandConfig.ClientConfig.EnableSsl;
9696
ClientConfig.SslCredentials.pem_root_certs = CommandConfig.ClientConfig.SslCredentials.pem_root_certs;
97+
ClientConfig.SslCredentials.pem_cert_chain = CommandConfig.ClientConfig.SslCredentials.pem_cert_chain;
98+
ClientConfig.SslCredentials.pem_private_key = CommandConfig.ClientConfig.SslCredentials.pem_private_key;
9799
}
98100

99101
static int PrepareConfigCredentials(NGRpcProxy::TGRpcClientConfig clientConfig, TConfig& commandConfig) {
@@ -159,4 +161,3 @@ class TClientGRpcCommand : public TClientCommand {
159161

160162
}
161163
}
162-

ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,16 @@ class TClientCommandRoot : public TClientCommandRootKikimrBase {
5656
config.EnableSsl = endpoint.EnableSsl.GetRef();
5757
}
5858
ParseCaCerts(config);
59+
ParseClientCert(config);
5960

6061
CommandConfig.ClientConfig = NYdbGrpc::TGRpcClientConfig(endpoint.Address);
6162
if (config.EnableSsl) {
6263
CommandConfig.ClientConfig.EnableSsl = config.EnableSsl;
6364
CommandConfig.ClientConfig.SslCredentials.pem_root_certs = config.CaCerts;
65+
if (config.ClientCert) {
66+
CommandConfig.ClientConfig.SslCredentials.pem_cert_chain = config.ClientCert;
67+
CommandConfig.ClientConfig.SslCredentials.pem_private_key = config.ClientCertPrivateKey;
68+
}
6469
}
6570
}
6671
};

ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ class TTenantClientGRpcCommand : public TTenantClientCommand {
142142
ClientConfig.MaxInFlight = CommandConfig.ClientConfig.MaxInFlight;
143143
ClientConfig.EnableSsl = CommandConfig.ClientConfig.EnableSsl;
144144
ClientConfig.SslCredentials.pem_root_certs = CommandConfig.ClientConfig.SslCredentials.pem_root_certs;
145+
ClientConfig.SslCredentials.pem_cert_chain = CommandConfig.ClientConfig.SslCredentials.pem_cert_chain;
146+
ClientConfig.SslCredentials.pem_private_key = CommandConfig.ClientConfig.SslCredentials.pem_private_key;
145147
}
146148

147149
int Run(TConfig &config) override

ydb/core/driver_lib/run/main.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,11 @@ int MainRun(const TKikimrRunConfig& runConfig, std::shared_ptr<TModuleFactories>
8989
configParser.SetupGlobalOpts(opts);
9090
NMsgBusProxy::TMsgBusClientConfig mbusConfig;
9191
mbusConfig.ConfigureLastGetopt(opts, "mb-");
92-
opts.AddLongOption("ca-file", "Path to a file containing the PEM encoding of the server root certificates for tls connections.\n").RequiredArgument("PATH");
92+
opts.AddLongOption("ca-file", "File containing PEM encoded root certificates for SSL/TLS connections. If this parameter is empty, the default roots will be used.\n").RequiredArgument("PATH");
9393
NDriverClient::HideOptions(opts);
94+
opts.AddLongOption("client-cert-file", "File containing client certificate for SSL/TLS connections (PKCS#12 or PEM-encoded)").RequiredArgument("PATH");
95+
opts.AddLongOption("client-cert-key-file", "File containing PEM encoded client certificate private key for SSL/TLS connections").RequiredArgument("PATH");
96+
opts.AddLongOption("client-cert-key-password-file", "File containing password for client certificate private key (if key is encrypted). If key file is encrypted, but this option is not set, password will be asked interactively").RequiredArgument("PATH");
9497
opts.AddLongOption('s', "server", "Server address to connect (default $KIKIMR_SERVER)").RequiredArgument("ADDR[:NUM]");
9598
opts.AddLongOption('k', "token", "Security token").RequiredArgument("TOKEN");
9699
opts.AddLongOption('f', "token-file", "Security token file").RequiredArgument("PATH");
@@ -203,4 +206,3 @@ int ParameterizedMain(int argc, char **argv, std::shared_ptr<NKikimr::TModuleFac
203206
return 1;
204207
}
205208
}
206-

ydb/public/lib/ydb_cli/commands/ydb_command.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ TDriverConfig TYdbCommand::CreateDriverConfig(TConfig& config) {
2929
driverConfig.UseSecureConnection(config.CaCerts);
3030
if (config.IsNetworkIntensive)
3131
driverConfig.SetNetworkThreadsNum(16);
32+
driverConfig.UseClientCertificate(config.ClientCert, config.ClientCertPrivateKey);
3233

3334
return driverConfig;
3435
}

ydb/public/lib/ydb_cli/commands/ydb_profile.cpp

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,15 @@ namespace {
244244
if (profile->Has("ca-file")) {
245245
Cout << " ca-file: " << profile->GetValue("ca-file").as<TString>() << Endl;
246246
}
247+
if (profile->Has("client-cert-file")) {
248+
Cout << " client-cert-file: " << profile->GetValue("client-cert-file").as<TString>() << Endl;
249+
}
250+
if (profile->Has("client-cert-key-file")) {
251+
Cout << " client-cert-key-file: " << profile->GetValue("client-cert-key-file").as<TString>() << Endl;
252+
}
253+
if (profile->Has("client-cert-key-password-file")) {
254+
Cout << " client-cert-key-password-file: " << profile->GetValue("client-cert-key-password-file").as<TString>() << Endl;
255+
}
247256
}
248257
}
249258

@@ -307,6 +316,15 @@ void TCommandConnectionInfo::PrintInfo(TConfig& config) {
307316
if (config.CaCertsFile) {
308317
Cout << "ca-file: " << config.CaCertsFile << Endl;
309318
}
319+
if (config.ClientCertFile) {
320+
Cout << "client-cert-file: " << config.ClientCertFile << Endl;
321+
}
322+
if (config.ClientCertPrivateKeyFile) {
323+
Cout << "client-cert-key-file: " << config.ClientCertPrivateKeyFile << Endl;
324+
}
325+
if (config.ClientCertPrivateKeyPasswordFile) {
326+
Cout << "client-cert-key-password-file: " << config.ClientCertPrivateKeyPasswordFile << Endl;
327+
}
310328
}
311329

312330
void TCommandConnectionInfo::PrintVerboseInfo(TConfig& config) {
@@ -382,7 +400,10 @@ void TCommandProfileCommon::GetOptionsFromStdin() {
382400
{"user", User},
383401
{"password-file", PasswordFile},
384402
{"iam-endpoint", IamEndpoint},
385-
{"ca-file", CaCertsFile}
403+
{"ca-file", CaCertsFile},
404+
{"client-cert-file", ClientCertFile},
405+
{"client-cert-key-file", ClientCertPrivateKeyFile},
406+
{"client-cert-key-password-file", ClientCertPrivateKeyPasswordFile},
386407
};
387408
while (Cin.ReadLine(line)) {
388409
Strip(line, trimmedLine);
@@ -432,6 +453,15 @@ void TCommandProfileCommon::ConfigureProfile(const TString& profileName, std::sh
432453
if (cmdLine && CaCertsFile) {
433454
profile->SetValue("ca-file", CaCertsFile);
434455
}
456+
if (cmdLine && ClientCertFile) {
457+
profile->SetValue("client-cert-file", ClientCertFile);
458+
}
459+
if (cmdLine && ClientCertPrivateKeyFile) {
460+
profile->SetValue("client-cert-key-file", ClientCertPrivateKeyFile);
461+
}
462+
if (cmdLine && ClientCertPrivateKeyPasswordFile) {
463+
profile->SetValue("client-cert-key-password-file", ClientCertPrivateKeyPasswordFile);
464+
}
435465

436466
if (interactive) {
437467
TString activeProfileName = profileManager->GetActiveProfileName();
@@ -673,7 +703,8 @@ bool TCommandProfileCommon::AnyProfileOptionInCommandLine() {
673703
return Endpoint || Database || TokenFile || Oauth2KeyFile ||
674704
IamTokenFile || YcTokenFile ||
675705
SaKeyFile || UseMetadataCredentials || User ||
676-
PasswordFile || IamEndpoint || AnonymousAuth || CaCertsFile;
706+
PasswordFile || IamEndpoint || AnonymousAuth || CaCertsFile ||
707+
ClientCertFile || ClientCertPrivateKeyFile || ClientCertPrivateKeyPasswordFile;
677708
}
678709

679710
TCommandCreateProfile::TCommandCreateProfile()
@@ -711,8 +742,17 @@ void TCommandProfileCommon::Config(TConfig& config) {
711742
.RequiredArgument("STR").StoreResult(&IamEndpoint);
712743
}
713744
opts.AddLongOption("ca-file",
714-
"Path to a file containing the PEM encoding of the server root certificates for tls connections.")
745+
"File containing PEM encoded root certificates for SSL/TLS connections.")
715746
.RequiredArgument("PATH").StoreResult(&CaCertsFile);
747+
opts.AddLongOption("client-cert-file",
748+
"File containing client certificate for SSL/TLS connections (PKCS#12 or PEM-encoded).")
749+
.RequiredArgument("PATH").StoreResult(&ClientCertFile);
750+
opts.AddLongOption("client-cert-key-file",
751+
"File containing PEM encoded client certificate private key for SSL/TLS connections.")
752+
.RequiredArgument("PATH").StoreResult(&ClientCertPrivateKeyFile);
753+
opts.AddLongOption("client-cert-key-password-file",
754+
"File containing password for client certificate private key (if key is encrypted). If key file is encrypted, but this option is not set, password will be asked interactively.")
755+
.RequiredArgument("PATH").StoreResult(&ClientCertPrivateKeyPasswordFile);
716756
if (!IsStdinInteractive()) {
717757
GetOptionsFromStdin();
718758
}
@@ -1064,8 +1104,14 @@ void TCommandUpdateProfile::Config(TConfig& config) {
10641104
if (config.UseIamAuth) {
10651105
opts.AddLongOption("no-iam-endpoint", "Delete endpoint of IAM service from the profile").StoreTrue(&NoIamEndpoint);
10661106
}
1067-
opts.AddLongOption("no-ca-file", "Delete path to file containing the PEM encoding of the "
1068-
"server root certificates for tls connections from the profile").StoreTrue(&NoCaCertsFile);
1107+
opts.AddLongOption("no-ca-file", "Delete path to file containing the PEM encoded "
1108+
"root certificates for SSL/TLS connections from the profile").StoreTrue(&NoCaCertsFile);
1109+
opts.AddLongOption("no-client-cert-file", "Delete path to file containing client certificate "
1110+
"for SSL/TLS connections").StoreTrue(&NoClientCertFile);
1111+
opts.AddLongOption("no-client-cert-key-file", "Delete path to file containing PEM encoded client "
1112+
"certificate private key for SSL/TLS connections").StoreTrue(&NoClientCertPrivateKeyFile);
1113+
opts.AddLongOption("no-client-cert-key-password-file", "Delete path to file containing password for "
1114+
"client certificate private key (if key is encrypted)").StoreTrue(&NoClientCertPrivateKeyPasswordFile);
10691115
}
10701116

10711117
void TCommandUpdateProfile::ValidateNoOptions() {
@@ -1080,21 +1126,21 @@ void TCommandUpdateProfile::ValidateNoOptions() {
10801126
throw TMisuseException() << "You cannot enter authentication options and the \"--no-auth\" option at the same time";
10811127
}
10821128
TStringBuilder str;
1083-
if (Endpoint && NoEndpoint) {
1084-
str << "\"--endpoint\" and \"--no-endpoint\"";
1085-
} else {
1086-
if (Database && NoDatabase) {
1087-
str << "\"--database and \"--no-database\"";
1088-
} else {
1089-
if (IamEndpoint && NoIamEndpoint) {
1090-
str << "\"--iam-endpoint\" and \"--no-iam-endpoint\"";
1091-
} else {
1092-
if (CaCertsFile && NoCaCertsFile) {
1093-
str << "\"--ca-file\" and \"--no-ca-file\"";
1094-
}
1129+
auto addMutuallyExclusiveOptionError = [&](bool validationResult, TStringBuf optionName) {
1130+
if (validationResult) {
1131+
if (str) {
1132+
str << ", ";
10951133
}
1134+
str << "\"--" << optionName << "\" and \"--no-" << optionName << "\"";
10961135
}
1097-
}
1136+
};
1137+
addMutuallyExclusiveOptionError(Endpoint && NoEndpoint, "endpoint");
1138+
addMutuallyExclusiveOptionError(Database && NoDatabase, "database");
1139+
addMutuallyExclusiveOptionError(IamEndpoint && NoIamEndpoint, "iam-endpoint");
1140+
addMutuallyExclusiveOptionError(CaCertsFile && NoCaCertsFile, "ca-file");
1141+
addMutuallyExclusiveOptionError(ClientCertFile && NoClientCertFile, "client-cert-file");
1142+
addMutuallyExclusiveOptionError(ClientCertPrivateKeyFile && NoClientCertPrivateKeyFile, "client-cert-key-file");
1143+
addMutuallyExclusiveOptionError(NoClientCertPrivateKeyPasswordFile && NoClientCertPrivateKeyPasswordFile, "client-cert-key-password-file");
10981144
if (!str.empty()) {
10991145
throw TMisuseException() << "Options " << str << " are mutually exclusive";
11001146
}
@@ -1116,6 +1162,15 @@ void TCommandUpdateProfile::DropNoOptions(std::shared_ptr<IProfile> profile) {
11161162
if (NoCaCertsFile) {
11171163
profile->RemoveValue("ca-file");
11181164
}
1165+
if (NoClientCertFile) {
1166+
profile->RemoveValue("client-cert-file");
1167+
}
1168+
if (NoClientCertPrivateKeyFile) {
1169+
profile->RemoveValue("client-cert-key-file");
1170+
}
1171+
if (NoClientCertPrivateKeyPasswordFile) {
1172+
profile->RemoveValue("client-cert-key-password-file");
1173+
}
11191174
}
11201175

11211176
void TCommandUpdateProfile::Parse(TConfig& config) {

ydb/public/lib/ydb_cli/commands/ydb_profile.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class TCommandProfileCommon : public TClientCommand {
4343
TConfig& config, bool interactive, bool cmdLine);
4444

4545
TString ProfileName, Endpoint, Database, TokenFile, Oauth2KeyFile, YcTokenFile, SaKeyFile,
46-
IamTokenFile, IamEndpoint, User, PasswordFile, CaCertsFile;
46+
IamTokenFile, IamEndpoint, User, PasswordFile, CaCertsFile, ClientCertFile, ClientCertPrivateKeyFile, ClientCertPrivateKeyPasswordFile;
4747

4848
bool UseMetadataCredentials = false;
4949
bool AnonymousAuth = false;
@@ -141,6 +141,9 @@ class TCommandUpdateProfile : public TCommandProfileCommon {
141141
bool NoAuth = false;
142142
bool NoIamEndpoint = false;
143143
bool NoCaCertsFile = false;
144+
bool NoClientCertFile = false;
145+
bool NoClientCertPrivateKeyFile = false;
146+
bool NoClientCertPrivateKeyPasswordFile = false;
144147
};
145148

146149
class TCommandReplaceProfile : public TCommandProfileCommon {

0 commit comments

Comments
 (0)