tng-sdk 2.7.3__py3-none-win_amd64.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.
- bin/scripts/tng.exe +0 -0
- tng/__init__.py +45 -0
- tng/_tng.py +394 -0
- tng/adapters/__init__.py +0 -0
- tng_sdk-2.7.3.data/data/bin/tng.exe +0 -0
- tng_sdk-2.7.3.dist-info/METADATA +96 -0
- tng_sdk-2.7.3.dist-info/RECORD +8 -0
- tng_sdk-2.7.3.dist-info/WHEEL +4 -0
bin/scripts/tng.exe
ADDED
|
Binary file
|
tng/__init__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""TNG (Trusted Network Gateway) Python SDK.
|
|
2
|
+
|
|
3
|
+
Provides encrypted HTTP requests with remote attestation by managing
|
|
4
|
+
a TNG subprocess (http_proxy ingress) and using each HTTP library's
|
|
5
|
+
native proxy support to route traffic through the encrypted tunnel.
|
|
6
|
+
|
|
7
|
+
The user only provides security options; the target address is carried
|
|
8
|
+
in the request URL's Host header and read by the http_proxy ingress.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from tng import Tng
|
|
12
|
+
|
|
13
|
+
# Simplest: disable remote attestation (for testing)
|
|
14
|
+
tng = Tng(no_ra=True)
|
|
15
|
+
|
|
16
|
+
# With verifier
|
|
17
|
+
tng = Tng(
|
|
18
|
+
verify={
|
|
19
|
+
"as_addr": "http://127.0.0.1:8080/",
|
|
20
|
+
"policy_ids": ["default"],
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Wrap requests.Session
|
|
25
|
+
import requests
|
|
26
|
+
session = requests.Session()
|
|
27
|
+
tng.wrap_requests(session)
|
|
28
|
+
resp = session.get("http://tng-server:10001/api/data")
|
|
29
|
+
|
|
30
|
+
# Wrap httpx.Client
|
|
31
|
+
import httpx
|
|
32
|
+
client = httpx.Client()
|
|
33
|
+
tng.wrap_httpx(client)
|
|
34
|
+
resp = client.get("http://tng-server:10001/api/data")
|
|
35
|
+
|
|
36
|
+
# Wrap OpenAI client
|
|
37
|
+
from openai import OpenAI
|
|
38
|
+
openai_client = OpenAI(api_key="sk-xxx", base_url="http://tng-server:10001/v1")
|
|
39
|
+
tng.wrap_openai(openai_client)
|
|
40
|
+
completion = openai_client.chat.completions.create(model="xxx", messages=[...])
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
from tng._tng import Tng
|
|
44
|
+
|
|
45
|
+
__all__ = ["Tng"]
|
tng/_tng.py
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"""Tng class: manages a TNG subprocess (http_proxy ingress) and provides
|
|
2
|
+
wrapper methods for requests, httpx, and OpenAI using native proxy support."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import atexit
|
|
7
|
+
import copy
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import shutil
|
|
11
|
+
import socket
|
|
12
|
+
import subprocess
|
|
13
|
+
import tempfile
|
|
14
|
+
import time
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
__all__ = ["Tng"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Tng:
|
|
22
|
+
"""High-level TNG client.
|
|
23
|
+
|
|
24
|
+
Starts the TNG binary as a subprocess with an auto-configured
|
|
25
|
+
``http_proxy`` ingress, then provides ``wrap_*`` methods that use
|
|
26
|
+
each library's native HTTP proxy support to route traffic through
|
|
27
|
+
the encrypted TNG tunnel.
|
|
28
|
+
|
|
29
|
+
The user only specifies security options (``no_ra``, ``verify``,
|
|
30
|
+
``attest``, ``ohttp``, ``rats_tls``). The target address is carried
|
|
31
|
+
in the request URL's Host header and read by the http_proxy ingress.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
no_ra: bool = False,
|
|
37
|
+
verify: dict[str, Any] | None = None,
|
|
38
|
+
attest: dict[str, Any] | None = None,
|
|
39
|
+
ohttp: dict[str, Any] | None = None,
|
|
40
|
+
rats_tls: dict[str, Any] | None = None,
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Create and start a local TNG proxy.
|
|
43
|
+
|
|
44
|
+
Spawns the ``tng`` binary as a subprocess with an auto-configured
|
|
45
|
+
``http_proxy`` ingress, then provides ``wrap_*`` methods that route
|
|
46
|
+
HTTP traffic through the encrypted tunnel.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
no_ra:
|
|
50
|
+
Disable remote attestation. Set to ``True`` for local testing
|
|
51
|
+
or when the AA/AS services are unavailable.
|
|
52
|
+
verify:
|
|
53
|
+
Verifier configuration for validating the remote server's
|
|
54
|
+
attestation. Keys: ``as_addr`` (Attestation Service URL),
|
|
55
|
+
``policy_ids`` (list of policy IDs to verify against).
|
|
56
|
+
attest:
|
|
57
|
+
Attester configuration for providing the client's own
|
|
58
|
+
attestation to the server. Keys: ``aa_addr`` (Attestation
|
|
59
|
+
Agent socket path), ``model`` (e.g. ``"passport"``),
|
|
60
|
+
``as_addr`` (optional, for nested attestation).
|
|
61
|
+
ohttp:
|
|
62
|
+
OHTTP configuration dict. Omitted keys use defaults.
|
|
63
|
+
Common keys: ``key`` (``{"source": "self_generated",
|
|
64
|
+
"rotation_interval": 300}``), ``path_rewrites``.
|
|
65
|
+
rats_tls:
|
|
66
|
+
rats-TLS configuration dict as an alternative to OHTTP.
|
|
67
|
+
Common keys: ``multiplex`` (enable connection multiplexing).
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
ValueError: If both ``ohttp`` and ``rats_tls`` are provided
|
|
71
|
+
(they are mutually exclusive).
|
|
72
|
+
FileNotFoundError: If the ``tng`` binary cannot be found.
|
|
73
|
+
RuntimeError: If the TNG process fails to start.
|
|
74
|
+
TimeoutError: If the proxy port is not ready within 30 seconds.
|
|
75
|
+
|
|
76
|
+
Example::
|
|
77
|
+
|
|
78
|
+
# Simplest: no remote attestation
|
|
79
|
+
tng = Tng(no_ra=True)
|
|
80
|
+
|
|
81
|
+
# With server verification
|
|
82
|
+
tng = Tng(verify={"as_addr": "http://127.0.0.1:8080/", "policy_ids": ["default"]})
|
|
83
|
+
|
|
84
|
+
# Using rats-TLS instead of OHTTP
|
|
85
|
+
tng = Tng(no_ra=True, rats_tls={"multiplex": True})
|
|
86
|
+
|
|
87
|
+
# Context manager for automatic cleanup
|
|
88
|
+
with Tng(no_ra=True) as tng:
|
|
89
|
+
tng.wrap_requests(session)
|
|
90
|
+
resp = session.get("http://server:10001/api/data")
|
|
91
|
+
"""
|
|
92
|
+
# Validate mutual exclusivity: ohttp and rats_tls conflict
|
|
93
|
+
if ohttp is not None and rats_tls is not None:
|
|
94
|
+
raise ValueError("ohttp and rats_tls are mutually exclusive")
|
|
95
|
+
|
|
96
|
+
# Auto-assign http_proxy port
|
|
97
|
+
proxy_port = _find_free_port()
|
|
98
|
+
self._proxy_port: int = proxy_port
|
|
99
|
+
|
|
100
|
+
# Build TNG config (ingress only, no egress)
|
|
101
|
+
config = _build_tng_config(
|
|
102
|
+
proxy_port=proxy_port,
|
|
103
|
+
no_ra=no_ra,
|
|
104
|
+
verify=verify,
|
|
105
|
+
attest=attest,
|
|
106
|
+
ohttp=ohttp,
|
|
107
|
+
rats_tls=rats_tls,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Find TNG binary
|
|
111
|
+
tng_bin = _find_tng_binary()
|
|
112
|
+
|
|
113
|
+
# Write config to temp JSON file
|
|
114
|
+
fd, config_path = tempfile.mkstemp(suffix=".json", prefix="tng_cfg_")
|
|
115
|
+
try:
|
|
116
|
+
with os.fdopen(fd, "w") as f:
|
|
117
|
+
json.dump(config, f)
|
|
118
|
+
except Exception:
|
|
119
|
+
os.unlink(config_path)
|
|
120
|
+
raise
|
|
121
|
+
self._config_path: str = config_path
|
|
122
|
+
|
|
123
|
+
# Start subprocess — capture stderr to a temp file for debugging
|
|
124
|
+
import io
|
|
125
|
+
|
|
126
|
+
stderr_path = tempfile.mkstemp(suffix=".log", prefix="tng_stderr_")[1]
|
|
127
|
+
self._stderr_path: str = stderr_path
|
|
128
|
+
self._proc: subprocess.Popen = subprocess.Popen(
|
|
129
|
+
[tng_bin, "launch", "--config-file", config_path],
|
|
130
|
+
stdout=subprocess.DEVNULL,
|
|
131
|
+
stderr=open(stderr_path, "w"),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Wait for proxy port to be ready, checking for process failure
|
|
135
|
+
_wait_for_port("127.0.0.1", proxy_port, self._proc, timeout=30.0)
|
|
136
|
+
|
|
137
|
+
# Register cleanup
|
|
138
|
+
atexit.register(self._cleanup)
|
|
139
|
+
|
|
140
|
+
# ------------------------------------------------------------------
|
|
141
|
+
# Wrapper injection methods — native proxy support
|
|
142
|
+
# ------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
def wrap_requests(self, session: Any) -> Any:
|
|
145
|
+
"""Inject the TNG http_proxy into a ``requests.Session``.
|
|
146
|
+
|
|
147
|
+
After calling this, all ``session.get/post/request(...)`` calls
|
|
148
|
+
go through the TNG encrypted tunnel.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
session: A ``requests.Session`` instance.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
The same session (for chaining).
|
|
155
|
+
"""
|
|
156
|
+
proxy_url = f"http://127.0.0.1:{self._proxy_port}"
|
|
157
|
+
session.proxies = {"http": proxy_url, "https": proxy_url}
|
|
158
|
+
# Disable trust_env so system proxy env vars don't interfere
|
|
159
|
+
session.trust_env = False
|
|
160
|
+
return session
|
|
161
|
+
|
|
162
|
+
def wrap_httpx(self, client: Any) -> Any:
|
|
163
|
+
"""Inject the TNG http_proxy into an ``httpx.Client`` or
|
|
164
|
+
``httpx.AsyncClient``.
|
|
165
|
+
|
|
166
|
+
After calling this, all ``client.get/post/request(...)`` calls
|
|
167
|
+
go through the TNG encrypted tunnel.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
client: An ``httpx.Client`` or ``httpx.AsyncClient`` instance.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
The same client (for chaining).
|
|
174
|
+
"""
|
|
175
|
+
import httpx
|
|
176
|
+
from httpx._utils import URLPattern
|
|
177
|
+
|
|
178
|
+
proxy_url = f"http://127.0.0.1:{self._proxy_port}"
|
|
179
|
+
proxy = httpx.Proxy(proxy_url)
|
|
180
|
+
|
|
181
|
+
# Set _proxy for introspection compatibility, and _mounts for routing.
|
|
182
|
+
# _init_proxy_transport creates a properly configured transport with
|
|
183
|
+
# SSL, timeouts, etc. We mount it for all:// URLs.
|
|
184
|
+
proxy_transport = client._init_proxy_transport(
|
|
185
|
+
proxy, verify=True, cert=None, trust_env=False,
|
|
186
|
+
http1=True, http2=False, limits=httpx.Limits(),
|
|
187
|
+
)
|
|
188
|
+
client._proxy = proxy
|
|
189
|
+
client._mounts = {URLPattern("all://"): proxy_transport}
|
|
190
|
+
return client
|
|
191
|
+
|
|
192
|
+
def wrap_openai(self, client: Any) -> Any:
|
|
193
|
+
"""Inject the TNG http_proxy into an ``openai.OpenAI`` or
|
|
194
|
+
``openai.AsyncOpenAI`` client.
|
|
195
|
+
|
|
196
|
+
OpenAI internally uses ``httpx.Client`` / ``httpx.AsyncClient``
|
|
197
|
+
stored at ``client._client``. We set its proxy so all API calls
|
|
198
|
+
go through TNG.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
client: An ``openai.OpenAI`` or ``openai.AsyncOpenAI`` instance.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
The same client (for chaining).
|
|
205
|
+
"""
|
|
206
|
+
import httpx
|
|
207
|
+
from httpx._utils import URLPattern
|
|
208
|
+
|
|
209
|
+
proxy_url = f"http://127.0.0.1:{self._proxy_port}"
|
|
210
|
+
proxy = httpx.Proxy(proxy_url)
|
|
211
|
+
inner = client._client
|
|
212
|
+
|
|
213
|
+
proxy_transport = inner._init_proxy_transport(
|
|
214
|
+
proxy, verify=True, cert=None, trust_env=False,
|
|
215
|
+
http1=True, http2=False, limits=httpx.Limits(),
|
|
216
|
+
)
|
|
217
|
+
inner._proxy = proxy
|
|
218
|
+
inner._mounts = {URLPattern("all://"): proxy_transport}
|
|
219
|
+
return client
|
|
220
|
+
|
|
221
|
+
# ------------------------------------------------------------------
|
|
222
|
+
# Cleanup
|
|
223
|
+
# ------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
def _cleanup(self) -> None:
|
|
226
|
+
"""Terminate the TNG subprocess and remove the temp config file."""
|
|
227
|
+
if getattr(self, "_proc", None) is not None:
|
|
228
|
+
proc = self._proc
|
|
229
|
+
if proc.poll() is None:
|
|
230
|
+
proc.terminate()
|
|
231
|
+
try:
|
|
232
|
+
proc.wait(timeout=10)
|
|
233
|
+
except subprocess.TimeoutExpired:
|
|
234
|
+
proc.kill()
|
|
235
|
+
proc.wait(timeout=5)
|
|
236
|
+
self._proc = None # type: ignore[assignment]
|
|
237
|
+
|
|
238
|
+
config_path = getattr(self, "_config_path", None)
|
|
239
|
+
if config_path and os.path.exists(config_path):
|
|
240
|
+
try:
|
|
241
|
+
os.unlink(config_path)
|
|
242
|
+
except OSError:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
def __del__(self) -> None:
|
|
246
|
+
self._cleanup()
|
|
247
|
+
|
|
248
|
+
def close(self) -> None:
|
|
249
|
+
"""Explicitly stop the TNG subprocess and clean up resources.
|
|
250
|
+
|
|
251
|
+
Safe to call multiple times.
|
|
252
|
+
"""
|
|
253
|
+
self._cleanup()
|
|
254
|
+
|
|
255
|
+
def __enter__(self) -> "Tng":
|
|
256
|
+
return self
|
|
257
|
+
|
|
258
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
259
|
+
self._cleanup()
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# ---------------------------------------------------------------------------
|
|
263
|
+
# Helpers
|
|
264
|
+
# ---------------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _find_free_port() -> int:
|
|
268
|
+
"""Find a free TCP port on 127.0.0.1."""
|
|
269
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
270
|
+
s.bind(("127.0.0.1", 0))
|
|
271
|
+
return s.getsockname()[1]
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _wait_for_port(
|
|
275
|
+
host: str, port: int, proc: subprocess.Popen | None = None, timeout: float = 30.0,
|
|
276
|
+
) -> None:
|
|
277
|
+
"""Wait until a TCP port is accepting connections.
|
|
278
|
+
|
|
279
|
+
If *proc* is given, also checks whether the process exited early
|
|
280
|
+
and raises a more useful error message.
|
|
281
|
+
|
|
282
|
+
Raises:
|
|
283
|
+
RuntimeError: If the subprocess exited before the port was ready.
|
|
284
|
+
TimeoutError: If the port is not ready within the timeout.
|
|
285
|
+
"""
|
|
286
|
+
deadline = time.monotonic() + timeout
|
|
287
|
+
while time.monotonic() < deadline:
|
|
288
|
+
# Check if the subprocess exited early
|
|
289
|
+
if proc is not None:
|
|
290
|
+
exit_code = proc.poll()
|
|
291
|
+
if exit_code is not None:
|
|
292
|
+
raise RuntimeError(
|
|
293
|
+
f"TNG process exited with code {exit_code} before proxy was ready"
|
|
294
|
+
)
|
|
295
|
+
try:
|
|
296
|
+
with socket.create_connection((host, port), timeout=0.5):
|
|
297
|
+
return
|
|
298
|
+
except (ConnectionRefusedError, OSError):
|
|
299
|
+
time.sleep(0.1)
|
|
300
|
+
raise TimeoutError(f"Port {port} not ready within {timeout}s")
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _find_tng_binary() -> str:
|
|
304
|
+
"""Locate the TNG binary.
|
|
305
|
+
|
|
306
|
+
Search order:
|
|
307
|
+
1. ``TNG_BINARY`` environment variable (if set)
|
|
308
|
+
2. ``shutil.which("tng")`` — system PATH (wheel installs to {prefix}/bin/tng)
|
|
309
|
+
3. ``{module_dir}/../bin/tng`` — development mode
|
|
310
|
+
4. ``/usr/bin/tng`` — system install (Unix only)
|
|
311
|
+
"""
|
|
312
|
+
import sys
|
|
313
|
+
|
|
314
|
+
# 1. Check environment variable
|
|
315
|
+
env_bin = os.environ.get("TNG_BINARY")
|
|
316
|
+
if env_bin and Path(env_bin).is_file():
|
|
317
|
+
return env_bin
|
|
318
|
+
|
|
319
|
+
# 2. Check PATH (wheel installation puts it in {prefix}/bin/tng)
|
|
320
|
+
found = shutil.which("tng")
|
|
321
|
+
if found:
|
|
322
|
+
return found
|
|
323
|
+
|
|
324
|
+
# 3. Check development mode location
|
|
325
|
+
module_dir = Path(__file__).resolve().parent
|
|
326
|
+
# Try platform-specific binary names
|
|
327
|
+
suffix = ".exe" if sys.platform == "win32" else ""
|
|
328
|
+
dev_bin = module_dir.parent / "bin" / f"tng{suffix}"
|
|
329
|
+
if dev_bin.is_file():
|
|
330
|
+
return str(dev_bin)
|
|
331
|
+
|
|
332
|
+
# 4. Check system install (Unix only)
|
|
333
|
+
if sys.platform != "win32":
|
|
334
|
+
system = Path("/usr/bin/tng")
|
|
335
|
+
if system.is_file():
|
|
336
|
+
return str(system)
|
|
337
|
+
|
|
338
|
+
raise FileNotFoundError(
|
|
339
|
+
"TNG binary not found. Options:\n"
|
|
340
|
+
" 1. Set TNG_BINARY environment variable to the tng binary path\n"
|
|
341
|
+
" 2. Install tng on your system PATH\n"
|
|
342
|
+
" 3. Place tng binary at tng-python/bin/tng (development mode)"
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _build_tng_config(
|
|
347
|
+
proxy_port: int,
|
|
348
|
+
no_ra: bool,
|
|
349
|
+
verify: dict[str, Any] | None,
|
|
350
|
+
attest: dict[str, Any] | None,
|
|
351
|
+
ohttp: dict[str, Any] | None,
|
|
352
|
+
rats_tls: dict[str, Any] | None,
|
|
353
|
+
) -> dict[str, Any]:
|
|
354
|
+
"""Build the TNG config dict (ingress only, no egress).
|
|
355
|
+
|
|
356
|
+
The http_proxy ingress listens on ``127.0.0.1:<proxy_port>``.
|
|
357
|
+
Incoming requests have their target extracted from the Host header.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
proxy_port: Port for the http_proxy ingress to listen on.
|
|
361
|
+
no_ra: Disable remote attestation (debugging only).
|
|
362
|
+
verify: Verifier config (as_addr, policy_ids, etc.).
|
|
363
|
+
attest: Attester config (aa_addr, as_addr, model, etc.).
|
|
364
|
+
ohttp: OHTTP customization (key, path_rewrites, etc.).
|
|
365
|
+
rats_tls: rats-TLS customization.
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
A TNG config dict with ``add_ingress`` only.
|
|
369
|
+
"""
|
|
370
|
+
ingress_entry: dict[str, Any] = {
|
|
371
|
+
"http_proxy": {
|
|
372
|
+
"proxy_listen": {"host": "127.0.0.1", "port": proxy_port},
|
|
373
|
+
},
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
# Default to ohttp={} when neither ohttp nor rats_tls is specified
|
|
377
|
+
if ohttp is not None:
|
|
378
|
+
ingress_entry["ohttp"] = copy.deepcopy(ohttp)
|
|
379
|
+
elif rats_tls is None:
|
|
380
|
+
ingress_entry["ohttp"] = {}
|
|
381
|
+
|
|
382
|
+
if rats_tls is not None:
|
|
383
|
+
ingress_entry["rats_tls"] = copy.deepcopy(rats_tls)
|
|
384
|
+
|
|
385
|
+
if no_ra:
|
|
386
|
+
ingress_entry["no_ra"] = True
|
|
387
|
+
|
|
388
|
+
if verify is not None:
|
|
389
|
+
ingress_entry["verify"] = copy.deepcopy(verify)
|
|
390
|
+
|
|
391
|
+
if attest is not None:
|
|
392
|
+
ingress_entry["attest"] = copy.deepcopy(attest)
|
|
393
|
+
|
|
394
|
+
return {"add_ingress": [ingress_entry]}
|
tng/adapters/__init__.py
ADDED
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tng-sdk
|
|
3
|
+
Version: 2.7.3
|
|
4
|
+
Summary: TNG (Trusted Network Gateway) Python SDK — encrypted HTTP requests with remote attestation
|
|
5
|
+
Project-URL: Homepage, https://github.com/inclavare-containers/TNG
|
|
6
|
+
Project-URL: Documentation, https://github.com/inclavare-containers/TNG/tree/master/tng-python
|
|
7
|
+
Project-URL: Source, https://github.com/inclavare-containers/TNG/tree/master/tng-python
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
12
|
+
Requires-Dist: openai>=1.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: requests>=2.28; extra == 'dev'
|
|
16
|
+
Provides-Extra: httpx
|
|
17
|
+
Requires-Dist: httpx>=0.27; extra == 'httpx'
|
|
18
|
+
Provides-Extra: openai
|
|
19
|
+
Requires-Dist: openai>=1.0; extra == 'openai'
|
|
20
|
+
Provides-Extra: requests
|
|
21
|
+
Requires-Dist: requests>=2.28; extra == 'requests'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# TNG Python SDK
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/tng-sdk/)
|
|
27
|
+
|
|
28
|
+
[中文文档](https://github.com/inclavare-containers/TNG/blob/master/tng-python/README_zh.md)
|
|
29
|
+
|
|
30
|
+
Trusted Network Gateway (TNG) Python SDK — encrypted HTTP requests with remote attestation.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install tng-sdk
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from tng import Tng
|
|
40
|
+
import requests
|
|
41
|
+
|
|
42
|
+
# Create TNG client (no_ra = disable remote attestation for testing)
|
|
43
|
+
tng = Tng(no_ra=True)
|
|
44
|
+
|
|
45
|
+
# Wrap a requests session
|
|
46
|
+
session = requests.Session()
|
|
47
|
+
tng.wrap_requests(session)
|
|
48
|
+
|
|
49
|
+
# All requests flow through the encrypted TNG tunnel
|
|
50
|
+
resp = session.get("http://tng-server:10001/api/data")
|
|
51
|
+
print(resp.json())
|
|
52
|
+
|
|
53
|
+
tng.close()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Supported Clients
|
|
57
|
+
|
|
58
|
+
| Client | Method |
|
|
59
|
+
|--------|--------|
|
|
60
|
+
| **requests** | `tng.wrap_requests(session)` |
|
|
61
|
+
| **httpx** | `tng.wrap_httpx(client)` |
|
|
62
|
+
| **openai** | `tng.wrap_openai(client)` |
|
|
63
|
+
|
|
64
|
+
## Security Options
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
# Disable remote attestation (testing only)
|
|
68
|
+
tng = Tng(no_ra=True)
|
|
69
|
+
|
|
70
|
+
# With verifier
|
|
71
|
+
tng = Tng(verify={"as_addr": "http://127.0.0.1:8080/", "policy_ids": ["default"]})
|
|
72
|
+
|
|
73
|
+
# With attester
|
|
74
|
+
tng = Tng(attest={"aa_addr": "unix:///run/aa.sock", "model": "passport"})
|
|
75
|
+
|
|
76
|
+
# Bidirectional attestation
|
|
77
|
+
tng = Tng(
|
|
78
|
+
attest={"aa_addr": "unix:///run/aa.sock"},
|
|
79
|
+
verify={"as_addr": "http://127.0.0.1:8080/", "policy_ids": ["default"]}
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Encryption Protocol
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# OHTTP is the default
|
|
87
|
+
tng = Tng(no_ra=True)
|
|
88
|
+
|
|
89
|
+
# Use rats-TLS instead of OHTTP (mutually exclusive)
|
|
90
|
+
tng = Tng(no_ra=True, rats_tls={"multiplex": True})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
- **[Getting Started](https://github.com/inclavare-containers/TNG/blob/master/tng-python/docs/getting-started.md)** — Complete installation, usage, and configuration guide
|
|
96
|
+
- **[Getting Started (中文)](https://github.com/inclavare-containers/TNG/blob/master/tng-python/docs/getting-started_zh.md)** — 完整的安装、使用和配置指南
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
bin/scripts/tng.exe,sha256=uu1IQXRISXKBcTiSNjYMCA1RLC8UYGcJGhSTqZVxvGM,37597696
|
|
2
|
+
tng/__init__.py,sha256=xEv5jAfrhwLgMMBFTDx7DnI1jzi7CBsX5V8ElrBhhgc,1301
|
|
3
|
+
tng/_tng.py,sha256=OgeHK99Faa6QibK2lxjafGsRwN-1S_2zd3Gc88kb8JA,13633
|
|
4
|
+
tng/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
tng_sdk-2.7.3.data/data/bin/tng.exe,sha256=uu1IQXRISXKBcTiSNjYMCA1RLC8UYGcJGhSTqZVxvGM,37597696
|
|
6
|
+
tng_sdk-2.7.3.dist-info/METADATA,sha256=nDZ0qQ5uzsImcl7vRcN3wlacJsjReKHN0D1iWzHTpgo,2885
|
|
7
|
+
tng_sdk-2.7.3.dist-info/WHEEL,sha256=_VsS0eVp3FGI7Mp-ReiZMJWtuJY7p8LSM-m3Ez2oUOk,93
|
|
8
|
+
tng_sdk-2.7.3.dist-info/RECORD,,
|