本文主要就实现前言中的要求2进一步展开说明。

实现的思路是利用前端服务器(即中间服务器)反向代理客户端的应用请求,并将请求转发到实际部署应用的后端应用服务器,对于同一台机器而言(假设应用实际部署在2367端口),在默认情况下前端相当于监听该机器80端口的虚拟服务器(本文使用Apache),后端相当于监听2367端口的虚拟服务器(本文使用Nginx).

对于Nginx和Apache的区别:

  • Nginx高度模块化设计,编写配置简单,处理静态文件速度高于Apache,内存占用较少,同时并发性较好(异步非阻塞型),适合做前端服务器
  • Apache非常稳定,动态页面处理能力高,模块非常丰富,rewrite性能强大

因此对于单独选择其中一种web服务器来构建项目时,如果追求项目性能,可以使用Nginx;相对的如果追求稳定性以及更多配置选项,建议使用Apache。对于两者结合使用来说,Nginx更适合作为前端来担任负载均衡和反向代理,将Apache用作后端服务器来处理动态请求。本文是在现有配置的基础(Apache监听80端口)并且不方便修改配置的情况下,用Apache作为前端反向代理,并不盲目推荐仿造该做法,应根据实际情况进行调整。

构建过程

具体构建过程如下图所示:

左边的绿框表示基于Apache构建的反向代理服务器,右边的粉框表示基于Nginx构建的应用服务器。反向代理服务器除了基础的反向代理配置以外,还加了一层ip过滤,用来阻止校园网以外ip(因为对于我的需求来说,应用的受访对象是学校研究室)的访问;应用服务器上除了配置应用以外还增加了一个登录认证的环节(用户名密码)来过滤研究室成员以外的访问。Apache和Nginx间添加了个白名单,使得应用服务器只能接受来自反向代理服务器的请求(这样外部即使探测到应用的地址也无法直接访问)。

反向代理

Apache的配置如下:

# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf

# Include list of ports to listen on
Include ports.conf

#NameVirtualHost *:443
<VirtualHost *:443>
        ServerName abc.example.com
        ServerAlias abc.example.com
        ProxyRequests Off
        <Proxy "*">
                Require ip yyy.yyy.yyy.yyy/zz
        </Proxy>
        ProxyPass / http://xxx.xxx.xxx.xxx:2367/
        ProxyPassReverse / http://xxx.xxx.xxx.xxx:2367
        SSLCertificateFile "/path/to/your/sslcert/fullchain.pem"
        SSLCertificateKeyFile "/path/to/your/sslcert/privkey.pem"
</VirtualHost>

其中要注意的是,Apache的反向代理功能是需要手动开启的。首先需要向apache.conf里导入mod_proxymod_proxy_http.somod_proxy_connect.so这三个模块。如果你是FreeBSD,你只需要在httpd.conf上把注释取消掉,并重启服务后就可以了;但如果你使用的是Ubuntu,与sites-availablesites-enabled的使用方法相同,mods-available是所有模块的存储目录,但是服务载入的模块是存储在mods-enabled的。

这一点通过apache.conf的以下几行可以证实:

# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf

当apache服务运行时,只载入mods-enabled里面的模组,而部分模组在安装时预设是不会加载的。(即该模组不会出现在mods-enabled)

Apache给我们提供了a2enmod指令帮助我们快速添加模块,相对的停用模块使用a2dismod命令:

admin@ubuntu:~$ sudo a2enmod proxy proxy_http proxy_connect # 启用模块
admin@ubuntu:~$ sudo a2dismod proxy proxy_http proxy_connect # 停用模块

启用之后,指定的模块会以软链接的形式被放进mods-enabled中,重启服务生效。

添加模块之后,需要在apache.conf中添加如下行即可启用反向代理功能。

ProxyRequests Off

添加如下行设置你的代理目标

ProxyPass / http://xxx.xxx.xxx.xxx:2367/
ProxyPassReverse / http://xxx.xxx.xxx.xxx:2367

添加ip白名单,你可以指定具体ip,也可以像我一样限定一个网段。

<Proxy "*">
    Require ip yyy.yyy.yyy.yyy/zz
</Proxy>

添加https证书,关于代理服务器与应用服务器是否需要https这一点可以参考反向代理服务器代理HTTPS

SSLCertificateFile "/path/to/your/sslcert/fullchain.pem"
SSLCertificateKeyFile "/path/to/your/sslcert/privkey.pem"

至此,反向代理服务器的配置到此为止。

应用服务器

Nginx的配置如下,笔者配置的是静态页面,php等动态应用请根据Nginx文档自行调整:

server {
        listen 2367;
        listen [::]:2367;

        root /path/to/app;

        # Add index.php to the list if you are using PHP
        index index.html index.htm;

        server_name _;

        auth_basic "Please enter your username and password.";
        auth_basic_user_file /path/to/your/htpasswd;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
                alias /path/of/your/app/;
                index index.html;
        }
}

以下开始说明。在/etc/nginx/sites-availble/下以默认模板创建一个全新的配置文件,将其软链接到/etc/nginx/sites-enabled/:

sudo touch /etc/nginx/sites-available/mysite.conf
sudo ln -s /etc/nginx/sites-available/mysite.conf /etc/nginx/sites-enabled/mysite.conf.enable

使用绝对路径,不然会报错。
应用的部署配置,根据你的实际情况进行编写。

登录验证我们通过nginx配置以及htpasswd工具实现。htpasswd是一款Apache内置的用户名密码生成工具,使用方法如下:

$ htpasswd -c /usr/local/nginx/password username
# 假如我们想将验证文件储存在/usr/local/nginx/下
# 文件名为password,登录用户名为username
# 回车后输入密码
# -c 创建加密文件

至此登录信息创建完毕,将验证信息写入网站配置文件中:

auth_basic "Please enter your username and password.";
auth_basic_user_file /usr/local/nginx/password;

实现效果如图所示:

该实现是一个最基本的验证功能,它有一个缺陷在于你无法设定登录过期时间,也就是说某台设备一次登录成功后它将一直保持登录成功状态,直到网站cache被使用者手动清除。

配置完成后,重启nginx服务器生效。

代理服务器与应用服务器之间添加白名单

这部分实现很简单粗暴,直接在应用服务器上(Nginx)过滤除代理服务器以外的所有请求即可,添加如下配置。

# Whitelist
allow zzz.zzz.zzz.zzz; # 反向代理服务器的地址
deny all;

allow关键字后跟的ip以外的请求会全部deny掉。

尾声

至此,全部实现的介绍完毕。

03-05 22:04