From c39255cc5790142ee204f7646c4deca26d71f94a Mon Sep 17 00:00:00 2001 From: monlor Date: Tue, 6 Aug 2024 15:29:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20all=20events=20are=20sup?= =?UTF-8?q?ported?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mitmproxy/Dockerfile | 10 ++-- mitmproxy/README.md | 4 +- mitmproxy/entrypoint.sh | 18 +++++++ mitmproxy/mitmproxy.py | 102 +++++++++++++++++++++++++++++----------- mitmproxy/nginx.conf | 28 +++++++++++ mitmproxy/test.py | 102 +++++++++++++++++++++++++++++++++++++++- 6 files changed, 230 insertions(+), 34 deletions(-) create mode 100644 mitmproxy/entrypoint.sh create mode 100644 mitmproxy/nginx.conf diff --git a/mitmproxy/Dockerfile b/mitmproxy/Dockerfile index 337aee2..6b3c6f6 100644 --- a/mitmproxy/Dockerfile +++ b/mitmproxy/Dockerfile @@ -3,11 +3,15 @@ FROM mitmproxy/mitmproxy LABEL MAINTAINER me@monlor.com LABEL VERSION 1.0.0 -RUN apt-get update && apt-get install -y curl && \ +RUN apt-get update && apt-get install -y curl nginx && \ pip install requests COPY mitmproxy.py /mitmproxy.py -EXPOSE 8080 8081 +COPY nginx.conf /etc/nginx/nginx.conf -ENTRYPOINT ["mitmweb", "--web-host", "0.0.0.0", "--web-port", "8081", "--listen-port", "8080", "-s", "/mitmproxy.py"] \ No newline at end of file +COPY --chmod=755 entrypoint.sh / + +EXPOSE 80 8080 8081 + +ENTRYPOINT [ "/entrypoint.sh" ] \ No newline at end of file diff --git a/mitmproxy/README.md b/mitmproxy/README.md index f66656e..dc6e413 100644 --- a/mitmproxy/README.md +++ b/mitmproxy/README.md @@ -15,7 +15,9 @@ export SCRIPT_UPDATE_INTERVAL=300 # 可选,默认为300秒 ## Port -8080,8081 +ui: 80,8080 + +proxy: 8081 ## Rmote script diff --git a/mitmproxy/entrypoint.sh b/mitmproxy/entrypoint.sh new file mode 100644 index 0000000..1ba2b87 --- /dev/null +++ b/mitmproxy/entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# 设置环境变量 +export MITMPROXY_USER=${MITMPROXY_USER:-""} +export MITMPROXY_PASS=${MITMPROXY_PASS:-""} + +# 构建 mitmweb 命令 +MITMWEB_CMD="mitmweb --web-host 0.0.0.0 --web-port 8081 --listen-port 8080 -s /mitmproxy.py $@" + +# 如果设置了用户名和密码,添加代理认证 +if [ -n "$MITMPROXY_USER" ] && [ -n "$MITMPROXY_PASS" ]; then + MITMWEB_CMD="$MITMWEB_CMD --proxyauth $MITMPROXY_USER:$MITMPROXY_PASS" +fi + +nginx -g 'daemon off;' & + +# 执行 mitmweb 命令 +exec $MITMWEB_CMD \ No newline at end of file diff --git a/mitmproxy/mitmproxy.py b/mitmproxy/mitmproxy.py index 106e7ff..cda928a 100644 --- a/mitmproxy/mitmproxy.py +++ b/mitmproxy/mitmproxy.py @@ -3,63 +3,109 @@ import os import time import threading -import base64 from mitmproxy import ctx class CustomMitmProxy: def __init__(self): - self.username = os.environ.get('MITMPROXY_USER', '') - self.password = os.environ.get('MITMPROXY_PASS', '') - self.auth_enabled = bool(self.username) self.remote_script_url = os.environ.get('REMOTE_SCRIPT_URL', '') self.remote_script = None self.update_interval = int(os.environ.get('SCRIPT_UPDATE_INTERVAL', 300)) # 默认5分钟 self.last_update_time = 0 + self.timeout = int(os.environ.get('SCRIPT_REQUEST_TIMEOUT', 10)) # 默认10秒 if self.remote_script_url: self.load_remote_script() # 启动时立即更新远程脚本 self.start_update_thread() def request(self, flow: http.HTTPFlow) -> None: - if self.auth_enabled: - if not self.authenticate(flow): - flow.response = http.Response.make( - 407, b"Authentication required", {"Proxy-Authenticate": "Basic"} - ) - return + self.handle_event(flow, 'request') - if self.remote_script_url and self.remote_script: - self.execute_remote_script(flow) + def response(self, flow: http.HTTPFlow) -> None: + self.handle_event(flow, 'response') - def authenticate(self, flow: http.HTTPFlow) -> bool: - auth_header = flow.request.headers.get("Proxy-Authorization") - if auth_header: - try: - scheme, user_pass = auth_header.split() - username, password = base64.b64decode(user_pass.encode()).decode().split(":") - if username == self.username and password == self.password: - return True - except Exception as e: - ctx.log.error(f"Authentication error: {e}") - return False + def client_connected(self, client): + self.handle_event(client, 'client_connected') + + def client_disconnected(self, client): + self.handle_event(client, 'client_disconnected') + + def server_connect(self, data): + self.handle_event(data, 'server_connect') + + def server_connected(self, data): + self.handle_event(data, 'server_connected') + + def server_disconnected(self, data): + self.handle_event(data, 'server_disconnected') + + def tcp_start(self, flow): + self.handle_event(flow, 'tcp_start') + + def tcp_message(self, flow): + self.handle_event(flow, 'tcp_message') + + def tcp_error(self, flow): + self.handle_event(flow, 'tcp_error') + + def tcp_end(self, flow): + self.handle_event(flow, 'tcp_end') + + def http_connect(self, flow): + self.handle_event(flow, 'http_connect') + + def websocket_handshake(self, flow): + self.handle_event(flow, 'websocket_handshake') + + def websocket_start(self, flow): + self.handle_event(flow, 'websocket_start') + + def websocket_message(self, flow): + self.handle_event(flow, 'websocket_message') + + def websocket_error(self, flow): + self.handle_event(flow, 'websocket_error') + + def websocket_end(self, flow): + self.handle_event(flow, 'websocket_end') + + def next_layer(self, layer): + self.handle_event(layer, 'next_layer') + + def configure(self, updated): + self.handle_event(updated, 'configure') + + def done(self): + self.handle_event(None, 'done') + + def load(self, loader): + self.handle_event(loader, 'load') + + def running(self): + self.handle_event(None, 'running') + + def handle_event(self, flow, event_type): + if self.remote_script_url and self.remote_script: + self.execute_remote_script(flow, event_type) def load_remote_script(self): if self.remote_script_url: try: - response = requests.get(self.remote_script_url) + response = requests.get(self.remote_script_url, timeout=self.timeout) if response.status_code == 200: self.remote_script = response.text ctx.log.info("Remote script updated successfully.") else: ctx.log.error(f"Failed to load remote script. Status code: {response.status_code}") - except Exception as e: + except requests.exceptions.Timeout: + ctx.log.error(f"Request to {self.remote_script_url} timed out.") + except requests.exceptions.RequestException as e: ctx.log.error(f"Failed to load remote script: {e}") - def execute_remote_script(self, flow): + def execute_remote_script(self, flow, event_type): try: - exec(self.remote_script, {'flow': flow}) + exec(self.remote_script, {'flow': flow, 'event_type': event_type}) except Exception as e: - ctx.log.error(f"Error executing remote script: {e}") + ctx.log.error(f"Error executing remote script for {event_type}: {e}") def update_script_periodically(self): while True: diff --git a/mitmproxy/nginx.conf b/mitmproxy/nginx.conf new file mode 100644 index 0000000..15bd697 --- /dev/null +++ b/mitmproxy/nginx.conf @@ -0,0 +1,28 @@ +events { + worker_connections 1024; +} + +http { + server { + listen 80; + server_name localhost; + + location / { + proxy_pass http://localhost:8081; + proxy_set_header Host localhost:8081; + proxy_set_header Origin http://localhost:8081; + + expires off; + proxy_http_version 1.1; + proxy_redirect http://$http_host:8081 http://$http_host; + proxy_buffering off; + + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Cookie $http_cookie; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + } +} \ No newline at end of file diff --git a/mitmproxy/test.py b/mitmproxy/test.py index 79dfed5..fbbe8b0 100644 --- a/mitmproxy/test.py +++ b/mitmproxy/test.py @@ -1,2 +1,100 @@ -def request(flow): - flow.request.headers["Custom-Header"] = "Custom-Value" \ No newline at end of file +def handle_event(flow, event_type): + print(f"Event: {event_type}") + + if event_type == 'request': + # 处理请求 + # 可以修改请求头、URL、方法等 + flow.request.headers["X-Custom-Request-Header"] = "CustomRequestValue" + print(f"Request URL: {flow.request.url}") + + elif event_type == 'response': + # 处理响应 + # 可以修改响应头、内容、状态码等 + flow.response.headers["X-Custom-Response-Header"] = "CustomResponseValue" + print(f"Response status code: {flow.response.status_code}") + + elif event_type == 'client_connected': + # 客户端连接时触发 + print(f"Client connected: {flow}") + + elif event_type == 'client_disconnected': + # 客户端断开连接时触发 + print(f"Client disconnected: {flow}") + + elif event_type == 'server_connect': + # 服务器连接开始时触发 + print(f"Server connecting: {flow}") + + elif event_type == 'server_connected': + # 服务器连接建立时触发 + print(f"Server connected: {flow}") + + elif event_type == 'server_disconnected': + # 服务器断开连接时触发 + print(f"Server disconnected: {flow}") + + elif event_type == 'tcp_start': + # TCP连接开始时触发 + print(f"TCP connection started: {flow}") + + elif event_type == 'tcp_message': + # 收到TCP消息时触发 + print(f"TCP message: {flow}") + + elif event_type == 'tcp_error': + # TCP连接出错时触发 + print(f"TCP error: {flow}") + + elif event_type == 'tcp_end': + # TCP连接结束时触发 + print(f"TCP connection ended: {flow}") + + elif event_type == 'http_connect': + # 处理HTTP CONNECT请求 + print(f"HTTP CONNECT: {flow}") + + elif event_type == 'websocket_handshake': + # WebSocket握手完成时触发 + print(f"WebSocket handshake: {flow}") + + elif event_type == 'websocket_start': + # WebSocket连接开始时触发 + print(f"WebSocket started: {flow}") + + elif event_type == 'websocket_message': + # 收到WebSocket消息时触发 + print(f"WebSocket message: {flow.websocket.messages[-1].content}") + + elif event_type == 'websocket_error': + # WebSocket连接出错时触发 + print(f"WebSocket error: {flow}") + + elif event_type == 'websocket_end': + # WebSocket连接结束时触发 + print(f"WebSocket ended: {flow}") + + elif event_type == 'next_layer': + # 用于协议嗅探和动态协议切换 + print(f"Next layer: {flow}") + + elif event_type == 'configure': + # 配置发生变化时触发 + print(f"Configuration changed: {flow}") + + elif event_type == 'done': + # addon关闭时触发 + print("Addon is shutting down") + + elif event_type == 'load': + # addon首次加载时触发 + print(f"Addon loaded: {flow}") + + elif event_type == 'running': + # 代理完全启动并运行时触发 + print("Proxy is running") + + # 可以根据需要添加更多的事件处理逻辑 + # 例如,可以在这里添加日志记录、数据分析或其他自定义操作 + +# 主执行点 +handle_event(flow, event_type) \ No newline at end of file