Skip to content

Commit 9724f9e

Browse files
committed
move certs to universal builder
1 parent 5f9d703 commit 9724f9e

18 files changed

Lines changed: 775 additions & 35 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: "certificates-example"
2+
uuid: "b8a4de1c-2f56-4bc1-a3b8-4fe40124cccc"
3+
description: "Exordos certificates example"
4+
schema_version: 1
5+
version: "0.0.1"
6+
api_version: "v1"
7+
8+
requirements:
9+
core:
10+
from_version: "0.0.0"
11+
12+
resources:
13+
$core.secret.certificates:
14+
# DNS Core certificate (ACME via Let's Encrypt with DNS-01 challenge)
15+
# Uses DNS CoreDNS plugin to automatically fulfill ACME challenges.
16+
# After provisioning, cert and key are stored on the agent.
17+
wildcard_cert:
18+
name: "wildcard-cert"
19+
project_id: "12345678-c625-4fee-81d5-f691897b8142"
20+
email: "admin@example.com"
21+
method:
22+
kind: dns_core
23+
domains:
24+
- "*.example.com"
25+
- "example.com"
26+
expiration_threshold: 30
27+
overcome_threshold: false
28+
29+
# Multi-domain SAN certificate
30+
# Subject Alternative Names for multiple service domains.
31+
san_cert:
32+
name: "san-cert"
33+
project_id: "12345678-c625-4fee-81d5-f691897b8142"
34+
email: "devops@example.com"
35+
method:
36+
kind: dns_core
37+
domains:
38+
- "api.example.com"
39+
- "auth.example.com"
40+
- "admin.example.com"
41+
expiration_threshold: 14
42+
43+
# Internal service certificate with no wildcards
44+
internal_cert:
45+
name: "internal-cert"
46+
project_id: "12345678-c625-4fee-81d5-f691897b8142"
47+
email: "it@example.com"
48+
method:
49+
kind: dns_core
50+
domains:
51+
- "gitlab.internal.example.com"
52+
- "jenkins.internal.example.com"
53+
expiration_threshold: 7
54+
overcome_threshold: true

exordos_core/agent/universal/drivers/secret/backend/cert.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,11 @@
3636
class CertBotBackendClient(base.AbstractBackendClient):
3737
"""Cert bot backend client."""
3838

39-
DEFAULT_PRIVATE_KEY_PATH = "/etc/exordos_core/certbot/privkey.pem"
40-
4139
def __init__(
4240
self,
4341
dns_client: dns_clients.TinyDNSCoreClient,
4442
admin_email: str,
45-
private_key_path: str = DEFAULT_PRIVATE_KEY_PATH,
43+
private_key_path: str,
4644
) -> None:
4745
self._dns_client = dns_client
4846
self._admin_email = admin_email
@@ -89,7 +87,7 @@ def create(self, resource: models.Resource) -> tp.Dict[str, tp.Any]:
8987
cert_x509 = x509.load_pem_x509_certificate(fullchain_pem.encode())
9088
expiration_at = cert_x509.not_valid_after_utc
9189

92-
# Build storagable password and save
90+
# Build storage_cert and save
9391
driver_cert = driver_dm.Certificate.from_cert_resource(
9492
resource, pkey_pem, csr_pem, fullchain_pem, expiration_at
9593
)

exordos_core/agent/universal/drivers/secret/backend/db.py renamed to exordos_core/agent/universal/drivers/secret/backend/password.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
LOG = logging.getLogger(__name__)
3232

3333

34-
class DatabaseSecretBackendClient(base.AbstractBackendClient):
34+
class DatabasePasswordBackendClient(base.AbstractBackendClient):
3535
"""Secret Backend client based on SQL database."""
3636

3737
def get(self, resource: models.Resource) -> tp.Dict[str, tp.Any]:

exordos_core/agent/universal/drivers/secret/cert.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
CERT_TARGET_FIELDS_STORAGE = "/var/lib/exordos/exordos_core/cert_target_fields.json"
3030
DEFAULT_ADMIN_EMAIL = "infra@exordos.com"
31+
DEFAULT_PRIVATE_KEY_PATH = "/etc/exordos_core/certbot/privkey.pem"
3132

3233

3334
class CoreDNSCertificateCapabilityDriver(direct.DirectAgentDriver):
@@ -38,9 +39,11 @@ def __init__(
3839
user_api_base_url: str,
3940
username: str,
4041
password: str,
42+
private_key_path: str = DEFAULT_PRIVATE_KEY_PATH,
4143
admin_email: str = DEFAULT_ADMIN_EMAIL,
44+
storage_path: str = CERT_TARGET_FIELDS_STORAGE,
4245
) -> None:
43-
storage = fs.TargetFieldsFileStorage(CERT_TARGET_FIELDS_STORAGE)
46+
storage = fs.TargetFieldsFileStorage(storage_path)
4447

