追梦人博客Dream.ren

追梦人 Dream.ren

如何用Nginx搬运整个世界


基于http协议的各种反代方法太多太简单了,网上肯定一大把,这里就无需多言了,今天博主来介绍一种更为通用、隐蔽、安全和高效的反代方法,此方法工作在第四层,添加了对第七层ssl/tls协议中域名字段的嗅探,这种代理对https有着更多的支持和配置项,又原生支持TCP、UDP代理,是真的几乎代天代地代空气,反代整个世界。涨姿势的时候到了,请大家撸起。。袖子,准备干他娘的一炮!

由于在国内使用https协议访问谷歌学术时会受到域名字段检测,导致在国内无法正常使用谷歌学术,即使使用SNI代理,依然无法规避这种检测机制导致的连接失败,所以,国内的上网设备,在不使用代理客户端的情况下,使用谷歌学术去检索资料几乎不可能。然而Pure DNS却能支持谷歌学术的访问并且有长期支持的打算,其实现正是得益于Nginx强大到炸裂的代理功能。下面将对四层Nginx反代配置、当前追梦人博客和谷歌学术反代服务器的Nginx配置进行介绍,说到Nginx,真的得夸一夸,在兼顾性能的情况下各种负载均衡、热更新、各种反代、各种骚操作都能搞定,还能有lua加持,点个赞👍。

上面也提到了,通常情况下国内上网设备需要安装代理客户端才能访问谷歌学术,Pure DNS也不能算例外,只不过Pure DNS把客户端也放到了服务器上运行,才实现了用户免装客户端。为了实现谷歌学术的免客户端访问,需要防火墙内、外各一台服务器进行多层代理,防火墙外Nginx服务器反代谷歌学术并同时作为服务端运行,防火墙内服务器Nginx作为客户端与防火墙外服务端加密通信,同时,防火墙内Nginx又作为服务端为用户提供服务,Pure DNS友情劫持谷歌学术的解析结果,使谷歌学术指向防火墙内自建Nginx反代服务器。这时,用户与谷歌学术服务器间的连接路径为:用户→https协议→防火墙内Nginx→tls加密→防火墙外Nginx→sni反代→谷歌学术服务器,用户端效果就像普通https方式一样即可访问谷歌学术。注意,此处单箭头并不合适,但是我懒得画图了。。。

Nginx过早的版本并不支持下面的配置,可以阅读我的另一篇博文Nginx之stream模块初体验,查看对Nginx的要求,下面的配置仅供参考,实际操作前请备份Nginx配置文件。

下面首先是防火墙外Nginx配置:

user  nginx;
#机器几个核就写几,更通用的方法是auto,Nginx会自动应用合适的值
worker_processes  auto;
error_log  /var/log/nginx/error.log error;
pid        /var/run/nginx.pid;
#Nginx允许使用的最大文件描述符数量限制,适当调大可避免"too many open files"错误
worker_rlimit_nofile 1048576;
events
    {
        #设置轮询方法
        use epoll;
        #单个工作进程可以允许同时建立外部连接的数量,低配机器慎加
        worker_connections 262144;
        #尽可能多地接收连接
        multi_accept on;
    }
stream {
    #这里定义一个后端的map,防止直接通过https方式访问IP造成的循环反代
    map $ssl_preread_server_name $backend {
        #~*[0-9]$             unix:/dev/shm/null.sock;
        ~*\S$                 $ssl_preread_server_name:443;
        default               unix:/dev/shm/null.sock;
    }
    #下面这段就是sni代理+tls加密服务端的实现
    server {
        listen              4333 ssl;
        #与防火墙内Nginx服务器使用tls加密方式进行连接,效果和stunnel等隧道代理类似,
        #这里主要为了加密流量实现防火墙穿透
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
        #服务端加密方式优先
        #ssl_prefer_server_ciphers on;
        #证书文件,可以是自签证书,这里的证书配置主要是服务端验证客户端时要用到,
        #可以用这两行的命令自签(记得去掉#号):
        #openssl req -new -x509 -days 3650 -nodes -subj "/C=CA/ST=CA/L=CA/O=CA/OU=CA/CN=CA" \
        #-out /etc/nginx/cert.pem -keyout /etc/nginx/key.pem
        ssl_certificate     /etc/nginx/cert.pem;
        ssl_certificate_key /etc/nginx/key.pem;
        #下面三条配置是用来验证客户端的,防止非授权客户端连接,没错,这里博主懒得再签一个了
        ssl_client_certificate /etc/nginx/cert.pem;
        ssl_trusted_certificate /etc/nginx/cert.pem;
        #验证客户端貌有问题
        #ssl_verify_client on;
        ssl_session_cache   shared:SSL:20m;
        ssl_session_timeout 10m;
        #下面是sni代理转发设置
        ssl_preread on;
        #DNS服务器设置,用来解析$ssl_preread_server_name(也就是https中的域名)
        #注意,下面一定要替换成可用的dns!
        #我在此处用来限制被代理域名,不能啥网站都代啊对不,当然你也可以用map去限制域名,一样的效果
        #如果允许代理任何https站点,可以设置为resolver 8.8.8.8;
        resolver 127.0.0.1:5353;
        #resolver 8.8.8.8;
        proxy_pass $backend;
    }
}

