使用 Nginx 做翻墙代理

配置

/etc/nginx/nginx.conf 配置,可以采用自签名证书,客户端连的时候不 verify 证书即可。

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;

events {
    worker_connections 10240;
}

http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for" '
    '"upstream=$upstream_addr" "scheme=$scheme"     "X-Remote-App=$http_x_remote_app" "reqtime=$request_time"     "upstream_resptime=$upstream_response_time"     "$upstream_cache_status" "host=$host"';
    ssl_certificate /etc/nginx/ssl/52.77.252.184.crt;
    ssl_certificate_key /etc/nginx/ssl/52.77.252.184.key;
    ssl_session_cache shared:SSL:100m;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SH    A256:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AE    S128-SHA:DES-CBC3-SHA';
    ssl_prefer_server_ciphers on;

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Load modular configuration files from the     /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/server.conf 配置。

#
# A virtual host using mix of IP-, name-, and port-based configuration
#

server {
    listen 443 ssl;
    server_name _;

    access_log /var/log/nginx/443_access.log main;
    resolver 8.8.8.8;

    set $upstream_endpoint $host;

    location / {
        proxy_pass https://$upstream_endpoint;
    }
}

server {
    listen 80;
    server_name _;

    access_log /var/log/nginx/80_access.log main;
    resolver 8.8.8.8;

    set $upstream_endpoint $host;

    location / {
        proxy_pass http://$upstream_endpoint;
    }
}

测试

要修改 header,而且 curl 要加 -k 不 verify 证书。

$ curl -k -I -H "Host:play.google.com" "https://52.77.252.184/store/apps/details?hl=en&id=tr.com.fugo.kelimeavi2.en"
HTTP/1.1 200 OK

$ curl -k -I -H "Host:www.baidu.com" http://52.77.252.184
HTTP/1.1 200 OK

这样翻墙的好处是可以有多台 Nginx,程序能够控制访问哪台 Nginx。

python requests 信任自签名证书

我在一台 Nginx 上搭建 https 服务,证书使用自签名的证书,然后使用 Python 访问 https 服务。

 

解决方法1:

#!/bin/env python

import requests

url = "https://52.77.252.184/test.txt"

ret = requests.get(url, verify="/tmp/ssl/requests_test.crt")
print ret.status_code

通过 verify 指定证书,表示相信此证书(requests_test.crt 是服务器端证书);也可以用 verify=False,表示不验证服务器端的证书。

 

解决方法2:

设置环境变量 REQUESTS_CA_BUNDLE:
export REQUESTS_CA_BUNDLE=/tmp/ssl/requests_test.crt

然后使用 request 访问。

#!/bin/env python

import requests

url = "https://52.77.252.184/test.txt"

ret = requests.get(url)
print ret.status_code

遇到的一个用于 LVS 检查的 Nginx ssl 配置的大坑

参照 我之前遇到的  ,这次的坑 更大了一点,导致贵司最重要的数个域名挂了 6 min,真是令人伤心啊。

看下面的用于 LVS 检查的配置:

server {
    listen 80 default_server;
    listen 443 ssl default_server;

    # Do not delete it, otherwise lvs http check will fail, it’s terrible!!!
    location /http_check/index.html {
        root /home/work/nginx/conf/;
        open_file_cache off;
    }

    location /nginx_status {
        stub_status on;
        access_log off;
        allow 10.0.0.0/8;
        deny all;
    }
}

 

坑的主角是 linsten 443 后面的 ssl,如果不加 ssl ,而且 在 Nginx 其他的配置里 没有 linsten 443 ssl 的话,那么 LVS 检查就会失败,报 400,而且 Nginx 日志显示类似的乱码:

10.0.19.34 – – [05/Feb/2015:00:01:05 +0800] “\x16\x03\x01\x01\x02\x01\x00\x00\xFE\x03\x03T\xD2BA\xC2\x03G\xCB\xF1M\xCE\xD5\
xB5\xF0 \xB8e\x0F\x1Cwh\x06%,\xDB\xFD\xD7\x0C\xE9\xD8\xBB\xCA\x00\x00\x94\xC00\xC0,\xC0(\xC0$\xC0\x14\xC0” 400 588 “-” “-”

 

今天我「走运了」,修改配置 恰巧引发的了 LVS SSL 检查的 400 ,根据 上面的链接中的解释,如果 LVS 检查某个 VIP 的后端的某个端口全部失败时,会删除 这个 VIP ,这个 VIP 上面的其他端口 也就没了。所以今天才导致数个重要域名无法访问,真是罪过啊 。

 

Nginx 单IP下 配置多个server https 的问题

很早之前就遇到类似的问题,今天写下来以防忘记。


我们的负载均衡架构是这样的:

请求 —> 网络 —> LVS集群 —> Nginx 集群 —> APP

LVS使用FULLNAT模式,每台Nginx 机器只有一个IP(内网IP),LVS也是把流量转到这个IP。如果Nginx想对多个域名使用 https,比如两个域名 wandoujia.com 和 wandoulabs.com ,是可能有问题的。


看下面的配置(两个server写在不同文件中,用 include * 加载):

server {
    listen 80;
    listen 443 ssl;
    server_name test.wandoujia.com;
    ssl_certificate wandoujia.crt;
    ssl_certificate_key wandoujia.key;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;
    …
}

server {
    listen 80;
    listen 443 ssl;
    server_name test.wandoulabs.com;
    ssl_certificate wandoulabs.crt;
    ssl_certificate_key wandoulabs.key;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;
    …
}

这样你访问 https://test.wandoujia.com 或者 https://test.wandoulabs.com,都可能报证书有问题,这是为什么。

事实上,SSL运行在TCP之上(SSL/TLS协议),ssl通过四次握手和服务器(这里是Nginx,LVS纯转发,可忽略)的IP + PORT(443)建立ssl连接,建立连接之后浏览器才会发送HTTP请求。所以在Nginx收到HTTP请求之后才知道Host,才知道转到哪个server 去处理,所以在SSL连接建立的时候Nginx是不知道用哪个 Server 的SSL配置的,在这种情况下,Nginx会使用它加载到的第一个SSL配置(需验证)。当然如果你在listen 443的后面加上 default_server,Nginx就会使用此SSL配置,即:

listen 443 default_server ssl;

那么对于单域名的https,我更喜欢把SSL配置写在http配置里,在server只需要加上 listen 443 ssl;,类似:

http {
    …
    ssl_certificate wandoujia.crt;
    ssl_certificate_key wandoujia.key;
    #ssl_certificate wandoulabs.crt;
    #ssl_certificate_key wandoulabs.key;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;
    …
}

server {
    listen 80;
    listen 443 ssl;
    server_name test.wandoujia.com;
    …
}

那么怎么才能实现多域名的 https 呢,是有办法的,叫做 TLS Server Name Indication extension(SNI, RFC 6066),它允许浏览器在SSL握手的时候发送请求的server name,也就是 Host,这样 Nginx 就能找到对应server 的SSL配置。

但是要浏览器支持SNI 才可以,支持SNI的浏览器有:

Opera 8.0;
MSIE 7.0 (but only on Windows Vista or higher);
Firefox 2.0 and other browsers using Mozilla Platform rv:1.8.1;
Safari 3.2.1 (Windows version supports SNI on Vista or higher);
and Chrome (Windows version supports SNI on Vista or higher, too).

同样,在服务器端 openssl 要支持SNI,编译的时候加上–enable-tlsext 即可,不过从 0.9.8j 版本开始编译的时候默认会加。openssl 支持了SNI,Nginx 才可以work,确认是否OK:

$ ./nginx -V
…
TLS SNI support enabled
…

所以,如果想要实现多域名https,要确认你的Nginx 支持 TLS SNI support,然后在server 单独配置SSL, 但是要注意,某些浏览器会有问题,而且因为Nginx加载server 的顺序不同(特别是每个server在同一目录的不同文件中,然后在nginx.conf主配置文件中用 include * 包含),可能会出现奇怪的问题,我是遇到过的。