Skip to content

Commit 323f348

Browse files
mregrockydbot
authored andcommitted
Add tests for V2 deploy with auth (#27729)
1 parent a12f81e commit 323f348

File tree

6 files changed

+169
-37
lines changed

6 files changed

+169
-37
lines changed

ydb/tests/functional/config/test_distconf.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import ydb.tests.library.common.cms as cms
1212
from ydb.tests.library.clients.kikimr_http_client import SwaggerClient
1313
from ydb.tests.library.harness.kikimr_runner import KiKiMR
14-
from ydb.tests.library.clients.kikimr_config_client import ConfigClient
1514
from ydb.tests.library.harness.kikimr_config import KikimrConfigGenerator
1615
from ydb.tests.library.kv.helpers import create_kv_tablets_and_wait_for_start
1716
from ydb.public.api.protos.ydb_status_codes_pb2 import StatusIds
@@ -46,6 +45,7 @@ class DistConfKiKiMRTest(object):
4645
use_config_store = True
4746
separate_node_configs = True
4847
nodes_count = 0
48+
protected_mode = False
4949
metadata_section = {
5050
"kind": "MainConfig",
5151
"version": 0,
@@ -73,17 +73,17 @@ def setup_class(cls):
7373
separate_node_configs=cls.separate_node_configs,
7474
simple_config=True,
7575
use_self_management=True,
76+
protected_mode=cls.protected_mode,
7677
extra_grpc_services=['config'],
7778
additional_log_configs=log_configs)
7879

7980
cls.cluster = KiKiMR(configurator=cls.configurator)
8081
cls.cluster.start()
8182

82-
cms.request_increase_ratio_limit(cls.cluster.client)
83+
if not cls.protected_mode:
84+
cms.request_increase_ratio_limit(cls.cluster.client)
8385
host = cls.cluster.nodes[1].host
84-
grpc_port = cls.cluster.nodes[1].port
8586
cls.swagger_client = SwaggerClient(host, cls.cluster.nodes[1].mon_port)
86-
cls.config_client = ConfigClient(host, grpc_port)
8787

8888
@classmethod
8989
def teardown_class(cls):
@@ -135,7 +135,7 @@ def test_cluster_expand_with_distconf(self):
135135

136136
node_port_allocator = self.configurator.port_allocator.get_node_port_allocator(expected_new_node_id)
137137

138-
fetched_config = fetch_config(self.config_client)
138+
fetched_config = fetch_config(self.cluster.config_client)
139139
dumped_fetched_config = yaml.safe_load(fetched_config)
140140
config_section = dumped_fetched_config["config"]
141141

@@ -170,7 +170,7 @@ def test_cluster_expand_with_distconf(self):
170170
dumped_fetched_config["metadata"]["version"] = 1
171171

172172
# replace config
173-
replace_config_response = self.config_client.replace_config(yaml.dump(dumped_fetched_config))
173+
replace_config_response = self.cluster.config_client.replace_config(yaml.dump(dumped_fetched_config))
174174
logger.debug(f"replace_config_response: {replace_config_response}")
175175
assert_that(replace_config_response.operation.status == StatusIds.SUCCESS)
176176
# start new node
@@ -219,7 +219,7 @@ def test_cluster_expand_with_seed_nodes(self):
219219

220220
node_port_allocator = self.configurator.port_allocator.get_node_port_allocator(expected_new_node_id)
221221

222-
fetched_config = fetch_config(self.config_client)
222+
fetched_config = fetch_config(self.cluster.config_client)
223223
dumped_fetched_config = yaml.safe_load(fetched_config)
224224
config_section = dumped_fetched_config["config"]
225225

@@ -264,7 +264,7 @@ def test_cluster_expand_with_seed_nodes(self):
264264
dumped_fetched_config["metadata"]["version"] = 1
265265

266266
# replace config
267-
replace_config_response = self.config_client.replace_config(yaml.dump(dumped_fetched_config))
267+
replace_config_response = self.cluster.config_client.replace_config(yaml.dump(dumped_fetched_config))
268268
logger.debug(f"replace_config_response: {replace_config_response}")
269269
assert_that(replace_config_response.operation.status == StatusIds.SUCCESS)
270270
# start new node
@@ -299,24 +299,24 @@ def test_cluster_expand_with_seed_nodes(self):
299299
os.unlink(seed_nodes_file.name)
300300

301301
def test_invalid_host_config_id(self):
302-
fetched_config = fetch_config(self.config_client)
302+
fetched_config = fetch_config(self.cluster.config_client)
303303
dumped_fetched_config = yaml.safe_load(fetched_config)
304304

305305
# replace config with invalid host config id
306306
dumped_fetched_config['metadata']['version'] = 1
307307
dumped_fetched_config["config"]["host_configs"][0]["host_config_id"] = 1000
308-
replace_config_response = self.config_client.replace_config(yaml.dump(dumped_fetched_config))
308+
replace_config_response = self.cluster.config_client.replace_config(yaml.dump(dumped_fetched_config))
309309
logger.debug(f"replace_config_response: {replace_config_response}")
310310
assert_that(replace_config_response.operation.status == StatusIds.INTERNAL_ERROR)
311311

312312
def test_invalid_change_host_config_disk(self):
313-
fetched_config = fetch_config(self.config_client)
313+
fetched_config = fetch_config(self.cluster.config_client)
314314
dumped_fetched_config = yaml.safe_load(fetched_config)
315315

316316
# replace config with invalid host config disk path
317317
dumped_fetched_config["config"]["host_configs"][0]["drive"].append(dumped_fetched_config["config"]["host_configs"][0]["drive"][0])
318318
dumped_fetched_config['metadata']['version'] = 1
319-
replace_config_response = self.config_client.replace_config(yaml.dump(dumped_fetched_config))
319+
replace_config_response = self.cluster.config_client.replace_config(yaml.dump(dumped_fetched_config))
320320
logger.debug(f"replace_config_response: {replace_config_response}")
321321
assert_that(replace_config_response.operation.status == StatusIds.INTERNAL_ERROR)
322322

@@ -348,3 +348,18 @@ def test_bootstrap_selector_validation(self):
348348
with pytest.raises(Exception) as ei:
349349
cluster.start()
350350
assert 'YAML validation failed' in str(ei.value)
351+
352+
353+
class TestDistConfWithAuth(DistConfKiKiMRTest):
354+
protected_mode = True
355+
356+
def test_auth_v2_initialization(self):
357+
table_path = '/Root/mydb/mytable_with_auth'
358+
self.cluster.create_database(
359+
table_path,
360+
storage_pool_units_count={
361+
'rot': 1
362+
},
363+
timeout_seconds=60,
364+
token=self.cluster.root_token
365+
)

ydb/tests/library/clients/kikimr_config_client.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,30 @@
1414
logger = logging.getLogger()
1515

1616

17-
def config_client_factory(server, port, cluster=None, retry_count=1):
17+
def config_client_factory(server, port, cluster=None, retry_count=1, ca_path=None, cert_path=None, key_path=None):
1818
return ConfigClient(
1919
server, port, cluster=cluster,
20-
retry_count=retry_count
20+
retry_count=retry_count,
21+
ca_path=ca_path, cert_path=cert_path, key_path=key_path,
2122
)
2223

2324

2425
def channels_list():
2526
return os.getenv('CHANNELS_LIST', '')
2627

2728

29+
def read_file(path):
30+
with open(path, 'rb') as f:
31+
return f.read()
32+
33+
2834
class ConfigClient(object):
2935
class FetchTransform(Enum):
3036
NONE = 1
3137
DETACH_STORAGE_CONFIG_SECTION = 2
3238
ATTACH_STORAGE_CONFIG_SECTION = 3
3339

34-
def __init__(self, server, port, cluster=None, retry_count=1):
40+
def __init__(self, server, port, cluster=None, retry_count=1, ca_path=None, cert_path=None, key_path=None):
3541
self.server = server
3642
self.port = port
3743
self._cluster = cluster
@@ -42,7 +48,17 @@ def __init__(self, server, port, cluster=None, retry_count=1):
4248
('grpc.max_receive_message_length', 64 * 10 ** 6),
4349
('grpc.max_send_message_length', 64 * 10 ** 6)
4450
]
45-
self._channel = grpc.insecure_channel("%s:%s" % (self.server, self.port), options=self._options)
51+
52+
if cert_path and key_path:
53+
credentials = grpc.ssl_channel_credentials(
54+
root_certificates=read_file(ca_path) if ca_path else None,
55+
private_key=read_file(key_path),
56+
certificate_chain=read_file(cert_path)
57+
)
58+
self._channel = grpc.secure_channel("%s:%s" % (self.server, self.port), credentials, options=self._options)
59+
else:
60+
self._channel = grpc.insecure_channel("%s:%s" % (self.server, self.port), options=self._options)
61+
4662
self._stub = grpc_server.ConfigServiceStub(self._channel)
4763
self._auth_token = None
4864