4548
auth = core_client_base.CoreIamAuthenticator(
4649
base_url=user_api_base_url, username=username, password=password
@@ -49,7 +52,9 @@ def __init__(
4952
base_url=user_api_base_url, auth=auth
5053
)
5154

52-
client = cert_back.CertBotBackendClient(dns_client, admin_email)
55+
client = cert_back.CertBotBackendClient(
56+
dns_client, admin_email, private_key_path
57+
)
5358

5459
super().__init__(storage=storage, client=client)
5560

exordos_core/agent/universal/drivers/secret/password.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from gcl_sdk.agents.universal.drivers import direct
2121
from gcl_sdk.agents.universal.storage import fs
2222

23-
from exordos_core.agent.universal.drivers.secret.backend import db as db_back
23+
from exordos_core.agent.universal.drivers.secret.backend import password as db_back
2424

2525
LOG = logging.getLogger(__name__)
2626

@@ -34,7 +34,7 @@ class PasswordCapabilityDriver(direct.DirectAgentDriver):
3434

3535
def __init__(self, storage_path: str = PASSWORD_TARGET_FIELDS_STORAGE):
3636
storage = fs.TargetFieldsFileStorage(storage_path)
37-
client = db_back.DatabaseSecretBackendClient()
37+
client = db_back.DatabasePasswordBackendClient()
3838

3939
super().__init__(storage=storage, client=client)
4040

exordos_core/common/constants.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,13 @@
4141
NODE_UUID_PATH = os.path.join(WORK_DIR, "node-id")
4242

4343
DEFAULT_USER_API_HOST = "127.0.0.1"
44-
DEFAULT_USER_API_PORT = 11010
45-
DEFAULT_ROOT_ENDPOINT = f"http://{DEFAULT_USER_API_HOST}:{DEFAULT_USER_API_PORT}/v1/"
44+
DEFAULT_USER_API_PORT = 80
45+
DEFAULT_ROOT_ENDPOINT = (
46+
f"http://{DEFAULT_USER_API_HOST}:{DEFAULT_USER_API_PORT}/api/core/v1/"
47+
)
48+
49+
DEFAULT_DNS_CERT_USERNAME = "admin"
50+
DEFAULT_DNS_CERT_PASSWORD = "admin"
4651

4752
DEFAULT_GLOBAL_SALT = "FOy/2kwwdn0ig1QOq7cestqe"
4853
DEFAULT_ADMIN_SALT = "d4JJ9QYuEEJxHCFja9FZskG4"

exordos_core/gservice/service.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
from exordos_core.network.lb.builders import paas as net_lb_paas
5353
from exordos_core.network.lb.dm import models as lb_models
5454
from exordos_core.secret import service as secret_service
55-
from exordos_core.secret.builders import service as password_builder_svc
55+
from exordos_core.secret.builders import service as secret_builder_svc
5656
from exordos_core.telemetry import service as telemetry_service
5757
from exordos_core.vs.builders import service as vs_builder_svc
5858

@@ -187,7 +187,10 @@ def __init__(self, iter_min_period=3, iter_pause=0.1):
187187
secret_svc = secret_service.SecretServiceBuilder(
188188
iter_min_period=iter_min_period,
189189
)
190-
password_builder = password_builder_svc.PasswordBuilder(
190+
password_builder = secret_builder_svc.PasswordBuilder(
191+
iter_min_period=iter_min_period
192+
)
193+
cert_builder = secret_builder_svc.CertificateBuilder(
191194
iter_min_period=iter_min_period
192195
)
193196
# Build the event sender only when event delivery is enabled (the
@@ -230,6 +233,7 @@ def __init__(self, iter_min_period=3, iter_pause=0.1):
230233
net_lb_paas_builder,
231234
secret_svc,
232235
password_builder,
236+
cert_builder,
233237
em_builder,
234238
dns_sync,
235239
# non-essential services should be last

exordos_core/secret/builders/service.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,28 @@ def actualize_outdated_instance(
9696

9797
if status_updated or password_updated:
9898
current_instance.save()
99+
100+
101+
class CertificateBuilder(sdk_builder.UniversalBuilderService):
102+
def __init__(
103+
self,
104+
iter_min_period: int = 1,
105+
iter_pause: float = 0.1,
106+
) -> None:
107+
super().__init__(
108+
instance_model=Certificate,
109+
iter_min_period=iter_min_period,
110+
iter_pause=iter_pause,
111+
)
112+
113+
def actualize_outdated_instance(
114+
self,
115+
current_instance: Certificate,
116+
actual_instance: Certificate,
117+
) -> None:
118+
status_updated = current_instance.status != actual_instance.status
119+
if status_updated:
120+
current_instance.status = actual_instance.status
121+
122+
if status_updated:
123+
current_instance.save()

exordos_core/secret/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,5 +547,5 @@ def _actualize_ssh_keys(self) -> None:
547547

548548
def _iteration(self) -> None:
549549
with contexts.Context().session_manager():
550-
self._actualize_certificates()
550+
# self._actualize_certificates()
551551
self._actualize_ssh_keys()

exordos_core/tests/functional/conftest.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from restalchemy.dm import filters as dm_filters
3838
from restalchemy.storage.sql import engines
3939

40+
from exordos_core.agent.universal.drivers.secret import cert as cert_driver
4041
from exordos_core.agent.universal.drivers.secret import password as password_driver
4142
from exordos_core.common import constants as c
4243
from exordos_core.common import utils
@@ -1429,19 +1430,18 @@ def factory(hostname, ip_addresses=None, key=None):
14291430

14301431

14311432
@pytest.fixture()
1432-
def agent_service(
1433+
def password_agent_service(
14331434
default_node: tp.Dict[str, tp.Any],
14341435
user_api_client: iam_clients.GenesisCoreTestRESTClient,
14351436
):
14361437
agent_uuid = sys_uuid.UUID(default_node["uuid"])
14371438
orch_client = orch_db.DatabaseOrchClient()
14381439
with tempfile.TemporaryDirectory() as temp_dir:
1439-
temp_file_path = os.path.join(temp_dir, "password_target_fields.json")
1440-
1441-
with open(temp_file_path, "w", encoding="utf-8") as f:
1440+
passwords_temp_file_path = os.path.join(temp_dir, "password_target_fields.json")
1441+
with open(passwords_temp_file_path, "w", encoding="utf-8") as f:
14421442
json.dump({}, f, indent=4)
14431443

1444-
p_d = password_driver.PasswordCapabilityDriver(temp_file_path)
1444+
p_d = password_driver.PasswordCapabilityDriver(passwords_temp_file_path)
14451445
caps_drivers = [
14461446
p_d,
14471447
]
@@ -1460,11 +1460,53 @@ def agent_service(
14601460
yield agent
14611461

14621462

1463+
@pytest.fixture()
1464+
def cert_agent_service(
1465+
default_node: tp.Dict[str, tp.Any],
1466+
user_api_client: iam_clients.GenesisCoreTestRESTClient,
1467+
):
1468+
agent_uuid = sys_uuid.UUID(default_node["uuid"])
1469+
orch_client = orch_db.DatabaseOrchClient()
1470+
with tempfile.TemporaryDirectory() as temp_dir:
1471+
cert_temp_file_path = os.path.join(temp_dir, "cert_target_fields.json")
1472+
with open(cert_temp_file_path, "w", encoding="utf-8") as f:
1473+
json.dump({}, f, indent=4)
1474+
privkey_temp_file_path = os.path.join(temp_dir, "privkey.pem")
1475+
c_d = cert_driver.CoreDNSCertificateCapabilityDriver(
1476+
user_api_base_url=c.DEFAULT_ROOT_ENDPOINT,
1477+
username=c.DEFAULT_DNS_CERT_USERNAME,
1478+
password=c.DEFAULT_DNS_CERT_PASSWORD,
1479+
storage_path=cert_temp_file_path,
1480+
private_key_path=privkey_temp_file_path,
1481+
)
1482+
caps_drivers = [
1483+
c_d,
1484+
]
1485+
agent = ua_agent_service.UniversalAgentService(
1486+
system_uuid=agent_uuid,
1487+
agent_uuid=agent_uuid,
1488+
orch_client=orch_client,
1489+
caps_drivers=caps_drivers,
1490+
facts_drivers=[],
1491+
iter_min_period=3,
1492+
payload_path=None,
1493+
verify_node_on_register=False,
1494+
)
1495+
agent._setup()
1496+
agent._register_agent()
1497+
yield agent
1498+
1499+
14631500
@pytest.fixture()
14641501
def password_builder():
14651502
yield secret_service.PasswordBuilder()
14661503

14671504

1505+
@pytest.fixture()
1506+
def certificate_builder():
1507+
yield secret_service.CertificateBuilder()
1508+
1509+
14681510
@pytest.fixture()
14691511
def universal_scheduler():
14701512
yield ua_scheduler_service.UniversalAgentSchedulerService(capabilities=["*"])

0 commit comments

Comments
 (0)