Skip to content

Commit

Permalink
Fix for --allow-hosts/--ignore-hosts options in WireGuard mode (mitmp…
Browse files Browse the repository at this point in the history
  • Loading branch information
dsphper committed Dec 6, 2023
1 parent 81fc802 commit 558b0b6
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 12 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,12 @@

## Unreleased: mitmproxy next

* Improved handling for `--allow-hosts`/`--ignore-hosts` options in WireGuard mode (#5930).
([#6513](https://github.com/mitmproxy/mitmproxy/pull/6513), @dsphper)
* DNS resolution is now exempted from `--ignore-hosts` in WireGuard Mode.
([#6513](https://github.com/mitmproxy/mitmproxy/pull/6513), @dsphper)
* For plaintext traffic, `--ignore-hosts` now also takes HTTP/1 host headers into account.
([#6513](https://github.com/mitmproxy/mitmproxy/pull/6513), @dsphper)
* Fix empty cookie attributes being set to `Key=` instead of `Key`
([#5084](https://github.com/mitmproxy/mitmproxy/pull/5084), @Speedlulu)
* Scripts with relative paths are now loaded relative to the config file and not where the command is ran
Expand Down
16 changes: 14 additions & 2 deletions mitmproxy/addons/next_layer.py
Expand Up @@ -210,7 +210,11 @@ def _ignore_connection(
"""
if not ctx.options.ignore_hosts and not ctx.options.allow_hosts:
return False

# Special handling for wireguard mode: if the hostname is "10.0.0.53", do not ignore the connection
if isinstance(
context.client.proxy_mode, mode_specs.WireGuardMode
) and context.server.address == ("10.0.0.53", 53):
return False
hostnames: list[str] = []
if context.server.peername and (peername := context.server.peername[0]):
hostnames.append(peername)
Expand All @@ -220,7 +224,9 @@ def _ignore_connection(
client_hello := self._get_client_hello(context, data_client)
) and client_hello.sni:
hostnames.append(client_hello.sni)

# If the client data is not a TLS record, try to extract the domain from the HTTP request
elif host := self._extract_http1_host_header(data_client):
hostnames.append(host)
if not hostnames:
return False

Expand All @@ -239,6 +245,12 @@ def _ignore_connection(
else: # pragma: no cover
raise AssertionError()

@staticmethod
def _extract_http1_host_header(data_client: bytes) -> str:
pattern = rb"Host:\s+(.+?)\r\n"
match = re.search(pattern, data_client)
return match.group(1).decode() if match else ""

def _get_client_hello(
self, context: Context, data_client: bytes
) -> ClientHello | None:
Expand Down
86 changes: 76 additions & 10 deletions test/mitmproxy/addons/test_next_layer.py
Expand Up @@ -90,6 +90,8 @@

dns_query = bytes.fromhex("002a01000001000000000000076578616d706c6503636f6d0000010001")

http_query = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"


class TestNextLayer:
def test_configure(self):
Expand All @@ -101,19 +103,40 @@ def test_configure(self):
)

@pytest.mark.parametrize(
"ignore, allow, transport_protocol, server_address, data_client, result",
"mode, ignore, allow, transport_protocol, server_address, data_client, result",
[
pytest.param(
[],
[],
["example.com"],
"tcp",
"example.org",
http_query,
False,
id="extract host from http request",
),
pytest.param(
["wireguard"],
["example.com"],
[],
"udp",
"10.0.0.53",
dns_query,
False,
id="special handling for wireguard mode",
),
# ignore
pytest.param(
[], [], "example.com", "tcp", b"", False, id="nothing ignored"
[], [], [], "example.com", "tcp", b"", False, id="nothing ignored"
),
pytest.param(
["example.com"], [], "tcp", "example.com", b"", True, id="address"
[], ["example.com"], [], "tcp", "example.com", b"", True, id="address"
),
pytest.param(
["1.2.3.4"], [], "tcp", "example.com", b"", True, id="ip address"
[], ["1.2.3.4"], [], "tcp", "example.com", b"", True, id="ip address"
),
pytest.param(
[],
["example.com"],
[],
"tcp",
Expand All @@ -123,9 +146,17 @@ def test_configure(self):
id="partial address match",
),
pytest.param(
["example.com"], [], "tcp", None, b"", False, id="no destination info"
[],
["example.com"],
[],
"tcp",
None,
b"",
False,
id="no destination info",
),
pytest.param(
[],
["example.com"],
[],
"tcp",
Expand All @@ -135,6 +166,7 @@ def test_configure(self):
id="no sni",
),
pytest.param(
[],
["example.com"],
[],
"tcp",
Expand All @@ -144,6 +176,7 @@ def test_configure(self):
id="sni",
),
pytest.param(
[],
["example.com"],
[],
"tcp",
Expand All @@ -153,6 +186,7 @@ def test_configure(self):
id="incomplete client hello",
),
pytest.param(
[],
["example.com"],
[],
"tcp",
Expand All @@ -162,6 +196,7 @@ def test_configure(self):
id="invalid client hello",
),
pytest.param(
[],
["example.com"],
[],
"tcp",
Expand All @@ -171,6 +206,7 @@ def test_configure(self):
id="sni mismatch",
),
pytest.param(
[],
["example.com"],
[],
"udp",
Expand All @@ -180,6 +216,7 @@ def test_configure(self):
id="dtls sni",
),
pytest.param(
[],
["example.com"],
[],
"udp",
Expand All @@ -189,6 +226,7 @@ def test_configure(self):
id="incomplete dtls client hello",
),
pytest.param(
[],
["example.com"],
[],
"udp",
Expand All @@ -198,16 +236,38 @@ def test_configure(self):
id="invalid dtls client hello",
),
pytest.param(
["example.com"], [], "udp", None, quic_client_hello, True, id="quic sni"
[],
["example.com"],
[],
"udp",
None,
quic_client_hello,
True,
id="quic sni",
),
# allow
pytest.param(
[], ["example.com"], "tcp", "example.com", b"", False, id="allow: allow"
[],
[],
["example.com"],
"tcp",
"example.com",
b"",
False,
id="allow: allow",
),
pytest.param(
[], ["example.com"], "tcp", "example.org", b"", True, id="allow: ignore"
[],
[],
["example.com"],
"tcp",
"example.org",
b"",
True,
id="allow: ignore",
),
pytest.param(
[],
[],
["example.com"],
"tcp",
Expand All @@ -220,6 +280,7 @@ def test_configure(self):
)
def test_ignore_connection(
self,
mode: list[str],
ignore: list[str],
allow: list[str],
transport_protocol: TransportProtocol,
Expand All @@ -233,7 +294,8 @@ def test_ignore_connection(
tctx.configure(nl, ignore_hosts=ignore)
if allow:
tctx.configure(nl, allow_hosts=allow)

if mode:
tctx.options.mode = mode
ctx = Context(
Client(peername=("192.168.0.42", 51234), sockname=("0.0.0.0", 8080)),
tctx.options,
Expand All @@ -242,7 +304,10 @@ def test_ignore_connection(
if server_address:
ctx.server.address = (server_address, 443)
ctx.server.peername = ("1.2.3.4", 443)

if "wireguard" in tctx.options.mode:
ctx.server.peername = ("10.0.0.53", 53)
ctx.server.address = ("10.0.0.53", 53)
ctx.client.proxy_mode = ProxyMode.parse("wireguard")
if result is NeedsMoreData:
with pytest.raises(NeedsMoreData):
nl._ignore_connection(ctx, data_client)
Expand All @@ -268,6 +333,7 @@ def test_next_layer(self, monkeypatch, caplog):
assert m.layer is preexisting

m.layer = None
monkeypatch.setattr(m, "data_client", lambda: http_query)
nl.next_layer(m)
assert m.layer

Expand Down

0 comments on commit 558b0b6

Please sign in to comment.