美文网首页
Nginx https代理在阿里云SDK中的应用

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

作者: Teddy_b | 来源:发表于2024-08-12 12:20 被阅读0次

    背景

    在国内访问海外阿里云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

    相关文章

      网友评论

          本文标题:Nginx https代理在阿里云SDK中的应用

          本文链接:https://www.haomeiwen.com/subject/mlzrkjtx.html