ydb/tests/library/harness/kikimr_cluster_interface.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from ydb.tests.library.clients.kikimr_client import kikimr_client_factory
99
from ydb.tests.library.clients.kikimr_keyvalue_client import keyvalue_client_factory
1010
from ydb.tests.library.clients.kikimr_scheme_client import scheme_client_factory
11-
from ydb.tests.library.clients.kikimr_config_client import config_client_factory
1211
from ydb.tests.library.common.protobuf_console import (
1312
CreateTenantRequest, AlterTenantRequest, GetTenantStatusRequest,
1413
RemoveTenantRequest, GetOperationRequest)
@@ -115,14 +114,16 @@ def scheme_client(self):
115114
@property
116115
def config_client(self):
117116
if self.__config_client is None:
118-
self.__config_client = config_client_factory(
119-
server=self.nodes[1].host,
120-
port=self.nodes[1].grpc_port,
121-
cluster=self,
122-
retry_count=10,
123-
)
117+
self.__config_client = self._create_config_client()
124118
return self.__config_client
125119

120+
@abc.abstractmethod
121+
def _create_config_client(self):
122+
"""
123+
Factory method for ConfigClient, must be implemented by subclasses.
124+
"""
125+
pass
126+
126127
def _send_get_tenant_status_request(self, database_name, token=None):
127128
req = GetTenantStatusRequest(database_name)
128129

