mirror of
https://github.com/mitmproxy/mitmproxy.git
synced 2024-11-27 07:10:51 +00:00
Add domain fronting example (#5217)
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
This commit is contained in:
parent
1bc265ace7
commit
66dd158560
@ -10,6 +10,8 @@
|
||||
([#5109](https://github.com/mitmproxy/mitmproxy/issues/5109), @mhils)
|
||||
* Make sure that mitmproxy displays error messages on startup.
|
||||
([#5225](https://github.com/mitmproxy/mitmproxy/issues/5225), @mhils)
|
||||
* Add example addon for domain fronting.
|
||||
([#5217](https://github.com/mitmproxy/mitmproxy/issues/5217), @randomstuff)
|
||||
|
||||
## 19 March 2022: mitmproxy 8.0.0
|
||||
|
||||
|
130
examples/contrib/domain_fronting.py
Normal file
130
examples/contrib/domain_fronting.py
Normal file
@ -0,0 +1,130 @@
|
||||
from typing import Set, Union, Dict, Optional
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from mitmproxy import ctx
|
||||
from mitmproxy.addonmanager import Loader
|
||||
from mitmproxy.http import HTTPFlow
|
||||
|
||||
|
||||
"""
|
||||
This extension implements support for domain fronting.
|
||||
|
||||
Usage:
|
||||
|
||||
mitmproxy -s examples/contrib/domain_fronting.py --set domainfrontingfile=./domain_fronting.json
|
||||
|
||||
In the following basic example, www.example.com will be used for DNS requests and SNI values
|
||||
but the secret.example.com value will be used for the HTTP host header:
|
||||
|
||||
{
|
||||
"mappings": [
|
||||
{
|
||||
"patterns": ["secret.example.com"],
|
||||
"server": "www.example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
The following example demonstrates the usage of a wildcard (at the beginning of the domain name only):
|
||||
|
||||
{
|
||||
"mappings": [
|
||||
{
|
||||
"patterns": ["*.foo.example.com"],
|
||||
"server": "www.example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
In the following example, we override the HTTP host header:
|
||||
|
||||
{
|
||||
"mappings": [
|
||||
{
|
||||
"patterns": ["foo.example"],
|
||||
"server": "www.example.com",
|
||||
"host": "foo.proxy.example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class Mapping:
|
||||
server: Union[str, None]
|
||||
host: Union[str, None]
|
||||
|
||||
|
||||
class HttpsDomainFronting:
|
||||
|
||||
# configurations for regular ("foo.example.com") mappings:
|
||||
star_mappings: Dict[str, Mapping]
|
||||
|
||||
# Configurations for star ("*.example.com") mappings:
|
||||
strict_mappings: Dict[str, Mapping]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.strict_mappings = {}
|
||||
self.star_mappings = {}
|
||||
|
||||
def _resolve_addresses(self, host: str) -> Optional[Mapping]:
|
||||
mapping = self.strict_mappings.get(host)
|
||||
if mapping is not None:
|
||||
return mapping
|
||||
|
||||
index = 0
|
||||
while True:
|
||||
index = host.find(".", index)
|
||||
if index == -1:
|
||||
break
|
||||
super_domain = host[(index + 1):]
|
||||
mapping = self.star_mappings.get(super_domain)
|
||||
if mapping is not None:
|
||||
return mapping
|
||||
index += 1
|
||||
|
||||
return None
|
||||
|
||||
def load(self, loader: Loader) -> None:
|
||||
loader.add_option(
|
||||
name="domainfrontingfile",
|
||||
typespec=str,
|
||||
default="./fronting.json",
|
||||
help="Domain fronting configuration file",
|
||||
)
|
||||
|
||||
def _load_configuration_file(self, filename: str) -> None:
|
||||
config = json.load(open(filename, "rt"))
|
||||
strict_mappings: Dict[str, Mapping] = {}
|
||||
star_mappings: Dict[str, Mapping] = {}
|
||||
for mapping in config["mappings"]:
|
||||
item = Mapping(server=mapping.get("server"), host=mapping.get("host"))
|
||||
for pattern in mapping["patterns"]:
|
||||
if pattern.startswith("*."):
|
||||
star_mappings[pattern[2:]] = item
|
||||
else:
|
||||
strict_mappings[pattern] = item
|
||||
self.strict_mappings = strict_mappings
|
||||
self.star_mappings = star_mappings
|
||||
|
||||
def configure(self, updated: Set[str]) -> None:
|
||||
if "domainfrontingfile" in updated:
|
||||
domain_fronting_file = ctx.options.domainfrontingfile
|
||||
self._load_configuration_file(domain_fronting_file)
|
||||
|
||||
def request(self, flow: HTTPFlow) -> None:
|
||||
if not flow.request.scheme == "https":
|
||||
return
|
||||
# We use the host header to dispatch the request:
|
||||
target = flow.request.host_header
|
||||
if target is None:
|
||||
return
|
||||
mapping = self._resolve_addresses(target)
|
||||
if mapping is not None:
|
||||
flow.request.host = mapping.server or target
|
||||
flow.request.headers["host"] = mapping.host or target
|
||||
|
||||
|
||||
addons = [HttpsDomainFronting()]
|
Loading…
Reference in New Issue
Block a user