86 lines
2.5 KiB
Python
86 lines
2.5 KiB
Python
# tractor: distributed structured concurrency.
|
|
r'''
|
|
Verify `wg` keys declared by tractor's multiaddr parser.
|
|
|
|
`tractor.discovery.parse_wg_maddr()` owns pure parsing and delegates
|
|
all tunnel peeling to `py-multiaddr`. This example keeps only the
|
|
explicit impure probe used by the two-host demo; parsing never shells
|
|
out or verifies local interface state implicitly.
|
|
|
|
The canonical maddr form is:
|
|
|
|
/ip4/10.0.0.1/udp/51820/wg/u<key>/ip4/10.0.11.1/tcp/1616
|
|
\_______ wg bearer ______/\_ key _/\____ tractor ep _____/
|
|
|
|
The kernel owns the bearer socket. A future tractor bindspace may
|
|
provision it through netlink, but only the overlay is an application
|
|
`MsgTransport` endpoint.
|
|
|
|
'''
|
|
from __future__ import annotations
|
|
from typing import Literal
|
|
|
|
import trio
|
|
|
|
from tractor.discovery import (
|
|
TunnelledAddress,
|
|
WGTunnelSpec,
|
|
)
|
|
|
|
|
|
async def verify_wg_key(
|
|
addr: TunnelledAddress,
|
|
role: Literal['local', 'peer'],
|
|
iface: str | None = None,
|
|
timeout: float = 5,
|
|
inspection: str | None = None,
|
|
) -> bool:
|
|
'''
|
|
Verify the declared key in the role required on this host.
|
|
|
|
A bearer host uses `role='local'`; a dialer uses `role='peer'`.
|
|
This verifies only key presence. It does not inspect the peer's
|
|
endpoint, AllowedIPs, handshake state, or iface addresses.
|
|
|
|
`inspection` accepts output captured by a separate privileged
|
|
`wg show` step. Without it, query asynchronously for callers
|
|
which already have interface-inspection permission.
|
|
|
|
IMPURE + explicit by design: neither `parse_wg_maddr()` nor
|
|
`tractor.discovery.parse_maddr()` calls this probe.
|
|
|
|
?TODO, per plan-03 layer B, swap this body for `pyroute2`
|
|
while retaining the explicit verification boundary.
|
|
|
|
'''
|
|
spec = addr.tunnel
|
|
if not isinstance(spec, WGTunnelSpec):
|
|
raise TypeError(
|
|
f'Unsupported tunnel spec: {type(spec)!r}'
|
|
)
|
|
|
|
iface = iface or spec.iface
|
|
|
|
match role:
|
|
case 'local':
|
|
field = 'public-key'
|
|
case 'peer':
|
|
field = 'peers'
|
|
case _:
|
|
raise ValueError(
|
|
f'Unknown WireGuard key role: {role!r}'
|
|
)
|
|
|
|
if inspection is None:
|
|
with trio.fail_after(timeout):
|
|
proc = await trio.run_process(
|
|
['wg', 'show', iface, field],
|
|
capture_stdout=True,
|
|
check=True,
|
|
)
|
|
inspection = proc.stdout.decode()
|
|
|
|
if role == 'local':
|
|
return spec.peer_pubkey == inspection.strip()
|
|
return spec.peer_pubkey in inspection.split()
|