ydb/tests/library/harness/kikimr_config.py

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ def __init__(
186186
verbose_memory_limit_exception=False,
187187
enable_static_auth=False,
188188
cms_config=None,
189-
explicit_statestorage_config=None
189+
explicit_statestorage_config=None,
190+
protected_mode=False
190191
):
191192
if extra_feature_flags is None:
192193
extra_feature_flags = []
@@ -213,7 +214,8 @@ def __init__(
213214
self.app_config = config_pb2.TAppConfig()
214215
self.port_allocator = KikimrPortManagerPortAllocator() if port_allocator is None else port_allocator
215216
erasure = Erasure.NONE if erasure is None else erasure
216-
self.__grpc_ssl_enable = grpc_ssl_enable
217+
self.protected_mode = protected_mode
218+
self.__grpc_ssl_enable = grpc_ssl_enable or protected_mode
217219
self.__grpc_tls_data_path = None
218220
self.__grpc_tls_ca = None
219221
self.__grpc_tls_key = None
@@ -453,12 +455,52 @@ def __init__(
453455
if os.getenv("YDB_ALLOW_ORIGIN") is not None:
454456
self.yaml_config["monitoring_config"] = {"allow_origin": str(os.getenv("YDB_ALLOW_ORIGIN"))}
455457

456-
if enforce_user_token_requirement:
458+
if enforce_user_token_requirement or protected_mode:
457459
security_config_root["security_config"]["enforce_user_token_requirement"] = True
458460

459461
if default_user_sid:
460462
security_config_root["security_config"]["default_user_sids"] = [default_user_sid]
461463

464+
# protected mode is described in the YDB documentation for cluster deployment: it uses both certificate and token authentication.
465+
# see https://ydb.tech/docs/en/devops/deployment-options/manual/initial-deployment?version=main
466+
if protected_mode:
467+
security_config = security_config_root.setdefault("security_config", {})
468+
if "default_users" in security_config:
469+
del security_config["default_users"]
470+
471+
base_sids = ["root", "root@builtin", "ADMINS", "DATABASE-ADMINS", "clusteradmins@cert"]
472+
security_config["monitoring_allowed_sids"] = base_sids
473+
security_config["viewer_allowed_sids"] = base_sids
474+
security_config["bootstrap_allowed_sids"] = base_sids
475+
security_config["administration_allowed_sids"] = base_sids
476+
477+
self.yaml_config["interconnect_config"] = {
478+
"start_tcp": True,
479+
"encryption_mode": "OPTIONAL",
480+
"path_to_certificate_file": self.grpc_tls_cert_path,
481+
"path_to_private_key_file": self.grpc_tls_key_path,
482+
"path_to_ca_file": self.grpc_tls_ca_path,
483+
}
484+
485+
self.yaml_config['grpc_config']['services_enabled'] = ['legacy']
486+
if 'services' in self.yaml_config['grpc_config']:
487+
del self.yaml_config['grpc_config']['services']
488+
489+
self.yaml_config['client_certificate_authorization'] = {
490+
"request_client_certificate": True,
491+
"client_certificate_definitions": [
492+
{
493+
"member_groups": ["clusteradmins@cert"],
494+
"subject_terms": [
495+
{
496+
"short_name": "O",
497+
"values": ["YDB"]
498+
}
499+
]
500+
}
501+
]
502+
}
503+
462504
if memory_controller_config:
463505
self.yaml_config["memory_controller_config"] = memory_controller_config
464506

@@ -528,14 +570,19 @@ def __init__(
528570
self._add_host_config_and_hosts()
529571
self.yaml_config.pop("nameservice_config")
530572
if self.use_self_management:
573+
531574
if "security_config" in self.yaml_config["domains_config"]:
532-
self.yaml_config["domains_config"].pop("security_config")
575+
self.yaml_config["security_config"] = self.yaml_config["domains_config"].pop("security_config")
576+
577+
if "services" in self.yaml_config["grpc_config"] and "config" not in self.yaml_config["grpc_config"]["services"]:
578+
self.yaml_config["grpc_config"]["services"].append("config")
579+
533580
self.yaml_config["default_disk_type"] = "ROT"
534581
self.yaml_config["fail_domain_type"] = "rack"
535582
self.yaml_config["erasure"] = self.yaml_config.pop("static_erasure")
536583

537-
for name in ['blob_storage_config', 'domains_config', 'system_tablets', 'grpc_config',
538-
'channel_profile_config', 'interconnect_config']:
584+
for name in ['blob_storage_config', 'domains_config', 'system_tablets',
585+
'channel_profile_config']:
539586
del self.yaml_config[name]
540587
if self.simple_config:
541588
self.yaml_config.pop("feature_flags")

0 commit comments

Comments
 (0)