Nginx https代理在阿里云SDK中的应用

2024-08-12  本文已影响0人  Teddy_b

背景

在国内访问海外阿里云SDK时,经常超时,而且丢包严重;现象如下:


image.png

为了解决这个问题,尝试在香港搭建Nginx代理,通过代理来访问海外地址

Nginx代理

HTTP代理

首先是搭建Nginx代理,为了快速验证代理的可行性,先以代理HTTP为例进行验证;

Nginx的部署我这里是直接容器部署的,然后给他挂载一个简单的配置文件来负载默认的配置文件

user  root;
worker_processes  auto;

#error_log  logs/error.log notice;
#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    gzip  on;

        upstream ap_southeast_5 {
                server 149.129.204.7:443 weight=1;
                server 147.139.128.4:443 weight=1;
        }


          server {
        listen 32880;
        proxy_connect;
        proxy_connect_allow            443 563;
        proxy_connect_connect_timeout  10s;
        proxy_connect_read_timeout     10s;
        proxy_connect_send_timeout     10s;

        # forward proxy for non-CONNECT request
        location / {
            proxy_set_header Host $http_host;
            proxy_pass http://ap_southeast_5;
        }
    }

}

在这份配置文件中,Nginx监听的是32880端口,然后将收到的所有请求直接转到阿里云雅加达的SDK站点地址

然后写个简单的单元测试验证下代理的可行性

@Test
    public void testListRegions() throws Exception {
        Config config = new Config();
        config.setAccessKeyId(accessKey);
        config.setAccessKeySecret(secretKey);
        config.setRegionId("ap-southeast-5");
        config.setProtocol("http");
        config.setHttpProxy("http://ng-ip:28089");

        com.aliyun.ecs20140526.Client client = new com.aliyun.ecs20140526.Client(config);

        com.aliyun.ecs20140526.models.DescribeRegionsRequest request = new DescribeRegionsRequest();
        request.setResourceType("instance");

        DescribeRegionsResponse response = client.describeRegionsWithOptions(request, new RuntimeOptions());

        log.info("Done");
    }

示例中的ng-ip替换为实际的Nginx运行IP即可,上述单元测试是可以正常跑通的。

这里吐槽下,最初我还以为是需要设置服务端点为代理地址即可,可以这样是不行的,因为阿里云SDK会把Header中的host作为鉴权签名内容的一部分,直接设置端点的话会引起签名错误

config.setEndpoint("http://ng-ip:28089");
image.png image.png

抓包后可以看到Nginx代理上收到的包和发出去的包host变了,因此会引起签名错误;阿里云文档上没有找到相关的使用说明。

HTTPS代理

HTTP跑通之后,再来看看代理HTTPS请求,添加Nginx配置文件

        server {
                listen       28444 ssl;
                 proxy_connect;
            proxy_connect_allow            443 563;
            proxy_connect_connect_timeout  10s;
            proxy_connect_read_timeout     10s;
            proxy_connect_send_timeout     10s;                 
         
                #ssl on;
                ssl_certificate      /data/services/nginx_vhost/cert/weihuitel.com/weihuitel.com.crt;
                ssl_certificate_key  /data/services/nginx_vhost/cert/weihuitel.com/weihuitel.com.key;
              ssl_protocols        SSLv3 TLSv1.1 TLSv1.2 TLSv1.3;
             ssl_ciphers          HIGH:!aNULL:!MD5;


                location / {
                   proxy_set_header Host $http_host;
                   proxy_pass https://slb.ap-southeast-5.aliyuncs.com;
                }

        }

添加的配置让Nginx通过https方式监听在28444 端口

然后修改示例代码:

@Test
    public void testListRegions() throws Exception {
        Config config = new Config();
        config.setAccessKeyId(accessKey);
        config.setAccessKeySecret(secretKey);
        config.setRegionId("ap-southeast-5");
        config.setHttpsProxy("https://ng-ip:28444");

        com.aliyun.ecs20140526.Client client = new com.aliyun.ecs20140526.Client(config);

        com.aliyun.ecs20140526.models.DescribeRegionsRequest request = new DescribeRegionsRequest();
        request.setResourceType("instance");

        // size: 30
        DescribeRegionsResponse response = client.describeRegionsWithOptions(request, new RuntimeOptions());

        log.info("Done");
    }

这次却怎么都不成功,一直报错CONNECT 400,抓包之后可以看到客户端发出的请求仍然是HTTP类型的,正常来讲应该是TLS才对

image.png

看了下SDK代码后,发现他们使用的是okhttp作为客户端发送代理请求的,于是乎,搜了下okhttp proxy https,找了一圈后,在okhttp的 issue里找到这个问题:https://github.com/square/okhttp/issues/6561

发现使用okhttp代理https时需要设置socketFactory,而阿里云SDK里没有入口可以设置这个参数,重写SDK的部分代码后,并引入这个类:https://github.com/square/okhttp/blob/480c20e46bb1745e280e42607bbcc73b2c953d97/okhttp/src/test/java/okhttp3/DelegatingSocketFactory.java

设置上了socketFactory后,确实没有报错CONNECT 400了,但是这次又开始报错CONNECT 502

一番折腾后,发现示例代码里的配置需要调整

Config config = new Config();
        config.setAccessKeyId(accessKey);
        config.setAccessKeySecret(secretKey);
        config.setRegionId("ap-southeast-5");
        config.setProtocol("http");
        config.setHttpsProxy("https://ng-ip:28444");

需要设置协议为HTTP,这次终于不报错了,虽然这里设置了协议是http,但是抓包你会发现协议是TLS,至此才终于解决了这个问题。

Nginx forward https

上述为了简化问题,没有特别说明Nginx也是需要重新编译的,参考文档的说明:
https://stackoverflow.com/questions/46330313/nginx-ssl-forward-proxy-config

Nginx默认不支持转发HTTPS,因为不支持CONNECT方法;

为此需要添加ngx_http_proxy_connect_module模块,参考地址为:

https://github.com/chobits/ngx_http_proxy_connect_module?tab=readme-ov-file#install

如果是容器版本的,可以参考:
https://docs.getconvoy.io/product-manual/forward-proxies/nginx

上一篇下一篇

猜你喜欢

热点阅读