add transparent server mode based on WireGuard (#5562)

* add mode spec for WireGuard mode

* add WireGuard server implementation

* remove coverage excludes

* simplify wireguard spec

* lint!

* remove superfluous tests

* bump to mitmproxy_wireguard 0.1.1

* proxy/test_mode_specs: remove unused import

* fix wireguard server mode

* WireGuard: move keyfile gen into `.start()`

This way any file format errors result in `.last_exception` being set.

* fixup UDP support

* bump to mitmproxy_wireguard v0.1.2

This release fixes TCP connections which were broken in v0.1.1.

* fix crash handler

* add simple test for WireGuard server instances

* bump to mitmproxy_wireguard v0.1.5 and fix launching wg-test-client

* fixups

 - monkeypatch `handle_client` instead of the handlers.
 - fix OS detection
 - ctx.log -> logging

* nits

* bump to mitmproxy_wireguard 0.1.6 for fixed test client

* move WireGuardDatagramTransport into dedicated module

this allows us to exclude it from individual coverage, which makes no sense.
Also improve type checking to make sure that it's a full replacement.

* cover WireGuardServerInstance.is_running property with tests

* enable specialized server instance creation

* test wireguard conf generation

* deduplicate tcp/udp handlers

* update CHANGELOG

Co-authored-by: Maximilian Hils <git@maximilianhils.com>
This commit is contained in:
Fabio Valentini 2022-09-18 17:15:15 +02:00 committed by GitHub
parent 12e2aecdf9
commit 2d495c093c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 450 additions and 79 deletions

View File

@ -37,6 +37,8 @@
([#5590](https://github.com/mitmproxy/mitmproxy/pull/5590), @mhils)
* Add MQTT content view.
([#5588](https://github.com/mitmproxy/mitmproxy/pull/5588), @nikitastupin, @abbbe)
* Add WireGuard mode to enable userspace transparent proxying via WireGuard.
([#5562](https://github.com/mitmproxy/mitmproxy/pull/5562), @decathorpe, @mhils)
## 28 June 2022: mitmproxy 8.1.1

View File

@ -4,7 +4,9 @@ import asyncio
import logging
import socket
from typing import Any, Callable, Optional, Union, cast
from mitmproxy.connection import Address
from mitmproxy.net import udp_wireguard
from mitmproxy.utils import human
logger = logging.getLogger(__name__)
@ -27,7 +29,6 @@ SockAddress = Union[tuple[str, int], tuple[str, int, int, int]]
class DrainableDatagramProtocol(asyncio.DatagramProtocol):
_loop: asyncio.AbstractEventLoop
_closed: asyncio.Event
_paused: int
@ -98,6 +99,7 @@ class UdpServer(DrainableDatagramProtocol):
def connection_made(self, transport: asyncio.BaseTransport) -> None:
if self._transport is None:
self._transport = cast(asyncio.DatagramTransport, transport)
self._transport.set_protocol(self)
self._local_addr = transport.get_extra_info("sockname")
super().connection_made(transport)
@ -112,7 +114,6 @@ class UdpServer(DrainableDatagramProtocol):
class DatagramReader:
_packets: asyncio.Queue
_eof: bool
@ -157,7 +158,6 @@ class DatagramReader:
class DatagramWriter:
_transport: asyncio.DatagramTransport
_remote_addr: Address
_reader: DatagramReader | None
@ -175,14 +175,12 @@ class DatagramWriter:
"""
self._transport = transport
self._remote_addr = remote_addr
proto = transport.get_protocol()
assert isinstance(proto, DrainableDatagramProtocol)
self._reader = reader
self._closed = asyncio.Event() if reader is not None else None
@property
def _protocol(self) -> DrainableDatagramProtocol:
return cast(DrainableDatagramProtocol, self._transport.get_protocol())
def _protocol(self) -> DrainableDatagramProtocol | udp_wireguard.WireGuardDatagramTransport:
return self._transport.get_protocol() # type: ignore
def write(self, data: bytes) -> None:
self._transport.sendto(data, self._remote_addr)

View File

@ -0,0 +1,35 @@
"""
This module contains a mock DatagramTransport for use with mitmproxy-wireguard.
"""
import asyncio
from typing import Any
import mitmproxy_wireguard as wg
from mitmproxy.connection import Address
class WireGuardDatagramTransport(asyncio.DatagramTransport):
def __init__(self, server: wg.Server, local_addr: Address, remote_addr: Address):
self._server: wg.Server = server
self._local_addr: Address = local_addr
self._remote_addr: Address = remote_addr
super().__init__()
def sendto(self, data, addr=None):
self._server.send_datagram(data, self._local_addr, addr or self._remote_addr)
def get_extra_info(self, name: str, default: Any = None) -> Any:
if name == "sockname":
return self._server.getsockname()
else:
raise NotImplementedError
def get_protocol(self):
return self
async def drain(self) -> None:
pass
async def wait_closed(self) -> None:
pass

View File

@ -12,25 +12,29 @@ Example:
from __future__ import annotations
import asyncio
import json
import logging
import errno
import socket
import textwrap
import typing
from abc import ABCMeta, abstractmethod
from contextlib import contextmanager
from pathlib import Path
from typing import ClassVar, Generic, TypeVar, cast, get_args
import errno
import mitmproxy_wireguard as wg
from mitmproxy import ctx, flow, platform
from mitmproxy.connection import Address
from mitmproxy.master import Master
from mitmproxy.net import udp
from mitmproxy.net import local_ip, udp
from mitmproxy.net.udp_wireguard import WireGuardDatagramTransport
from mitmproxy.proxy import commands, layers, mode_specs, server
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layer import Layer
from mitmproxy.utils import human
logger = logging.getLogger(__name__)
@ -62,8 +66,11 @@ class ServerManager(typing.Protocol):
... # pragma: no cover
class ServerInstance(Generic[M], metaclass=ABCMeta):
# Python 3.11: Use typing.Self
Self = TypeVar("Self", bound="ServerInstance")
class ServerInstance(Generic[M], metaclass=ABCMeta):
__modes: ClassVar[dict[str, type[ServerInstance]]] = {}
def __init__(self, mode: M, manager: ServerManager):
@ -80,14 +87,20 @@ class ServerInstance(Generic[M], metaclass=ABCMeta):
assert mode.type not in ServerInstance.__modes
ServerInstance.__modes[mode.type] = cls
@staticmethod
@classmethod
def make(
cls: typing.Type[Self],
mode: mode_specs.ProxyMode | str,
manager: ServerManager,
) -> ServerInstance:
) -> Self:
if isinstance(mode, str):
mode = mode_specs.ProxyMode.parse(mode)
return ServerInstance.__modes[mode.type](mode, manager)
inst = ServerInstance.__modes[mode.type](mode, manager)
if not isinstance(inst, cls):
raise ValueError(f"{mode!r} is not a spec for a {cls.__name__} server.")
return inst
@property
@abstractmethod
@ -107,6 +120,10 @@ class ServerInstance(Generic[M], metaclass=ABCMeta):
def listen_addrs(self) -> tuple[Address, ...]:
pass
@abstractmethod
def make_top_layer(self, context: Context) -> Layer:
pass
def to_json(self) -> dict:
return {
"type": self.mode.type,
@ -117,15 +134,67 @@ class ServerInstance(Generic[M], metaclass=ABCMeta):
"listen_addrs": self.listen_addrs,
}
async def handle_tcp_connection(
self,
reader: asyncio.StreamReader | wg.TcpStream,
writer: asyncio.StreamWriter | wg.TcpStream,
) -> None:
connection_id = (
"tcp",
writer.get_extra_info("peername"),
writer.get_extra_info("sockname"),
)
handler = ProxyConnectionHandler(
ctx.master, reader, writer, ctx.options, self.mode
)
handler.layer = self.make_top_layer(handler.layer.context)
if isinstance(self.mode, mode_specs.TransparentMode):
socket = writer.get_extra_info("socket")
try:
assert platform.original_addr
handler.layer.context.server.address = platform.original_addr(socket)
except Exception as e:
logger.error(f"Transparent mode failure: {e!r}")
return
with self.manager.register_connection(connection_id, handler):
await handler.handle_client()
def handle_udp_datagram(
self,
transport: asyncio.DatagramTransport,
data: bytes,
remote_addr: Address,
local_addr: Address,
) -> None:
connection_id = ("udp", remote_addr, local_addr)
if connection_id not in self.manager.connections:
reader = udp.DatagramReader()
writer = udp.DatagramWriter(transport, remote_addr, reader)
handler = ProxyConnectionHandler(
ctx.master, reader, writer, ctx.options, self.mode
)
handler.timeout_watchdog.CONNECTION_TIMEOUT = 20
handler.layer = self.make_top_layer(handler.layer.context)
handler.layer.context.client.transport_protocol = "udp"
handler.layer.context.server.transport_protocol = "udp"
# pre-register here - we may get datagrams before the task is executed.
self.manager.connections[connection_id] = handler
asyncio.create_task(self.handle_udp_connection(connection_id, handler))
else:
handler = self.manager.connections[connection_id]
reader = cast(udp.DatagramReader, handler.transports[handler.client].reader)
reader.feed_data(data, remote_addr)
async def handle_udp_connection(self, connection_id: tuple, handler: ProxyConnectionHandler) -> None:
with self.manager.register_connection(connection_id, handler):
await handler.handle_client()
class AsyncioServerInstance(ServerInstance[M], metaclass=ABCMeta):
_server: asyncio.Server | udp.UdpServer | None = None
_listen_addrs: tuple[Address, ...] = tuple()
@abstractmethod
def make_top_layer(self, context: Context) -> Layer:
pass
@property
def is_running(self) -> bool:
return self._server is not None
@ -202,61 +271,117 @@ class AsyncioServerInstance(ServerInstance[M], metaclass=ABCMeta):
def listen_addrs(self) -> tuple[Address, ...]:
return self._listen_addrs
async def handle_tcp_connection(
self,
reader: asyncio.StreamReader,
writer: asyncio.StreamWriter,
) -> None:
connection_id = (
"tcp",
writer.get_extra_info("peername"),
writer.get_extra_info("sockname"),
)
handler = ProxyConnectionHandler(
ctx.master, reader, writer, ctx.options, self.mode
)
handler.layer = self.make_top_layer(handler.layer.context)
if isinstance(self.mode, mode_specs.TransparentMode):
socket = writer.get_extra_info("socket")
try:
assert platform.original_addr
handler.layer.context.server.address = platform.original_addr(socket)
except Exception as e:
logger.error(f"Transparent mode failure: {e!r}")
return
with self.manager.register_connection(connection_id, handler):
await handler.handle_client()
def handle_udp_datagram(
self,
transport: asyncio.DatagramTransport,
data: bytes,
remote_addr: Address,
local_addr: Address,
) -> None:
connection_id = ("udp", remote_addr, local_addr)
if connection_id not in self.manager.connections:
reader = udp.DatagramReader()
writer = udp.DatagramWriter(transport, remote_addr, reader)
handler = ProxyConnectionHandler(
ctx.master, reader, writer, ctx.options, self.mode
)
handler.timeout_watchdog.CONNECTION_TIMEOUT = 20
handler.layer = self.make_top_layer(handler.layer.context)
handler.layer.context.client.transport_protocol = "udp"
handler.layer.context.server.transport_protocol = "udp"
class WireGuardServerInstance(ServerInstance[mode_specs.WireGuardMode]):
_server: wg.Server | None = None
_listen_addrs: tuple[Address, ...] = tuple()
# pre-register here - we may get datagrams before the task is executed.
self.manager.connections[connection_id] = handler
asyncio.create_task(self.handle_udp_connection(connection_id, handler))
server_key: str
client_key: str
def make_top_layer(self, context: Context) -> Layer:
return layers.modes.TransparentProxy(context)
@property
def is_running(self) -> bool:
return self._server is not None
async def start(self) -> None:
assert self._server is None
host = self.mode.listen_host(ctx.options.listen_host)
port = self.mode.listen_port(ctx.options.listen_port)
if self.mode.data:
conf_path = Path(self.mode.data).expanduser()
else:
handler = self.manager.connections[connection_id]
reader = cast(udp.DatagramReader, handler.transports[handler.client].reader)
reader.feed_data(data, remote_addr)
conf_path = Path(ctx.options.confdir).expanduser() / "wireguard.conf"
async def handle_udp_connection(self, connection_id: tuple, handler: ProxyConnectionHandler) -> None:
with self.manager.register_connection(connection_id, handler):
await handler.handle_client()
try:
if not conf_path.exists():
conf_path.write_text(json.dumps({
"server_key": wg.genkey(),
"client_key": wg.genkey(),
}, indent=4))
try:
c = json.loads(conf_path.read_text())
self.server_key = c["server_key"]
self.client_key = c["client_key"]
except Exception as e:
raise ValueError(f"Invalid configuration file ({conf_path}): {e}") from e
# error early on invalid keys
p = wg.pubkey(self.client_key)
_ = wg.pubkey(self.server_key)
self._server = await wg.start_server(
host,
port,
self.server_key,
[p],
self.wg_handle_tcp_connection,
self.wg_handle_udp_datagram,
)
self._listen_addrs = (self._server.getsockname(),)
except Exception as e:
self.last_exception = e
message = f"{self.mode.description} failed to listen on {host or '*'}:{port} with {e}"
raise OSError(message) from e
else:
self.last_exception = None
addrs = " and ".join({human.format_address(a) for a in self.listen_addrs})
logger.info(f"{self.mode.description} listening at {addrs}.")
logger.info(self.client_conf())
def client_conf(self) -> str | None:
if not self._server:
return None
host = local_ip.get_local_ip() or local_ip.get_local_ip6()
port = self.mode.listen_port(ctx.options.listen_port)
return textwrap.dedent(f"""
------------------------------------------------------------
[Interface]
PrivateKey = {self.client_key}
Address = 10.0.0.1/32
[Peer]
PublicKey = {wg.pubkey(self.server_key)}
AllowedIPs = 0.0.0.0/0
Endpoint = {host}:{port}
------------------------------------------------------------
""").strip()
def to_json(self) -> dict:
return {
"wireguard_conf": self.client_conf(),
**super().to_json()
}
async def stop(self) -> None:
assert self._server is not None
self._server.close()
await self._server.wait_closed()
self._server = None
self.last_exception = None
addrs = " and ".join({human.format_address(a) for a in self.listen_addrs})
logger.info(f"Stopped {self.mode.description} at {addrs}.")
@property
def listen_addrs(self) -> tuple[Address, ...]:
return self._listen_addrs
async def wg_handle_tcp_connection(self, stream: wg.TcpStream) -> None:
await self.handle_tcp_connection(stream, stream)
def wg_handle_udp_datagram(self, data: bytes, remote_addr: Address, local_addr: Address) -> None:
transport = WireGuardDatagramTransport(self._server, local_addr, remote_addr)
self.handle_udp_datagram(
transport,
data,
remote_addr,
local_addr
)
class RegularInstance(AsyncioServerInstance[mode_specs.RegularMode]):

View File

@ -245,3 +245,13 @@ class DnsMode(ProxyMode):
def __post_init__(self) -> None:
_check_empty(self.data)
class WireGuardMode(ProxyMode):
"""Proxy Server based on WireGuard"""
description = "WireGuard server"
default_port = 51820
transport_protocol = UDP
def __post_init__(self) -> None:
pass

View File

@ -18,7 +18,9 @@ from contextlib import contextmanager
from dataclasses import dataclass
from typing import Optional, Union
import mitmproxy_wireguard as wg
from OpenSSL import SSL
from mitmproxy import http, options as moptions, tls
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layers.http import HTTPMode
@ -78,8 +80,8 @@ class TimeoutWatchdog:
@dataclass
class ConnectionIO:
handler: Optional[asyncio.Task] = None
reader: Optional[Union[asyncio.StreamReader, udp.DatagramReader]] = None
writer: Optional[Union[asyncio.StreamWriter, udp.DatagramWriter]] = None
reader: Optional[Union[asyncio.StreamReader, udp.DatagramReader, wg.TcpStream]] = None
writer: Optional[Union[asyncio.StreamWriter, udp.DatagramWriter, wg.TcpStream]] = None
class ConnectionHandler(metaclass=abc.ABCMeta):
@ -132,6 +134,8 @@ class ConnectionHandler(metaclass=abc.ABCMeta):
self.transports[self.client].handler = handler
self.server_event(events.Start())
await asyncio.wait([handler])
if not handler.cancelled() and (e := handler.exception()):
self.log(f"mitmproxy has crashed!\n{traceback.format_exception(e)}", logging.ERROR)
watch.cancel()
while self.wakeup_timer:
@ -407,8 +411,8 @@ class ConnectionHandler(metaclass=abc.ABCMeta):
class LiveConnectionHandler(ConnectionHandler, metaclass=abc.ABCMeta):
def __init__(
self,
reader: asyncio.StreamReader,
writer: asyncio.StreamWriter,
reader: Union[asyncio.StreamReader, wg.TcpStream],
writer: Union[asyncio.StreamWriter, wg.TcpStream],
options: moptions.Options,
mode: mode_specs.ProxyMode,
) -> None:

View File

@ -69,6 +69,7 @@ exclude =
mitmproxy/net/http/message.py
mitmproxy/net/http/multipart.py
mitmproxy/net/tls.py
mitmproxy/net/udp_wireguard.py
mitmproxy/options.py
mitmproxy/proxy/config.py
mitmproxy/proxy/server.py

View File

@ -82,6 +82,7 @@ setup(
"hyperframe>=6.0,<7",
"kaitaistruct>=0.10,<0.11",
"ldap3>=2.8,<2.10",
"mitmproxy_wireguard>=0.1.6,<0.2",
"msgpack>=1.0.0, <1.1.0",
"passlib>=1.6.5, <1.8",
"protobuf>=3.14,<5",

View File

@ -0,0 +1 @@
# testing this in isolation makes no sense. See proxy/test_mode_servers.py.

View File

@ -1,13 +1,15 @@
import asyncio
import platform
from typing import cast
from unittest.mock import AsyncMock, MagicMock, Mock
import pytest
from mitmproxy import platform
import mitmproxy.platform
from mitmproxy.addons.proxyserver import Proxyserver
from mitmproxy.net import udp
from mitmproxy.proxy.mode_servers import DnsInstance, ServerInstance
from mitmproxy.proxy.mode_servers import DnsInstance, ServerInstance, WireGuardServerInstance
from mitmproxy.proxy.server import ConnectionHandler
from mitmproxy.test import taddons
@ -23,6 +25,9 @@ def test_make():
assert inst.mode.description
assert inst.to_json()
with pytest.raises(ValueError, match="is not a spec for a WireGuardServerInstance server."):
WireGuardServerInstance.make("regular", manager)
async def test_last_exception_and_running(monkeypatch):
manager = MagicMock()
@ -33,7 +38,6 @@ async def test_last_exception_and_running(monkeypatch):
raise err
with taddons.context():
inst1 = ServerInstance.make("regular@127.0.0.1:0", manager)
await inst1.start()
assert inst1.last_exception is None
@ -80,9 +84,9 @@ async def test_transparent(failure, monkeypatch, caplog_async):
manager = MagicMock()
if failure:
monkeypatch.setattr(platform, "original_addr", None)
monkeypatch.setattr(mitmproxy.platform, "original_addr", None)
else:
monkeypatch.setattr(platform, "original_addr", lambda s: ("address", 42))
monkeypatch.setattr(mitmproxy.platform, "original_addr", lambda s: ("address", 42))
with taddons.context(Proxyserver()) as tctx:
tctx.options.connection_strategy = "lazy"
@ -107,6 +111,91 @@ async def test_transparent(failure, monkeypatch, caplog_async):
assert await caplog_async.await_log("Stopped transparent proxy")
async def test_wireguard(tdata, monkeypatch, caplog):
caplog.set_level("DEBUG")
async def handle_client(self: ConnectionHandler):
t = self.transports[self.client]
data = await t.reader.read(65535)
t.writer.write(data.upper())
await t.writer.drain()
t.writer.close()
monkeypatch.setattr(ConnectionHandler, "handle_client", handle_client)
system = platform.system()
if system == "Linux":
test_client_name = "linux-x86_64"
elif system == "Darwin":
test_client_name = "macos-x86_64"
elif system == "Windows":
test_client_name = "windows-x86_64.exe"
else:
return pytest.skip("Unsupported platform for wg-test-client.")
test_client_path = tdata.path(f"wg-test-client/{test_client_name}")
test_conf = tdata.path(f"wg-test-client/test.conf")
with taddons.context(Proxyserver()):
inst = WireGuardServerInstance.make(f"wireguard:{test_conf}@0", MagicMock())
await inst.start()
assert "WireGuard server listening" in caplog.text
_, port = inst.listen_addrs[0]
assert inst.is_running
proc = await asyncio.create_subprocess_exec(
test_client_path,
str(port),
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await proc.communicate()
try:
assert proc.returncode == 0
except AssertionError:
print(stdout)
print(stderr)
raise
await inst.stop()
assert "Stopped WireGuard server" in caplog.text
async def test_wireguard_generate_conf(tmp_path):
with taddons.context(Proxyserver()) as tctx:
tctx.options.confdir = str(tmp_path)
inst = WireGuardServerInstance.make(f"wireguard@0", MagicMock())
assert not inst.client_conf() # should not error.
await inst.start()
assert (tmp_path / "wireguard.conf").exists()
assert inst.client_conf()
assert inst.to_json()["wireguard_conf"]
k = inst.server_key
inst2 = WireGuardServerInstance.make(f"wireguard@0", MagicMock())
await inst2.start()
assert k == inst2.server_key
await inst.stop()
await inst2.stop()
async def test_wireguard_invalid_conf(tmp_path):
with taddons.context(Proxyserver()):
# directory instead of filename
inst = WireGuardServerInstance.make(f"wireguard:{tmp_path}", MagicMock())
with pytest.raises(OSError):
await inst.start()
assert "Invalid configuration file" in repr(inst.last_exception)
async def test_tcp_start_error():
manager = MagicMock()

View File

@ -58,6 +58,9 @@ def test_parse_specific_modes():
assert ProxyMode.parse("dns")
assert ProxyMode.parse("reverse:dns://8.8.8.8")
assert ProxyMode.parse("reverse:dtls://127.0.0.1:8004")
assert ProxyMode.parse("wireguard")
assert ProxyMode.parse("wireguard:foo.conf").data == "foo.conf"
assert ProxyMode.parse("wireguard@51821").listen_port() == 51821
with pytest.raises(ValueError, match="invalid port"):
ProxyMode.parse("regular@invalid-port")

View File

@ -0,0 +1,89 @@
The mitmproxy_wireguard test client is available under the same license (MIT)
as the mitmproxy_wireguard Python package and mitmproxy itself:
--------------------------------------------------------------------------------
Copyright (c) 2022, Fabio Valentini and Maximilian Hils
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--------------------------------------------------------------------------------
The test client also contains code from third-party Rust crates, which are
available under the following licenses:
aead v0.5.1: MIT OR Apache-2.0
anyhow v1.0.65: MIT OR Apache-2.0
base64 v0.13.0: MIT/Apache-2.0
bitflags v1.3.2: MIT/Apache-2.0
blake2 v0.10.4: MIT OR Apache-2.0
block-buffer v0.10.3: MIT OR Apache-2.0
boringtun v0.5.2: BSD-3-Clause
byteorder v1.4.3: Unlicense OR MIT
cfg-if v1.0.0: MIT/Apache-2.0
chacha20poly1305 v0.10.1: Apache-2.0 OR MIT
chacha20 v0.9.0: Apache-2.0 OR MIT
cipher v0.4.3: MIT OR Apache-2.0
cpufeatures v0.2.5: MIT OR Apache-2.0
crypto-common v0.1.6: MIT OR Apache-2.0
curve25519-dalek v3.2.0: BSD-3-Clause
digest v0.10.5: MIT OR Apache-2.0
digest v0.9.0: MIT OR Apache-2.0
generic-array v0.14.6: MIT
getrandom v0.1.16: MIT OR Apache-2.0
getrandom v0.2.7: MIT OR Apache-2.0
hex v0.4.3: MIT OR Apache-2.0
hmac v0.12.1: MIT OR Apache-2.0
inout v0.1.3: MIT OR Apache-2.0
ip_network_table-deps-treebitmap v0.5.0: MIT
ip_network_table v0.2.0: BSD-2-Clause
ip_network v0.4.1: BSD-2-Clause
libc v0.2.132: MIT OR Apache-2.0
lock_api v0.4.8: MIT OR Apache-2.0
log v0.4.17: MIT OR Apache-2.0
managed v0.8.0: 0BSD
once_cell v1.14.0: MIT OR Apache-2.0
opaque-debug v0.3.0: MIT OR Apache-2.0
parking_lot_core v0.9.3: MIT OR Apache-2.0
parking_lot v0.12.1: MIT OR Apache-2.0
pin-project-lite v0.2.9: Apache-2.0 OR MIT
poly1305 v0.8.0: Apache-2.0 OR MIT
rand_core v0.5.1: MIT OR Apache-2.0
rand_core v0.6.4: MIT OR Apache-2.0
ring v0.16.20:
scopeguard v1.1.0: MIT/Apache-2.0
smallvec v1.9.0: MIT OR Apache-2.0
smoltcp v0.8.1: 0BSD
spin v0.5.2: MIT
subtle v2.4.1: BSD-3-Clause
tracing-core v0.1.29: MIT
tracing v0.1.36: MIT
typenum v1.15.0: MIT OR Apache-2.0
universal-hash v0.5.0: MIT OR Apache-2.0
untrusted v0.7.1: ISC
untrusted v0.9.0: ISC
x25519-dalek v2.0.0-pre.1: BSD-3-Clause
zeroize v1.5.7: Apache-2.0 OR MIT
--------------------------------------------------------------------------------
This list of third-party crates and their licenses was collected for v0.1.6 of
the test client by running this command:
$ cargo tree --prefix none --edges no-build,no-dev,no-proc-macro --format "{p}: {l}" --no-dedupe | sort -u

View File

@ -0,0 +1,9 @@
# mitm-wg-test-client
This directory contains simple test client binaries built from
<https://github.com/decathorpe/mitmproxy_wireguard> version v0.1.6. New versions
of the test client binaries are published as release assets on GitHub.
The test binaries are used for sending WireGuard traffic from userspace in
`tests/mitmproxy/proxy/test_mode_servers.py:test_wireguard`.

BIN
test/wg-test-client/linux-x86_64 Executable file

Binary file not shown.

BIN
test/wg-test-client/macos-aarch64 Executable file

Binary file not shown.

BIN
test/wg-test-client/macos-x86_64 Executable file

Binary file not shown.

View File

@ -0,0 +1,4 @@
{
"server_key": "EG47ZWjYjr+Y97TQ1A7sVl7Xn3mMWDnvjU/VxU769ls=",
"client_key": "qG8b7LI/s+ezngWpXqj5A7Nj988hbGL+eQ8ePki0iHk="
}

Binary file not shown.