AnyProxy

本文档的适用范围是AnyProxy 4.0,此版本当前正在beta中,欢迎提供反馈

Ref: English Doc

AnyProxy是一个开放式的HTTP代理服务器。

Github主页:https://github.com/alibaba/anyproxy/tree/4.x

主要特性包括:

相比3.x版本,AnyProxy 4.0的主要变化:

快速上手

安装

对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 nodejs-legacy

sudo apg-get install nodejs-legacy

然后,安装AnyProxy

npm install -g anyproxy@beta #本文档对应的AnyProxy为4.0Beta版

启动

anyproxy

其他命令

anyproxy --port 1080

代理https请求

解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程

anyproxy-ca #生成rootCA证书,生成后需要手动信任
anyproxy --intercept #启动AnyProxy,并解析所有https请求

规则模块(Rule)

AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。

注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题

规则模块的能力范围包括:

开发示例

处理流程

如何引用

如下几种方案都可以用来引用规则模块:

规则接口文档

规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。

module.exports = {
  // 模块介绍
  summary: 'my customized rule for AnyProxy',
  // 发送请求前拦截处理
  *beforeSendRequest(requestDetail) { /* ... */ },
  // 发送响应前处理
  *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },
  // 是否处理https请求
  *beforeDealHttpsRequest(requestDetail) { /* ... */ },
  // 请求出错的事件
  *onError(requestDetail, error) { /* ... */ },
  // https连接服务器出错
  *onConnectError(requestDetail, error) { /* ... */ }
};

规则文件中,除了summary,都是由 co 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。

summary

summary

beforeSendRequest

beforeSendRequest(requestDetail)

beforeSendResponse

beforeSendResponse(requestDetail, responseDetail)

beforeDealHttpsRequest

beforeDealHttpsRequest(requestDetail)

onError

onError(requestDetail, error)

onConnectError

onConnectError(requestDetail, error)

FAQ

规则模块样例

使用本地数据

anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_use_local_response.js
/* 
  sample: 
    intercept all requests toward httpbin.org, use a local response
  test:
    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
*/
module.exports = {
  *beforeSendRequest(requestDetail) {
    const localResponse = {
      statusCode: 200,
      header: { 'Content-Type': 'application/json' },
      body: '{"hello": "this is local response"}'
    };
    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      return {
        response: localResponse
      };
    }
  },
};

修改请求头

anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_header.js
/* 
  sample: 
    modify the user-agent in requests toward httpbin.org
  test:
    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
*/
module.exports = {
  *beforeSendRequest(requestDetail) {
    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      const newRequestOptions = requestDetail.requestOptions;
      newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';
      return {
        requestOptions: newRequestOptions
      };
    }
  },
};

修改请求数据

anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_data.js
/*
  sample:
    modify the post data towards http://httpbin.org/post
  test:
    curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001
  expected response:
    { "data": "i-am-anyproxy-modified-post-data" }
*/
module.exports = {
  summary: 'Rule to modify request data',
  *beforeSendRequest(requestDetail) {
    if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {
      return {
        requestData: 'i-am-anyproxy-modified-post-data'
      };
    }
  },
};

修改请求的目标地址

anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_path.js
/* 
  sample: 
    redirect all httpbin.org requests to http://httpbin.org/user-agent
  test:
    curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001
  expected response:
    { "user-agent": "curl/7.43.0" }
*/
module.exports = {
  *beforeSendRequest(requestDetail) {
    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      const newRequestOptions = requestDetail.requestOptions;
      newRequestOptions.path = '/user-agent';
      newRequestOptions.method = 'GET';
      return {
        requestOptions: newRequestOptions
      };
    }
  },
};

修改请求协议

anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_protocol.js
/* 
  sample: 
    redirect all http requests of httpbin.org to https
  test:
    curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001
  expected response:
    { "X-Forwarded-Protocol": "https" }
*/
module.exports = {
  *beforeSendRequest(requestDetail) {
    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      const newOption = requestDetail.requestOptions;
      newOption.port = 443;
      return {
        protocol: 'https',
        requestOptions: newOption
      };
    }
  }
};

修改返回状态码

anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_statuscode.js
/* 
  sample: 
    modify all status code of http://httpbin.org/ to 404
  test:
    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
  expected response:
    HTTP/1.1 404 Not Found
*/
module.exports = {
  *beforeSendResponse(requestDetail, responseDetail) {
    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      const newResponse = responseDetail.response;
      newResponse.statusCode = 404;
      return {
        response: newResponse
      };
    }
  }
};

修改返回头

anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_header.js
/* 
  sample: 
    modify response header of http://httpbin.org/user-agent
  test:
    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
  expected response:
    X-Proxy-By: AnyProxy
*/
module.exports = {
  *beforeSendResponse(requestDetail, responseDetail) {
    if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {
      const newResponse = responseDetail.response;
      newResponse.header['X-Proxy-By'] = 'AnyProxy';
      return {
        response: newResponse
      };
    }
  }
};

修改返回内容并延迟

anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_data.js
/* 
  sample: 
    modify response data of http://httpbin.org/user-agent
  test:
    curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
  expected response:
    { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! --
*/

module.exports = {
  *beforeSendResponse(requestDetail, responseDetail) {
    if (requestDetail.url === 'http://httpbin.org/user-agent') {
      const newResponse = responseDetail.response;
      newResponse.body += '-- AnyProxy Hacked! --';
      return new Promise((resolve, reject) => {
        setTimeout(() => { // delay the response for 5s
          resolve({ response: newResponse });
        }, 5000);
      });
    }
  },
};

作为npm模块使用

AnyProxy可以作为一个npm模块使用,整合进其他工具。

如要启用https解析,请在代理服务器启动前自行调用AnyProxy.utils.certMgr相关方法生成证书,并引导用户信任安装。或引导用户使用anyproxy-ca方法。

npm i anyproxy@beta --save # 4.0版正在beta中
const AnyProxy = require('anyproxy');
const options = {
  port: 8001,
  rule: require('myRuleModule'),
  webInterface: {
    enable: true,
    webPort: 8002,
    wsPort: 8003,
  },
  throttle: 10000,
  forceProxyHttps: false,
  silent: false
};
const proxyServer = new AnyProxy.ProxyServer(options);

proxyServer.on('ready', () => { /* */ });
proxyServer.on('error', (e) => { /* */ });
proxyServer.start();

//when finished
proxyServer.close();

关于AnyProxy

配置帮助

OSX系统信任CA证书

警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管

安装CA:

Windows系统信任CA证书

配置OSX系统代理

配置浏览器HTTP代理

iOS系统信任CA证书

iOS >= 10.3信任CA证书

配置iOS/Android系统代理

FAQ

The connection is not private

访问特定的HTTPS站点时,AnyProxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确信任(比如,Nodejs不读取系统配置,需要手动传入ca)。

在评估了安全风险后,可以用以下方式绕过证书校验: