欢迎,来自IP地址为:18.221.53.209 的朋友



IPv4地址的稀缺,使从主机服务提供商那里获得IP址变得越来越困难和昂贵了,即便是公共云服务商也变得十分吝啬。在我们苦等IPv6地址到来的时候,其实也有一些办法来扩展使用现有IPv4地址空间。

IPv6的一些缺点我也不再一一列举,但是让其真正普及的障碍是全球许多ISP运营商还不想为用户提供IPv6的地址。这点在一些国家尤其严重,IPv6网络一直处于实验网阶段,没有真正向公众用户开放。

其实对于运营商来说,给每个用户分配IPv6地址就像噩梦一般,而用户往往会要求运营商给自己所有的联网设备都分配不同的IPv6地址,这种工作量对于IPv4地址分配来说可以是线性的增加。

依目前的形势来看,在可预见的未来,Web服务业务将继续采用IPv4地址。由于大多数人还没有实现IPv6的接入,并且可能会持续很长一段时间。现在要做的就是如何充分利用越来越稀缺的IPv4地址。

反向代理服务

要理解反向代理,首先需要了解一下地址转换(NAT)。NAT方式打破了端到端的网络模型,这对于IPv6来说简直是种可怕的事情。端到端模型是一种主机A到主机B之间直接通过IP地址互通的方式,中间不存在转换层。端到端方式主机的IP 地址应该都具有公网IP地址,以尽可能减少通信时受到的影响。

NAT方式恰恰相反,NAT方式将整个网络置于同一IP下,通过不同的端口来区分服务器。例如,你想通过RDP远程桌面工具访问一台服务器的3389端口,可能会将该端口映射到公网IP的33389端口。这样,连接此服务器时就要采用rdp.example.com:33389(假设公网IP对应域名example.com)而不是rdp.example.com。

开发人员之前一直在处理NAT的网络中进行编码开发,并没有真正关心过两台主机是如何实现互通的。并且在NAT网络技术基础上开发了大量的程序库,这看起来有些可怕。

如同IPv6纯粹主义者憎恨NAT一样,我们同样可以想像的到他们同样厌恶反向代理,但是反向代理技术是未来发展方向。反向代理服务器在一个IP地址上接收所有的流量,并为相将不同的流量引入不同的后端服务器为其服务。这种方式大量应用于HTTP和HTTPS流量。

HTTP和HTTPS流量包含一个主机头信息(对于HTTPS来说是Server Name Indication (SNI)),这个头信息包含了要访问服务器的信息。除了简单的采用IP地址访问,我们还可以采用域名访问服务器。反向代理服务器会识别不同的主机头信息,根据其配置信息,将不同的流量传递给后端不同的服务器。

网站反向流量代理一般还具有静态内容缓存功能,以加快后端服务器内容的访问速度。反向代理也越来越多的应用于防止服务侦测和内容侦测。同时,还具有一个个人很喜欢的功能,就是为那些不采用SSL技术的网站提供一个统一的SSL前端,以达到网站HTTPS化的目的。后端的WEB服务器可以是任意类型,我们可以使用nginx来提供HTTP反向代理。

商业反向代理当然是有的,Citrix NetScaler VPX是其中一款,当然Barracuda NG Firewall也行,Smoothwall UTM Untangle也具备相应功能。可以说现存的反向代理软件比HTTP服务器可能还要多,但是我们还是选择nginx,因为它是免费、开源和最流行的。

实例讲解

反向代理似乎令人望而生畏,但它实际上并不像想像的那么可怕。我花了几天时间就配置好了一个基于CentOS 7.0的反向代理服务器,以下将是一些基本内容。(这里假定你具有一定的RHEL系列Linux系统使用能力,包括安装、登录及使用vim及yum工具)。

1、最小化安装CentOS 7.0,关闭SELinux,安装相应软件和设置目录:

yum install epel-release
yum install net-tools wget python nginx git
yum update
mkdir /var/www/nginx_cache
mkdir /var/www/nginx_tmp
mkdir /var/www/letsencrypt-auto
chown nginx:nginx /var/www/nginx_cache
chown nginx:nginx /var/www/nginx_tmp
chown root:root /var/www/letsencrypt-auto
chmod 0755 /var/www/nginx_cache
chmod 0755 /var/www/nginx_tmp
chmod 0755 /var/www/letsencrypt-auto

2、将/etc/nginx/nginx.conf的配置文件替换为如下内容:

user nginx;
worker_processes 2;
worker_rlimit_nofile 2048;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

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

http {
    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  /var/log/nginx/access.log  main;

    # Proxy cache and temp configuration.
    proxy_cache_path /var/www/nginx_cache levels=1:2
                     keys_zone=main:10m
                     max_size=1g inactive=30m;
    proxy_temp_path /var/www/nginx_tmp;

    # Gzip Configuration.
    gzip on;
    gzip_disable msie6;
    gzip_static on;
    gzip_comp_level 4;
    gzip_proxied any;
    gzip_types text/plain
               text/css
               application/x-javascript
               text/xml
               application/xml
               application/xml+rss
               text/javascript;

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

    # Report real IPs from X-Forwarded-For header from Cloudflare IPs
    set_real_ip_from 103.21.244.0/22;
    set_real_ip_from 103.22.200.0/22;
    set_real_ip_from 103.31.4.0/22;
    set_real_ip_from 104.16.0.0/12;
    set_real_ip_from 108.162.192.0/18;
    set_real_ip_from 131.0.72.0/22;
    set_real_ip_from 141.101.64.0/18;
    set_real_ip_from 162.158.0.0/15;
    set_real_ip_from 172.64.0.0/13;
    set_real_ip_from 173.245.48.0/20;
    set_real_ip_from 188.114.96.0/20;
    set_real_ip_from 190.93.240.0/20;
    set_real_ip_from 197.234.240.0/22;
    set_real_ip_from 198.41.128.0/17;
    set_real_ip_from 199.27.128.0/21;
    set_real_ip_from 2400:cb00::/32;
    set_real_ip_from 2606:4700::/32;
    set_real_ip_from 2803:f800::/32;
    set_real_ip_from 2405:b500::/32;
    set_real_ip_from 2405:8100::/32;
    set_real_ip_from 2c0f:f248::/32;
    set_real_ip_from 2a06:98c0::/29;
    real_ip_header CF-Connecting-IP;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    include /etc/nginx/conf.d/*.conf;
}

3、创建/etc/nginx/conf.d/servers.conf配置文件或修改成如下内容:

server {
    listen 80;
    server_name example.com www.example.com;
    access_log  /dev/null;

    # Allows letsencrypt-auto to verify domain ownership
    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        allow all;
        root /var/www/letsencrypt-auto;
    }

    proxy_ignore_headers "Cache-Control" "Expires";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    client_max_body_size 100m;
    proxy_pass_header Set-Cookie;

    # Catch the wordpress cookies.
    # Must be set to blank first for when they don't exist.
    set $wordpress_auth "";

    if ($http_cookie ~* "wordpress_logged_in_[^=]*=([^%]+)%7C") {
        set $wordpress_auth wordpress_logged_in_$1;
    }

    # Set the proxy cache key
    set $cache_key $scheme$host$uri$is_args$args;

    # Don't cache these pages.
    location ~* ^/(wp-admin|wp-login.php){
        proxy_pass http://examplebackend;
    }

    location / {
        proxy_pass http://examplebackend;
        proxy_cache_bypass $cookie_nocache $arg_nocache;
        proxy_cache main;
        proxy_cache_key $cache_key;
        proxy_cache_valid 30m; # 200, 301 and 302 will be cached.
        # Fallback to stale cache on certain errors.
        # 503 is deliberately missing, if we're down for maintenance
        # we want the page to display.
        proxy_cache_use_stale error
                              timeout
                              invalid_header
                              http_500
                              http_502
                              http_504
                              http_404;

        # 2 rules to dedicate the no caching rule for logged in users.
        proxy_cache_bypass $wordpress_auth; # Do not cache the response.
        proxy_no_cache $wordpress_auth; # Do not serve response from cache.
}

如果你想缓存内容,则采用如下配置

# Backend server definition
upstream examplebackend {
    server 172.16.0.199;
}

server {
    listen 80;
    server_name example.com www.example.com;
    # Allows letsencrypt-auto to verify domain ownership
    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        allow all;
        root /var/www/letsencrypt-auto;
    }

    proxy_ignore_headers "Cache-Control" "Expires";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    client_max_body_size 100m;
    proxy_pass_header Set-Cookie;

    # Catch the wordpress cookies.
    # Must be set to blank first for when they don't exist.
    set $wordpress_auth "";
    if ($http_cookie ~* "wordpress_logged_in_[^=]*=([^%]+)%7C") {
        set $wordpress_auth wordpress_logged_in_$1;
    }

    # Set the proxy cache key
    set $cache_key $scheme$host$uri$is_args$args;

    location / {
        proxy_pass http://examplebackend;
        proxy_cache_valid any 0;
    }

这些配置文件被设计成WordPress兼容的,因为我的网站目前采用了此方式建设。

为后端的每台服务器都添加一个配置文件块,如果后端服务器对应多个网站,则只需要为主机添加不同的域名。如果想配置成缓存和非缓存混合结构,这看起来很不错,你可以配置足够的文件块来指向你后端的服务器。这里留下了一些配置内容给朋友们练习,我想以上的注释内容可以帮助完成配置。

最后还要在你的互联网出口设备上开一个端口,以便网络访问。确保公网IPv4地址的80端口可以被nginx侦听,完成此步骤后,你便拥有了一台全功能的反向代理服务器。

下面便是启用letsencrypt功能以进行SSL的配置,下面就是安装letsencrypt。

4、启用SSL功能配置

以下的操作假定使用root用户操作,首先安装letsencrypt:

$ cd ~
$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt
$ ./letsencrypt-auto

也许会出现“no installers are available on your OS yet; try running ‘letsencrypt-auto certonly’ to get a cert you can install manually”的提示,这也没有什么关系,忽略就是了。现在我们为openssl生成一个良好的密钥,由于要一直使用,当然选择加密位数较高的,可能花费的时间有点久,耐心等就是了。

$ cd /etc/nginx
$ openssl dhparam -out dhparam.pem 4096

一旦这步完成,便可以在/root/letsencrypt目录中创建一个叫作letsencrypt_gen的脚本文件,内容如下:

#!/usr/bin/env bash

if [[ -z "$DOMAINS" ]]; then
    echo "Please set DOMAINS environment variable " \
         "(e.g. \"-d example.com -d www.example.com\")"
    exit 1
fi

if [[ -z "$DIR" ]]; then
    export DIR=/var/www/letsencrypt-auto
fi


mkdir -p $DIR && /root/letsencrypt/letsencrypt-auto certonly -v \
    --server https://acme-v01.api.letsencrypt.org/directory \
    --webroot \
    --webroot-path=$DIR \
    $DOMAINS

service nginx reload

创建完成后,用命令chmod 0755将该脚本的权限设置为可执行。然后运行此脚本以生成letsencrypt的证书文件:

$ cd ~/letsencrypt
$ DOMAINS="-d example.com -d www.example.com" \
  /root/letsencrypt/letsencrypt_gen

假设域名使用example.com,也就是指向nginx服务器域名序列的第一个域名。操作完成后,会在/etc/letsencrypt目录生成相应文件。现在需要在nginx的配置文件中增加相应内容以开启SSL:

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    # certificates from letsencrypt

    ssl_certificate      /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # Diffie-Hellman parameter for DHE ciphersuites
    ssl_dhparam /etc/nginx/dhparam.pem;

    # modern configuration
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';

    ssl_prefer_server_ciphers on;

    # HSTS (ngx_http_headers required) - 6 months
    # add_header Strict-Transport-Security max-age=15768000;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # verify chain of trust of OCSP response
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    proxy_ignore_headers "Cache-Control" "Expires";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    client_max_body_size 100m;
    proxy_pass_header Set-Cookie;

    # Catch the wordpress cookies.
    # Must be set to blank first for when they don't exist.
    set $wordpress_auth "";
    if ($http_cookie ~* "wordpress_logged_in_[^=]*=([^%]+)%7C") {
        set $wordpress_auth wordpress_logged_in_$1;
    }

    # Set the proxy cache key
    set $cache_key $scheme$host$uri$is_args$args;

    location / {
        proxy_pass http://examplebackend;
        proxy_cache_valid any 0;
    }

完成后,重新启动nginx,开启外网相应端口(443)。

由于letsencrypt的证书一般只有90有效期,所以要在证书失效前重新更新证书。可以使用cron命令自动设置更新证书功能/root/letsencrypt/certbot-auto renew。

现在,你的反向代理服务器便可以对example.com的SSL请求做出响应了,然后将非SSL请求传递至后端服务器。不必为后端的每台服务器都应用SSL功能,也不必购买证书。现在,你就以一个IP地址运行了多台虚拟服务器,并且每台服务器都可以是自动更新SSL证书。

应对稀缺资源

在不久之前,每个SSL站点都需要独立的IP地址,但现在SNI(Server Name Indication)技术改变了这一状况。仿佛一下了我们可以使用30个地址一样,而在10年前,同时拥有30个地址很容易受到限制的。

反向代理只有应对IPv4地址枯竭的一种技术手段之一,在采用纯端到端IPv6技术之前,我们采用此类技术可以有效的使用现有IPv4地址,从而延长其使用寿命。

诚然,我们现有没有更多的IPv4地址可以分配了,但这差不意味着我们不能再使用IPv4地址了。我们会变得更加聪明,以保证我们在没有更多IPv4地址的情况部署更多的公众应用。

同时,反向代理和下一代防火墙将有效的延长现有IPv4地址的使用期,同时,所涉及的技术也真的没有那么复杂。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注