olas-operate-middleware 0.1.0rc59__py3-none-any.whl → 0.13.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- olas_operate_middleware-0.13.2.dist-info/METADATA +75 -0
- olas_operate_middleware-0.13.2.dist-info/RECORD +101 -0
- {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info}/WHEEL +1 -1
- operate/__init__.py +17 -0
- operate/account/user.py +35 -9
- operate/bridge/bridge_manager.py +470 -0
- operate/bridge/providers/lifi_provider.py +377 -0
- operate/bridge/providers/native_bridge_provider.py +677 -0
- operate/bridge/providers/provider.py +469 -0
- operate/bridge/providers/relay_provider.py +457 -0
- operate/cli.py +1565 -417
- operate/constants.py +60 -12
- operate/data/README.md +19 -0
- operate/data/contracts/{service_staking_token → dual_staking_token}/__init__.py +2 -2
- operate/data/contracts/dual_staking_token/build/DualStakingToken.json +443 -0
- operate/data/contracts/dual_staking_token/contract.py +132 -0
- operate/data/contracts/dual_staking_token/contract.yaml +23 -0
- operate/{ledger/base.py → data/contracts/foreign_omnibridge/__init__.py} +2 -19
- operate/data/contracts/foreign_omnibridge/build/ForeignOmnibridge.json +1372 -0
- operate/data/contracts/foreign_omnibridge/contract.py +130 -0
- operate/data/contracts/foreign_omnibridge/contract.yaml +23 -0
- operate/{ledger/solana.py → data/contracts/home_omnibridge/__init__.py} +2 -20
- operate/data/contracts/home_omnibridge/build/HomeOmnibridge.json +1421 -0
- operate/data/contracts/home_omnibridge/contract.py +80 -0
- operate/data/contracts/home_omnibridge/contract.yaml +23 -0
- operate/data/contracts/l1_standard_bridge/__init__.py +20 -0
- operate/data/contracts/l1_standard_bridge/build/L1StandardBridge.json +831 -0
- operate/data/contracts/l1_standard_bridge/contract.py +158 -0
- operate/data/contracts/l1_standard_bridge/contract.yaml +23 -0
- operate/data/contracts/l2_standard_bridge/__init__.py +20 -0
- operate/data/contracts/l2_standard_bridge/build/L2StandardBridge.json +626 -0
- operate/data/contracts/l2_standard_bridge/contract.py +130 -0
- operate/data/contracts/l2_standard_bridge/contract.yaml +23 -0
- operate/data/contracts/mech_activity/__init__.py +20 -0
- operate/data/contracts/mech_activity/build/MechActivity.json +111 -0
- operate/data/contracts/mech_activity/contract.py +44 -0
- operate/data/contracts/mech_activity/contract.yaml +23 -0
- operate/data/contracts/optimism_mintable_erc20/__init__.py +20 -0
- operate/data/contracts/optimism_mintable_erc20/build/OptimismMintableERC20.json +491 -0
- operate/data/contracts/optimism_mintable_erc20/contract.py +45 -0
- operate/data/contracts/optimism_mintable_erc20/contract.yaml +23 -0
- operate/data/contracts/recovery_module/__init__.py +20 -0
- operate/data/contracts/recovery_module/build/RecoveryModule.json +811 -0
- operate/data/contracts/recovery_module/contract.py +61 -0
- operate/data/contracts/recovery_module/contract.yaml +23 -0
- operate/data/contracts/requester_activity_checker/__init__.py +20 -0
- operate/data/contracts/requester_activity_checker/build/RequesterActivityChecker.json +111 -0
- operate/data/contracts/requester_activity_checker/contract.py +33 -0
- operate/data/contracts/requester_activity_checker/contract.yaml +23 -0
- operate/data/contracts/staking_token/__init__.py +20 -0
- operate/data/contracts/staking_token/build/StakingToken.json +1336 -0
- operate/data/contracts/{service_staking_token → staking_token}/contract.py +27 -13
- operate/data/contracts/staking_token/contract.yaml +23 -0
- operate/data/contracts/uniswap_v2_erc20/contract.yaml +3 -1
- operate/data/contracts/uniswap_v2_erc20/tests/__init__.py +20 -0
- operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py +363 -0
- operate/keys.py +118 -33
- operate/ledger/__init__.py +159 -56
- operate/ledger/profiles.py +321 -18
- operate/migration.py +555 -0
- operate/{http → operate_http}/__init__.py +3 -2
- operate/{http → operate_http}/exceptions.py +6 -4
- operate/operate_types.py +544 -0
- operate/pearl.py +13 -1
- operate/quickstart/analyse_logs.py +118 -0
- operate/quickstart/claim_staking_rewards.py +104 -0
- operate/quickstart/reset_configs.py +106 -0
- operate/quickstart/reset_password.py +70 -0
- operate/quickstart/reset_staking.py +145 -0
- operate/quickstart/run_service.py +726 -0
- operate/quickstart/stop_service.py +72 -0
- operate/quickstart/terminate_on_chain_service.py +83 -0
- operate/quickstart/utils.py +298 -0
- operate/resource.py +62 -3
- operate/services/agent_runner.py +202 -0
- operate/services/deployment_runner.py +868 -0
- operate/services/funding_manager.py +929 -0
- operate/services/health_checker.py +280 -0
- operate/services/manage.py +2356 -620
- operate/services/protocol.py +1246 -340
- operate/services/service.py +756 -391
- operate/services/utils/mech.py +103 -0
- operate/services/utils/tendermint.py +86 -12
- operate/settings.py +70 -0
- operate/utils/__init__.py +135 -0
- operate/utils/gnosis.py +407 -80
- operate/utils/single_instance.py +226 -0
- operate/utils/ssl.py +133 -0
- operate/wallet/master.py +708 -123
- operate/wallet/wallet_recovery_manager.py +507 -0
- olas_operate_middleware-0.1.0rc59.dist-info/METADATA +0 -304
- olas_operate_middleware-0.1.0rc59.dist-info/RECORD +0 -41
- operate/data/contracts/service_staking_token/build/ServiceStakingToken.json +0 -1273
- operate/data/contracts/service_staking_token/contract.yaml +0 -23
- operate/ledger/ethereum.py +0 -48
- operate/types.py +0 -260
- {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info}/entry_points.txt +0 -0
- {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# ------------------------------------------------------------------------------
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2025 Valory AG
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
# ------------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
"""Utility module for enforcing single-instance application behavior and monitoring parent process."""
|
|
21
|
+
|
|
22
|
+
import asyncio
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
import socket
|
|
26
|
+
import time
|
|
27
|
+
from contextlib import suppress
|
|
28
|
+
from typing import Callable, Optional
|
|
29
|
+
|
|
30
|
+
import psutil
|
|
31
|
+
import requests
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AppSingleInstance:
|
|
35
|
+
"""Ensure that only one instance of an application is running."""
|
|
36
|
+
|
|
37
|
+
host = "127.0.0.1"
|
|
38
|
+
after_kill_sleep_time = 1
|
|
39
|
+
proc_kill_wait_timeout = 10
|
|
40
|
+
proc_terminate_wait_timeout = 10
|
|
41
|
+
http_request_timeout = 3
|
|
42
|
+
|
|
43
|
+
def __init__(self, port_number: int, shutdown_endpoint: str = "/shutdown") -> None:
|
|
44
|
+
"""Initialize the AppSingleInstance manager."""
|
|
45
|
+
self.port_number = port_number
|
|
46
|
+
self.shutdown_endpoint = shutdown_endpoint
|
|
47
|
+
self.logger = logging.getLogger("app_single_instance")
|
|
48
|
+
self.logger.setLevel(logging.DEBUG)
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def is_port_in_use(port: int) -> bool:
|
|
52
|
+
"""Return True if a given TCP port is currently in use."""
|
|
53
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
54
|
+
return s.connect_ex(("127.0.0.1", port)) == 0
|
|
55
|
+
|
|
56
|
+
def shutdown_previous_instance(self) -> None:
|
|
57
|
+
"""Attempt to stop a previously running instance of the application."""
|
|
58
|
+
if not self.is_port_in_use(self.port_number):
|
|
59
|
+
self.logger.info(f"Port {self.port_number} is free. All good.")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
self.logger.warning(f"Port {self.port_number} is in use. Trying to free it!")
|
|
63
|
+
self.logger.warning(
|
|
64
|
+
f"Trying to stop previous instance via shutdown endpoint: {self.shutdown_endpoint}"
|
|
65
|
+
)
|
|
66
|
+
self.try_shutdown_with_endpoint()
|
|
67
|
+
|
|
68
|
+
if not self.is_port_in_use(self.port_number):
|
|
69
|
+
self.logger.info(f"Port {self.port_number} is free. All good.")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
self.logger.warning(
|
|
73
|
+
f"Trying to stop previous instance by killing process using port {self.port_number}"
|
|
74
|
+
)
|
|
75
|
+
self.try_kill_proc_using_port()
|
|
76
|
+
|
|
77
|
+
if not self.is_port_in_use(self.port_number):
|
|
78
|
+
self.logger.info(f"Port {self.port_number} is free. All good.")
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
self.logger.error(f"Port {self.port_number} still in use. Cannot continue.")
|
|
82
|
+
raise RuntimeError(f"Port {self.port_number} is in use, cannot continue!")
|
|
83
|
+
|
|
84
|
+
def try_shutdown_with_endpoint(self) -> None:
|
|
85
|
+
"""Attempt to gracefully shut down the previous instance via HTTP or HTTPS."""
|
|
86
|
+
try:
|
|
87
|
+
self.logger.warning(
|
|
88
|
+
"Attempting to stop previous instance via HTTPS shutdown endpoint."
|
|
89
|
+
)
|
|
90
|
+
requests.get(
|
|
91
|
+
f"https://{self.host}:{self.port_number}{self.shutdown_endpoint}",
|
|
92
|
+
timeout=self.http_request_timeout,
|
|
93
|
+
verify=False, # nosec
|
|
94
|
+
)
|
|
95
|
+
time.sleep(self.after_kill_sleep_time)
|
|
96
|
+
except requests.exceptions.SSLError:
|
|
97
|
+
self.logger.warning("HTTPS shutdown failed, retrying without SSL.")
|
|
98
|
+
try:
|
|
99
|
+
requests.get(
|
|
100
|
+
f"http://{self.host}:{self.port_number}{self.shutdown_endpoint}",
|
|
101
|
+
timeout=self.http_request_timeout,
|
|
102
|
+
)
|
|
103
|
+
time.sleep(self.after_kill_sleep_time)
|
|
104
|
+
except Exception as e: # pylint: disable=broad-except
|
|
105
|
+
self.logger.error(
|
|
106
|
+
f"Failed to stop previous instance (HTTP). Error: {e}"
|
|
107
|
+
)
|
|
108
|
+
except Exception as e: # pylint: disable=broad-except
|
|
109
|
+
self.logger.error(f"Failed to stop previous instance (HTTPS). Error: {e}")
|
|
110
|
+
|
|
111
|
+
def try_kill_proc_using_port(self) -> None:
|
|
112
|
+
"""Attempt to forcibly terminate the process occupying the target port."""
|
|
113
|
+
for conn in psutil.net_connections(kind="tcp"):
|
|
114
|
+
if (
|
|
115
|
+
conn.laddr.port == self.port_number
|
|
116
|
+
and conn.status == psutil.CONN_LISTEN
|
|
117
|
+
):
|
|
118
|
+
if conn.pid is None:
|
|
119
|
+
self.logger.info(
|
|
120
|
+
f"Process using port {self.port_number} found but PID is None. Cannot continue."
|
|
121
|
+
)
|
|
122
|
+
return
|
|
123
|
+
self.logger.info(
|
|
124
|
+
f"Process using port {self.port_number} found (PID={conn.pid}). Terminating..."
|
|
125
|
+
)
|
|
126
|
+
try:
|
|
127
|
+
self.kill_process_tree(conn.pid)
|
|
128
|
+
time.sleep(self.after_kill_sleep_time)
|
|
129
|
+
return
|
|
130
|
+
except Exception as e: # pylint: disable=broad-except
|
|
131
|
+
self.logger.error(f"Error stopping process {conn.pid}: {e}")
|
|
132
|
+
self.logger.info(f"No process found using port {self.port_number}.")
|
|
133
|
+
|
|
134
|
+
def kill_process_tree(self, pid: int) -> None:
|
|
135
|
+
"""Terminate a process and all its child processes."""
|
|
136
|
+
try:
|
|
137
|
+
parent = psutil.Process(pid)
|
|
138
|
+
children = parent.children(recursive=True)
|
|
139
|
+
|
|
140
|
+
for child in children:
|
|
141
|
+
with suppress(psutil.NoSuchProcess):
|
|
142
|
+
child.terminate()
|
|
143
|
+
|
|
144
|
+
_, still_alive = psutil.wait_procs(
|
|
145
|
+
children, timeout=self.proc_terminate_wait_timeout
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
for child in still_alive:
|
|
149
|
+
with suppress(psutil.NoSuchProcess):
|
|
150
|
+
child.kill()
|
|
151
|
+
|
|
152
|
+
parent.terminate()
|
|
153
|
+
try:
|
|
154
|
+
parent.wait(timeout=self.proc_terminate_wait_timeout)
|
|
155
|
+
except psutil.TimeoutExpired:
|
|
156
|
+
parent.kill()
|
|
157
|
+
parent.wait(timeout=self.proc_kill_wait_timeout)
|
|
158
|
+
|
|
159
|
+
except psutil.NoSuchProcess:
|
|
160
|
+
self.logger.info(f"Process {pid} already terminated.")
|
|
161
|
+
except Exception as e: # pylint: disable=broad-except
|
|
162
|
+
self.logger.error(f"Error killing process {pid}: {e}")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
logger = logging.getLogger("parent_watchdog")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class ParentWatchdog:
|
|
169
|
+
"""Monitor the parent process and trigger a shutdown when it exits."""
|
|
170
|
+
|
|
171
|
+
def __init__(
|
|
172
|
+
self, on_parent_exit: Callable[[], asyncio.Future], check_interval: int = 3
|
|
173
|
+
) -> None:
|
|
174
|
+
"""Initialize the ParentWatchdog."""
|
|
175
|
+
self.on_parent_exit = on_parent_exit
|
|
176
|
+
self.check_interval = check_interval
|
|
177
|
+
self._task: Optional[asyncio.Task] = None
|
|
178
|
+
self._stopping = False
|
|
179
|
+
|
|
180
|
+
async def _watch_loop(self) -> None:
|
|
181
|
+
"""Continuously monitor the parent process and invoke the shutdown callback when it exits."""
|
|
182
|
+
try:
|
|
183
|
+
own_pid = os.getpid()
|
|
184
|
+
logger.info(f"ParentWatchdog started (pid={own_pid}, ppid={os.getppid()})")
|
|
185
|
+
|
|
186
|
+
while not self._stopping:
|
|
187
|
+
try:
|
|
188
|
+
parent = psutil.Process(os.getppid())
|
|
189
|
+
if not parent.is_running() or os.getppid() == 1:
|
|
190
|
+
logger.warning(
|
|
191
|
+
"Parent process no longer alive, initiating shutdown."
|
|
192
|
+
)
|
|
193
|
+
await self.on_parent_exit()
|
|
194
|
+
break
|
|
195
|
+
except psutil.NoSuchProcess:
|
|
196
|
+
logger.warning("Parent process not found, initiating shutdown.")
|
|
197
|
+
await self.on_parent_exit()
|
|
198
|
+
break
|
|
199
|
+
except Exception: # pylint: disable=broad-except
|
|
200
|
+
logger.exception("Parent check iteration failed.")
|
|
201
|
+
await asyncio.sleep(self.check_interval)
|
|
202
|
+
|
|
203
|
+
except asyncio.CancelledError:
|
|
204
|
+
logger.info("ParentWatchdog task cancelled.")
|
|
205
|
+
except Exception: # pylint: disable=broad-except
|
|
206
|
+
logger.exception("ParentWatchdog crashed unexpectedly.")
|
|
207
|
+
finally:
|
|
208
|
+
logger.info("ParentWatchdog stopped.")
|
|
209
|
+
|
|
210
|
+
def start(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> asyncio.Task:
|
|
211
|
+
"""Start monitoring the parent process."""
|
|
212
|
+
if self._task:
|
|
213
|
+
logger.warning("ParentWatchdog already running.")
|
|
214
|
+
return self._task
|
|
215
|
+
loop = loop or asyncio.get_running_loop()
|
|
216
|
+
self._task = loop.create_task(self._watch_loop())
|
|
217
|
+
return self._task
|
|
218
|
+
|
|
219
|
+
async def stop(self) -> None:
|
|
220
|
+
"""Stop the parent process watchdog."""
|
|
221
|
+
self._stopping = True
|
|
222
|
+
if self._task:
|
|
223
|
+
with suppress(Exception):
|
|
224
|
+
self._task.cancel()
|
|
225
|
+
await self._task
|
|
226
|
+
self._task = None
|
operate/utils/ssl.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# ------------------------------------------------------------------------------
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2025 Valory AG
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
# ------------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
"""SSL certificate utilities."""
|
|
21
|
+
|
|
22
|
+
import datetime
|
|
23
|
+
import logging
|
|
24
|
+
import typing as t
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
from cryptography import x509
|
|
28
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
|
29
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
30
|
+
from cryptography.x509.oid import NameOID
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_ssl_certificate(
|
|
34
|
+
ssl_dir: Path,
|
|
35
|
+
key_filename: str = "key.pem",
|
|
36
|
+
cert_filename: str = "cert.pem",
|
|
37
|
+
validity_days: int = 365,
|
|
38
|
+
key_size: int = 2048,
|
|
39
|
+
common_name: str = "localhost",
|
|
40
|
+
) -> t.Tuple[Path, Path]:
|
|
41
|
+
"""
|
|
42
|
+
Create SSL certificate and private key files.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
ssl_dir: Path to the ssl directory
|
|
46
|
+
key_filename: Name of the private key file
|
|
47
|
+
cert_filename: Name of the certificate file
|
|
48
|
+
validity_days: Number of days the certificate is valid
|
|
49
|
+
key_size: RSA key size in bits
|
|
50
|
+
common_name: Common name for the certificate
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Tuple of (key_path, cert_path) as Path objects
|
|
54
|
+
"""
|
|
55
|
+
logger = logging.getLogger(__name__)
|
|
56
|
+
|
|
57
|
+
# Create SSL directory
|
|
58
|
+
ssl_dir.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
|
|
60
|
+
key_path = ssl_dir / key_filename
|
|
61
|
+
cert_path = ssl_dir / cert_filename
|
|
62
|
+
|
|
63
|
+
# Generate RSA private key
|
|
64
|
+
private_key = rsa.generate_private_key(
|
|
65
|
+
public_exponent=65537,
|
|
66
|
+
key_size=key_size,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Create certificate subject and issuer
|
|
70
|
+
subject = issuer = x509.Name(
|
|
71
|
+
[
|
|
72
|
+
x509.NameAttribute(NameOID.COUNTRY_NAME, "CH"),
|
|
73
|
+
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Local"),
|
|
74
|
+
x509.NameAttribute(NameOID.LOCALITY_NAME, "Local"),
|
|
75
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Valory AG"),
|
|
76
|
+
x509.NameAttribute(NameOID.COMMON_NAME, common_name),
|
|
77
|
+
]
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Create certificate
|
|
81
|
+
cert = (
|
|
82
|
+
x509.CertificateBuilder()
|
|
83
|
+
.subject_name(subject)
|
|
84
|
+
.issuer_name(issuer)
|
|
85
|
+
.public_key(private_key.public_key())
|
|
86
|
+
.serial_number(1)
|
|
87
|
+
.not_valid_before(datetime.datetime.now(datetime.timezone.utc))
|
|
88
|
+
.not_valid_after(
|
|
89
|
+
datetime.datetime.now(datetime.timezone.utc)
|
|
90
|
+
+ datetime.timedelta(days=validity_days)
|
|
91
|
+
)
|
|
92
|
+
.add_extension(
|
|
93
|
+
x509.BasicConstraints(ca=False, path_length=None),
|
|
94
|
+
critical=True,
|
|
95
|
+
)
|
|
96
|
+
.add_extension(
|
|
97
|
+
x509.KeyUsage(
|
|
98
|
+
digital_signature=True,
|
|
99
|
+
key_encipherment=True,
|
|
100
|
+
key_agreement=False,
|
|
101
|
+
key_cert_sign=False,
|
|
102
|
+
crl_sign=False,
|
|
103
|
+
content_commitment=False,
|
|
104
|
+
data_encipherment=False,
|
|
105
|
+
encipher_only=False,
|
|
106
|
+
decipher_only=False,
|
|
107
|
+
),
|
|
108
|
+
critical=True,
|
|
109
|
+
)
|
|
110
|
+
.add_extension(
|
|
111
|
+
x509.ExtendedKeyUsage([x509.ExtendedKeyUsageOID.SERVER_AUTH]),
|
|
112
|
+
critical=True,
|
|
113
|
+
)
|
|
114
|
+
.sign(private_key, hashes.SHA256())
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Write private key to file
|
|
118
|
+
with open(key_path, "wb") as f:
|
|
119
|
+
f.write(
|
|
120
|
+
private_key.private_bytes(
|
|
121
|
+
encoding=serialization.Encoding.PEM,
|
|
122
|
+
format=serialization.PrivateFormat.PKCS8,
|
|
123
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Write certificate to file
|
|
128
|
+
with open(cert_path, "wb") as f:
|
|
129
|
+
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
|
130
|
+
|
|
131
|
+
logger.info(f"SSL certificate created successfully at {key_path} and {cert_path}")
|
|
132
|
+
|
|
133
|
+
return key_path, cert_path
|