下面是防火墙内Nginx配置,因为此服务器上跑着现在这个博客以及Pure DNS官网,需要与谷歌学术共用443端口,而且博客又要能够知道到访客的真实IP,因此配置多了点(好像多了挺多的,我这属于比较硬核的写法,此服务器上若没有网站或网站不需要记录访客IP则写法可以简化很多!),在这里,并没有开放http80端口谷歌学术的访问,因为会被未备案提示拦截…然后刚好省下了80端口的反代,只使用443端口进行反代。

user  nginx;
worker_processes  auto;
error_log  /var/log/nginx/error.log error;
pid        /var/run/nginx.pid;
worker_rlimit_nofile 1048576;
events {
    worker_connections  262144;
    multi_accept on;
    use epoll;
}

stream {
    #代理缓冲区大小
    proxy_buffer_size 128k;
    #代理超时时间
    #proxy_connect_timeout 5s;
    #会话状态存储区域,限制单IP最大连接数的时候会用到
    #limit_conn_zone $binary_remote_addr zone=addr:10m;

    #SNI日志格式,SNI代理并未拆解证书,常规的变量在这里是用不了的
    #时间|用户IP|域名|接收字节数|发送字节数|会话用毫秒数
    log_format  main  '$time_iso8601|$remote_addr|$ssl_preread_server_name'
    '|$bytes_received|$bytes_sent|$session_time';

    #后端转发策略
    map $ssl_preread_server_name $backend {
        ~*[0-9]$              unix:/dev/shm/localweb.sock;
        ~*dream.ren$          unix:/dev/shm/localweb.sock;
        ~*puredns.cn$         unix:/dev/shm/localweb.sock;
        default               unix:/dev/shm/nginx-sni.sock;
    }
    #Nginx加密转发上游配置,也就是防火墙外Nginx,由于经费不足,其中只包含了一台备份机,防止单点故障导致的服务中断
    upstream sni_stunnel {
        zone upstream_sni_stunnel 64k;
        server  **.**.**.**:4333 fail_timeout=120s;
        server  **.**.**.**:4333 backup;
    }
    #Nginx加密转发
    server {
        #在内存中监听域套接字(域套接字相比sock套接字具有更好的性能),
        #并声明使用proxy_protocol代理协议,这个协议是用来传递客户端真实IP的
        listen unix:/dev/shm/nginx-sni.sock proxy_protocol;
        #并不需要和sni_stunnel配置中的远程服务器使用proxy_protocol协议,要关闭,否则连接会被中断
        proxy_protocol off;
        #与sni_stunnel服务器使用tls加密方式进行连接
        proxy_ssl on;
        #证书文件,可以是自签证书,为了能与防火墙外服务器连接时通过验证,
        #若防火墙外Nginx未开启客户端验证可以不用配置证书
        #proxy_ssl_certificate /etc/nginx/cert.pem;
        #证书秘钥文件
        #proxy_ssl_certificate_key /etc/nginx/key.pem;
        #下面2行是用来验证服务端的,好像还有些问题
        #proxy_ssl_trusted_certificate /etc/nginx/cert.pem;
        #proxy_ssl_verify on;
        #proxy_ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
        #proxy_connect_timeout 5s;
        #转发到sni_stunnel中的服务器
        proxy_pass sni_stunnel;
        #开启ssl_preread,用来检测域名的
        ssl_preread on;
    }
    #监听服务端口
    server {
        listen      443;
        #声明使用proxy_protocol代理协议,和上面那个server配套使用的
        proxy_protocol on;
        #限制单IP最大连接数
        #limit_conn addr 10;
        #单连接限速
        #proxy_download_rate 400k;
        #proxy_upload_rate 400k;
        #配置sni使用日志,并给日志加缓冲,
        #if=$ssl_preread_server_name表示只有当sni域名不为空的时候进行记录
        access_log  /var/log/nginx/sniproxy.log main buffer=8k flush=5s if=$ssl_preread_server_name;
        #使用上面的那个转发策略
        proxy_pass  $backend;
        #开启ssl_preread,用来检测域名的
        ssl_preread on;
    }
}
#下面是托管在本机上网站的相关配置,网上应该有很多详细介绍,这里就不多做介绍了;
#相信聪明的你配置出一个SSL LAB评分A+的站点应该不是什么难事儿~~
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    charset utf-8;
    log_format  main  '$time_iso8601|$remote_addr|$host|$request|$request_time|$status'
                      '|$body_bytes_sent|$upstream_addr|$upstream_response_time|$upstream_status'
                      '|"$http_referer"|"$http_user_agent"|"$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    client_max_body_size 50m;
    keepalive_timeout  65;

    gzip  on;
    gzip_min_length  1k;
    gzip_buffers     16 16k;
    #gzip_http_version 1.1;
    gzip_comp_level 5;
    gzip_types     text/plain application/javascript application/x-javascript
    text/javascript text/css application/xml application/xml+rss application/json
    application/vnd.ms-fontobject font/ttf font/otf image/svg+xml;
    #gzip_vary on;
    #gzip_proxied   expired no-cache no-store private auth;
    gzip_proxied any;
    gzip_disable   "MSIE [1-6]\.";
    include /etc/nginx/vhost/*.conf;
}

配置完后

nginx -t

检查下配置是否存在错误,没有错误的话

nginx -s reload

重启nginx服务后一个防火墙内可用的SNI代理就搭建完成了~~~
另外,在写本文的时候,发现了另一个好玩的东西:Go Proxy,看起来可玩性很高,感兴趣的童鞋可以去看看:
https://github.com/snail007/goproxy/blob/master/README_ZH.md

2018年3月10日 48 / /
标签:  暂无标签

评论回复

  1. 回复 艺屿EYESVOT

    现在无法使用了

  2. 回复 hostloc

    purdns就是用这个原理实现的吗?

  3. 回复 hostloc

    博主QQ多少?想请教一下具体怎么搭建

  4. 回复 hostloc

    请教一个问题,处了防火墙内Nginx和防火墙外Nginx,是不是还需要一台sniproxy服务器?

    • 回复 Dreamer

      @hostloc防火墙外Nginx自身已经充当SNI了,4333端口相关的配置仅仅是充当一个加密隧道为了把这个SNI服务“映射”到防火墙内的IP上。

  5. 回复 hostloc

    帮我看看,我这墙内和墙外机器配置是否正确。
    墙外:

    user www www;

    worker_processes auto;

    error_log /home/wwwlogs/nginx_error.log crit;

    pid /usr/local/nginx/logs/nginx.pid;

    #Specifies the value for maximum file descriptors that can be opened by this process.
    worker_rlimit_nofile 51200;

    events
    {
    use epoll;
    worker_connections 51200;
    multi_accept on;
    }

    stream {
    #这里定义一个后端的map,防止直接通过https方式访问IP造成的循环反代
    map $ssl_preread_server_name $backend {
    ~*[0-9]$ unix:/dev/shm/null.sock;
    default $ssl_preread_server_name:443;
    ~*google.com$ $ssl_preread_server_name:443;
    ~*youtube.com$ $ssl_preread_server_name:443;

    }
    #下面这段就是sni代理+ssl加密服务端的实现
    server {
    listen 4333 ssl;
    #与防火墙内Nginx服务器使用ssl加密方式进行连接,效果和stunnel等隧道代理类似,
    #这里主要为了加密流量实现防火墙穿透
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
    #服务端加密方式优先
    #ssl_prefer_server_ciphers on;
    #证书文件,可以是自签证书,这里的证书配置主要是服务端验证客户端时要用到,
    #可以用这两行的命令自签(记得去掉#号):
    #openssl req -new -x509 -days 3650 -nodes -subj "/C=CA/ST=CA/L=CA/O=CA/OU=CA/CN=CA" -out /usr/local/nginx/conf/ssl/cert.pem -keyout /usr/local/nginx/conf/ssl/key.pem
    ssl_certificate /usr/local/nginx/conf/ssl/cert.pem;
    ssl_certificate_key /usr/local/nginx/conf/ssl/key.pem;
    #下面三条配置是用来验证客户端的,防止非授权客户端连接,没错,这里博主懒得再签一个了
    ssl_client_certificate /usr/local/nginx/conf/ssl/cert.pem;
    ssl_trusted_certificate /usr/local/nginx/conf/ssl/cert.pem;
    ssl_verify_client on;
    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 10m;
    #下面是sni代理转发设置
    ssl_preread on;
    #DNS服务器设置,用来解析$ssl_preread_server_name(也就是https中的域名)
    #注意,下面一定要替换成可用的dns!
    #我在此处用来限制被代理域名,不能啥网站都代啊对不,当然你也可以用map去限制域名,一样的效果
    #如果允许代理任何https站点,可以设置为resolver 8.8.8.8;
    #resolver 127.0.0.1:5353;
    resolver 8.8.8.8;
    proxy_pass $backend;
    }
    }

    http
    {
    include mime.types;
    default_type application/octet-stream;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    server_names_hash_bucket_size 128;
    client_header_buffer_size 32k;
    large_client_header_buffers 4 32k;
    client_max_body_size 50m;

    sendfile on;
    tcp_nopush on;

    keepalive_timeout 60;

    tcp_nodelay on;

    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 256k;

    proxy_connect_timeout 5;
    proxy_read_timeout 60;
    proxy_send_timeout 5;
    proxy_buffer_size 16k;
    proxy_buffers 4 64k;
    proxy_busy_buffers_size 128k;

    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 2;
    gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss;
    gzip_vary on;
    gzip_proxied expired no-cache no-store private auth;
    gzip_disable "MSIE [1-6]\.";

    #limit_conn_zone $binary_remote_addr zone=perip:10m;
    ##If enable limit_conn_zone,add "limit_conn perip 10;" to server section.

    server_tokens off;
    #log format
    log_format access '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" $http_x_forwarded_for';
    access_log off;

    server
    {
    listen 80 default_server;
    #listen [::]:80 default_server ipv6only=on;
    server_name www.lnmp.org;
    index index.html index.htm index.php;
    root /home/wwwroot/default;

    #error_page 404 /404.html;
    include enable-php.conf;

    location /nginx_status
    {
    stub_status on;
    access_log off;
    }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
    expires 30d;
    }

    location ~ .*\.(js|css)?$
    {
    expires 12h;
    }

    location ~ /\.
    {
    deny all;
    }

    access_log /home/wwwlogs/access.log access;
    }
    include vhost/*.conf;
    }

    墙内:

    user www www;

    worker_processes auto;

    error_log /home/wwwlogs/nginx_error.log crit;

    pid /usr/local/nginx/logs/nginx.pid;

    #Specifies the value for maximum file descriptors that can be opened by this process.
    worker_rlimit_nofile 51200;

    events
    {
    use epoll;
    worker_connections 51200;
    multi_accept on;
    }

    stream {
    #代理缓冲区大小
    proxy_buffer_size 128k;
    #代理超时时间
    #proxy_connect_timeout 5s;
    #会话状态存储区域,限制单IP最大连接数的时候会用到
    #limit_conn_zone $binary_remote_addr zone=addr:10m;

    #SNI日志格式,SNI代理并未拆解证书,常规的变量在这里是用不了的
    #时间|用户IP|域名|接收字节数|发送字节数|会话用毫秒数
    log_format main '$time_iso8601|$remote_addr|$ssl_preread_server_name'
    '|$bytes_received|$bytes_sent|$session_time';

    #后端转发策略
    map $ssl_preread_server_name $backend {
    ~*[0-9]$ unix:/dev/shm/localweb.sock;
    ~*google.com$ unix:/dev/shm/localweb.sock;
    ~*google.com.hk$ unix:/dev/shm/localweb.sock;
    ~*youtube.com$ unix:/dev/shm/localweb.sock;
    default unix:/dev/shm/nginx-sni.sock;
    }
    #Nginx加密转发上游配置,也就是防火墙外Nginx,由于经费不足,其中只包含了一台备份机,防止单点故障导致的服务中断
    upstream sni_stunnel {
    zone upstream_sni_stunnel 64k;
    server 墙外IP:4333 fail_timeout=120s;
    #server 墙外IP:4333 backup;
    }
    #Nginx加密转发
    server {
    #在内存中监听域套接字(域套接字相比sock套接字具有更好的性能),
    #并声明使用proxy_protocol代理协议,这个协议是用来传递客户端真实IP的
    listen unix:/dev/shm/nginx-sni.sock proxy_protocol;
    #并不需要和sni_stunnel配置中的远程服务器使用proxy_protocol协议,要关闭,否则连接会被中断
    proxy_protocol off;
    #与sni_stunnel服务器使用ssl加密方式进行连接
    proxy_ssl on;
    #证书文件,可以是自签证书,为了能与防火墙外服务器连接时通过验证,
    #若防火墙外Nginx未开启客户端验证可以不用配置证书
    proxy_ssl_certificate /usr/local/nginx/conf/ssl/cert.pem;
    #证书秘钥文件
    proxy_ssl_certificate_key /usr/local/nginx/conf/ssl/key.pem;
    #下面2行是用来验证服务端的,好像还有些问题
    proxy_ssl_trusted_certificate /usr/local/nginx/conf/ssl/cert.pem;
    proxy_ssl_verify on;
    proxy_ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
    proxy_connect_timeout 5s;
    #转发到sni_stunnel中的服务器
    proxy_pass sni_stunnel;
    #开启ssl_preread,用来检测域名的
    ssl_preread on;
    }
    #监听服务端口
    server {
    listen 443;
    #声明使用proxy_protocol代理协议,和上面那个server配套使用的
    proxy_protocol on;
    #限制单IP最大连接数
    #limit_conn addr 10;
    #单连接限速
    #proxy_download_rate 400k;
    #proxy_upload_rate 400k;
    #配置sni使用日志,并给日志加缓冲,
    #if=$ssl_preread_server_name表示只有当sni域名不为空的时候进行记录
    access_log /var/log/nginx/sniproxy.log main buffer=8k flush=5s if=$ssl_preread_server_name;
    #使用上面的那个转发策略
    proxy_pass $backend;
    #开启ssl_preread,用来检测域名的
    ssl_preread on;
    }
    }

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

    server_names_hash_bucket_size 128;
    client_header_buffer_size 32k;
    large_client_header_buffers 4 32k;
    client_max_body_size 50m;

    sendfile on;
    tcp_nopush on;

    keepalive_timeout 60;

    tcp_nodelay on;

    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 256k;

    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 2;
    gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss;
    gzip_vary on;
    gzip_proxied expired no-cache no-store private auth;
    gzip_disable "MSIE [1-6]\.";

    #limit_conn_zone $binary_remote_addr zone=perip:10m;
    ##If enable limit_conn_zone,add "limit_conn perip 10;" to server section.

    server_tokens off;
    access_log off;

    server
    {
    listen 80 default_server;
    #listen [::]:80 default_server ipv6only=on;
    server_name _;
    index index.html index.htm index.php;
    root /home/wwwroot/default;

    #error_page 404 /404.html;

    # Deny access to PHP files in specific directory
    #location ~ /(wp-content|uploads|wp-includes|images)/.*\.php$ { deny all; }

    include enable-php.conf;

    location /nginx_status
    {
    stub_status on;
    access_log off;
    }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
    expires 30d;
    }

    location ~ .*\.(js|css)?$
    {
    expires 12h;
    }

    location ~ /.well-known {
    allow all;
    }

    location ~ /\.
    {
    deny all;
    }

    access_log /home/wwwlogs/access.log;
    }
    include vhost/*.conf;
    }

  6. 回复 hostloc

    访问的时候,我将google.com劫持指向墙内IP,是这样吗?

    • 回复 Dreamer

      @hostloc是的

      • 回复 hostloc

        我那墙内墙外的配置是对的吗? 为什么我把google.com指向墙内的时候,页面提示“无法访问此网站 http://www.google.com 意外终止了连接。”

        • 回复 Dreamer

          @hostlocnginx -t测试下配置是否存在异常,另外,墙外nginx配置中的DNS改了吗?还有,你访问的是http而不是https。

          • 回复 hostloc

            @Dreamernginx -t墙内墙外都正常。墙外nginx配置中的DNS我改成8.8.8.8. 另外我浏览器直接输入https://www.google.com进行访问的

  7. 回复 hostloc

    博主能分享你的墙内和墙外的配置文件吗?搞了很久,始终搞不定

    • 回复 Dreamer

      @hostloc这个就是我实际使用的配置添加注释、隐去IP后的结果~

  8. 回复 x

    puredns的sni代理为什么不用ss之类的进行中转 这样应该更抗干扰

    • 回复 Dreamer

      之前用的stunnel中转流量,和现在的效果相同,防火墙已经检测不到加密前的流量内容了,没有必要再强求什么抗干扰了,之前也试过经ss转sni的gost,奈何软件bug太多没法用,遂放弃了,况且更加小众的中转方式不见得不好。

      • 回复 x

        用ss或者ssr做中转建sni代理必须要用软件吗 我还盘算着以后用ss或者ssr拾建sni代理呢

9 + 9 =

回到顶部