之前在 mitmproxy 上支持了 http2 trailer,pr 已经合并到 5.2 版本,可以正常代理 grpc 请求了,介绍下如何使用。
grpc 原始信息不可读,可以写个自定义视图显示反序列化后的内容。
先用 grpc 官方示例启动一个 grpc 服务:
python greeter_server.py
客户端就用 bloomrpc,不另外写代码了。导入对应的 proto 文件,向 localhost:50051 发送请求验证服务有没有正常工作:
因为 mitmproxy 目前只支持 TLS 上的 HTTP/2,还需要再用 nginx 转发并加上 TLS。
参考这篇文章生成自签名证书:
openssl genrsa -des3 -out rootCA.key 4096
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt
openssl genrsa -out localhost.key 2048
openssl req -new -key localhost.key -out localhost.csr # Common Name 输入域名 localhost,其他随便填一下
openssl req -in localhost.csr -noout -text
openssl x509 -req -in localhost.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out localhost.crt -days 500 -sha256
openssl x509 -in localhost.crt -text -noout
cat localhost.crt localhost.key > localhost.pem
配置 nginx:
server {
listen 6556 ssl http2;
server_name localhost;
ssl_certificate ~/localhost.pem;
ssl_certificate_key ~/localhost.pem;
location / {
grpc_pass grpc://localhost:50051;
}
}
配置好启动 nginx,在 bloomrpc 里导入证书:
向 localhost:6556 发送请求验证 nginx 有没有正常工作:
启动 mitmproxy,因为是自签名证书,多加两个参数:
mitmweb --certs localhost=~/localhost.pem -k
MacOS 上,通过下面的命令重新启动 bloomrpc,可以让 bloomrpc 发出的请求经过代理:
export http_proxy=http://127.0.0.1:8080;export https_proxy=http://127.0.0.1:8080;
open /Applications/BloomRPC.app
再次向 localhost:6556 发送请求,验证 mitmweb 有没有正常工作:
然后写自定义视图,先安装 mitmproxy 依赖:
pip install mitmproxy
新建 grpc_addon.py
内容如下:
from mitmproxy import contentviews, http
import helloworld_pb2
class GrpcView(contentviews.View):
name = 'grpc'
def __call__(self, data, **metadata) -> contentviews.TViewResult:
# 参考 https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md,grpc 请求 body 的第一个字节代表有没有被压缩,示例中没有压缩,所以不需要额外处理。二到五字节是 protobuf 内容的长度。
compressed_flag = data[0]
message_length = int.from_bytes(data[1:5], 'big')
message = data[5:message_length + 5]
# 之后就是反序列化 protobuf
if isinstance(metadata['message'], http.HTTPRequest):
message_method = helloworld_pb2.HelloRequest
elif isinstance(metadata['message'], http.HTTPResponse):
message_method = helloworld_pb2.HelloReply
target = message_method()
target.ParseFromString(message)
return 'deserialized grpc', contentviews.format_text(str(target))
grpc_view = GrpcView()
def load(l):
contentviews.add(grpc_view)
def done():
contentviews.remove(grpc_view)
重新启动 mitmproxy,加载 grpc_addon.py
:
mitmweb --certs localhost=~/localhost.pem -k -s grpc_addon.py
再次向 localhost:6556 发送请求,可以查看反序列化后的内容了:
也可以改写响应内容,在 grpc_addon.py
加上如下内容后再发送请求看看:
class Rewrite:
def response(self, flow):
data = flow.response.raw_content
compressed_flag = data[0]
message_length = int.from_bytes(data[1:5], 'big')
message = data[5:message_length + 5]
target = helloworld_pb2.HelloReply()
target.ParseFromString(message)
target.message = 'Hello, rewrited'
modified_message = target.SerializeToString()
# 改写后内容长度可能会变,所以要重新计算长度
modified_response = data[0].to_bytes(1, byteorder='big') + len(modified_message).to_bytes(4, byteorder='big') + modified_message
flow.response.headers['content-length'] = str(len(modified_response))
flow.response.raw_content = modified_response
addons = [
Rewrite()
]