把玩Nginx


最近在做服务器横向比较的一些资料,其中包括Nginx,也恰好有个朋友在学习部署Nginx,于是深入的把玩了一下Nginx。不过我比较懒...,不太想换到linux环境下,其实Windows下还是有办法搞的,网上资料也很多。要在Windows环境模拟PHP-FPM的话,需要自己动手写个程序监控php-cgi.exe的进程,只要解决这个问题,就能确保Windows环境下的php-cgi.exe的持久运行。
 
虽然说我一直不太喜欢Nginx,不过不得不佩服,Nginx的Windows版本是静态编译,只有一个EXE文件,没有其他动态库,而Nginx的主要功能和扩展都集成与此,不得不让我等怪癖之辈心生怜爱之心...呃,5MB,Windows版本的Lighttpd都要10MB,还是动态加载扩展。
 
话说Nginx的Rewrite,搞得我蛋疼了半天,终于摸清门道了,其实问题存在于他默认提供的那个配置文件(默认配置害人啊,真要不得)。
 
001
#user  nobody;
002
worker_processes  1;
003
 
004
#error_log  logs/error.log;
005
#error_log  logs/error.log  notice;
006
#error_log  logs/error.log  info;
007
 
008
#pid        logs/nginx.pid;
009
 
010
 
011
events {
012
    worker_connections  1024;
013
}
014
 
015
 
016
http {
017
    include       mime.types;
018
    default_type  application/octet-stream;
019
 
020
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
021
    #                  '$status $body_bytes_sent "$http_referer" '
022
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
023
 
024
    #access_log  logs/access.log  main;
025
 
026
    sendfile        on;
027
    #tcp_nopush     on;
028
 
029
    #keepalive_timeout  0;
030
    keepalive_timeout  65;
031
 
032
    #gzip  on;
033
 
034
    server {
035
        listen       9090;
036
        server_name  localhost;
037
 
038
        #charset koi8-r;
039
 
040
        #access_log  logs/host.access.log  main;
041
 
042
        location / {
043
            root   html;
044
            index  index.html index.htm;
045
        }
046
 
047
        #error_page  404              /404.html;
048
 
049
        # redirect server error pages to the static page /50x.html
050
        #
051
        error_page   500 502 503 504  /50x.html;
052
        location = /50x.html {
053
            root   html;
054
        }
055
 
056
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
057
        #
058
        #location ~ \.php$ {
059
        #    proxy_pass   http://127.0.0.1;
060
        #}
061
 
062
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
063
        #
064
        location ~ \.php$ {
065
            root           html;
066
            fastcgi_pass   127.0.0.1:9000;
067
            fastcgi_index  index.php;
068
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
069
            include        fastcgi_params;
070
        }
071
 
072
        # deny access to .htaccess files, if Apache's document root
073
        # concurs with nginx's one
074
        #
075
        #location ~ /\.ht {
076
        #    deny  all;
077
        #}
078
    }
079
 
080
 
081
    # another virtual host using mix of IP-, name-, and port-based configuration
082
    #
083
    #server {
084
    #    listen       8000;
085
    #    listen       somename:8080;
086
    #    server_name  somename  alias  another.alias;
087
 
088
    #    location / {
089
    #        root   html;
090
    #        index  index.html index.htm;
091
    #    }
092
    #}
093
 
094
 
095
    # HTTPS server
096
    #
097
    #server {
098
    #    listen       443;
099
    #    server_name  localhost;
100
 
101
    #    ssl                  on;
102
    #    ssl_certificate      cert.pem;
103
    #    ssl_certificate_key  cert.key;
104
 
105
    #    ssl_session_timeout  5m;
106
 
107
    #    ssl_protocols  SSLv2 SSLv3 TLSv1;
108
    #    ssl_ciphers  HIGH:!aNULL:!MD5;
109
    #    ssl_prefer_server_ciphers   on;
110
 
111
    #    location / {
112
    #        root   html;
113
    #        index  index.html index.htm;
114
    #    }
115
    #}
116
 
117
}
这是Nginx默认自带的conf文件,取Server这段,location / {} 默认指向的是html,就是nginx所在目录下的html目录。其实这个默认配置最大的问题,没有声明全局的documentRoot。假如你按照这个配置,再添加一个location,比如:
1
location /hello_world {
2
    if (-f $request_filename) {
3
        break;
4
    }
5
    if (!-e $request_filename) {
6
        rewrite . /hello_world/index.php last;
7
    }
8
}
怪事就来了,如果访问不存在的文件,会匹配!-e $request_filename这条规则,即重定向到/hello_world/index.php。
 
但是,但是,在/hello_world前面是什么呢?如前所述,由于没有全局的DocumentRoot,所以他会自己默认认为DocumentRoot是nginx所在的那个html,比如D:\nginx\html(他是不会找location / {}里面设定的root的),于是重定向的全路径是D:\nginx\html\hello_world\index.php,于是不断的报404错误,这个问题....(我的版本是1.0.6,不晓得新版本的默认配置是否有作出调整)。
 
由于对lighttpd已经十分十分熟悉了,两者可以做一个比较(非性能对比,这个文章太多),主要基于配置、管理方面。
 
lighttpd配置方面,好像写php脚本,大量的[]和{},一个不小心漏一个,就够你流汗半天,咋服务器起不来呢?MD原来是个逗号。
 
lighttpd配置貌似不支持include的模式,但却支持嵌入lua脚本。比如,如果你想写这种!-e $request_filename的配置,需要自己倒腾一个lua脚本,说他好,也好,用lua,你可以干更多的事情,说不好,也不好,因为其实说明白了,rewrtie的事情就那么多,好端端多一个lua,没事找事。
 
相比之下,Nginx可以支持include全目录的下的文件,而且他的配置写起来,更容易发现纰漏(分号和逗号,会有很大差异)。而且nginx的配置的内容更加精简,这是件好事。
 
其实无论lighttpd还是nginx,真的要作为发布服务器使用,首先第一件事,就是...编译,废话。编译完的第一件事,就是从0开始写配置文件。事实上这是确保服务器安全和稳健的必要条件,lighttpd的配置,我都是从第一行开始写起的(当然,部分会粘贴),确保清楚、了解、熟知每一行配置所要做的事情和其背景,不开放和应用无关的任何组件和扩展。如果条件允许,在手动编译的过程,不妨去了解清楚每一个编译的依赖库都是用于什么功能的,比如,我还记得当年很认真的查询过什么叫做stat-cache-fam。
 
其实我是十分不提倡通过yum或者apt-get的方式安装服务器的关键服务,只是呢,这话,我说了很多年,说了很多次,被视作耳旁风。尤其记得,当年安装公司某服务器的时候,老板走过来,看到configure过程,刷屏一样的字符,惊讶的问道,你装的不是Ubuntu吗?我说是啊。他接着问道:那为啥装个Apache要那么久?其实我挺无语的,当时没多想,只是觉得,终归自己编译的靠谱。结果,3年后,公司的服务器终于爆发大规模的漏洞,安全问题等等。其实,从年前至今,所有爆发的种种问题,可以归结到以下几点:
 
1、程序员开发程序,缺乏体系、规范,导致服务器被攻击或者被Hack,不是PHP或者Apache的漏洞所致,而是程序员开发的程序缺乏必要的检查。你可以说,是程序员的水平问题,或者说是素质问题。但是还是那句老话,如果站在项目角度看,这个人完成了项目,从功利角度,时间角度,宇宙角度来说,他是ok的。但是因为他老人家的疏忽、一时大意、赶工,什么这个那个的,他的上传文件忘记检查文件类型,于是被上传了一个perl脚本,然后服务器被hack了。我会认为,不要针对人吧,通过人来看事情,最终能说的,就是缺乏开发规范,代码缺乏审核机制。
 
2、过分倚重开源产品,这个有时候也真是很头疼的问题。倚重于某个开源核心库,还好些。比如wordpress,这种整合性的产品,千万别拿个人博客来这里说事,你该想想,如果一个公司超过20个wordpress的项目,一个版本号存在漏洞,你就慢慢升级吧,这就好像整人一样。尤其最近这一年,好像wordpress的漏洞一直没停过。
 
3、服务器管理经验不足。看一些国外的文章,常常埋怨:其实Apache本身的性能还是不错的,令人诟病的就是默认配置问题,尤其是linux下的默认配置,好像逗你玩似的。
 
其实无论是Nginx、Lighttpd、Apache,这些WebServer,都是出色的开源软件,问题不存在选择哪个平台,或者选择什么软件,完全在于使用者。便秘,你不能赖马桶太差了,厕所不够和谐。只是醉翁之意不在酒,你非说鹿是马,大家只好看你的笑话了。
 
使用Nginx作为WebServer,是比较理想的选择,不过也必须看到他的一些问题:
 
1、nginx的fastcgi处理的方式,还是不太理想,如Apache或者lighttpd,如果指定了某个目录的rewrite规则,会依照rewrite规则处理,就算你请求的后缀是.php。而nginx的话,如果你指定了location \.php以后,凡是请求中包含.php的,都会转交fastcgi来处理。如果文件不存在,会报no file input之类的错误,但是终归来说,对于服务器外部,还是暴露了一个可攻击的入口点。
 
2、nginx的配置特点是简,超级简,乃至有些弱,虽然允许你if,set,return,break,不过你别想循环嵌套。有点一根筋,不过倒让人很放心,这样的语义分析也简单。不过为了某个问题,可能会让你抓狂,实现起来,也不太优雅,比如上述的例子里面,假如我想限制upload目录里面的.php文件不被执行,你只能这样:
 
1
location ~ \.php {
2
        root   D:\\Development\\wwwroot;
3
        include         fastcgi_params;
4
        if ($uri !~ "/upload/") {
5
                fastcgi_pass    127.0.0.1:9000;
6
        }
7
        fastcgi_index   index.php;
8
        fastcgi_param   SCRIPT_FILENAME  $document_root$fastcgi_script_name;
9
}
可是就大多数的公司来说,怎么可能通过描述一个upload足够呢?可能存在各种不同的目录,这时候你可能得这样来写:
01
location ~ \.php {
02
        root   D:\\Development\\wwwroot;
03
        include         fastcgi_params;
04
        set $is_block 0;
05
        if ($uri !~ "/upload/") {
06
                set $is_block 1;
07
        }
08
        if ($uri !~ "/no_php/") {
09
                set $is_block 1;
10
        }
11
        # |||,这可得无限多啊
12
        if ($is_block = "1") {
13
                fastcgi_pass    127.0.0.1:9000;
14
        }
15
        fastcgi_index   index.php;
16
        fastcgi_param   SCRIPT_FILENAME  $document_root$fastcgi_script_name;
17
}
可能还有更好的写法吧!
结合他的这两个特点,有以下两点需要注意的:
 
1、默认的conf,不能设置.php的处理,具体的.php处理应该具体配置到每个vhost的配置文件上。因为有那么多的网站,每个网站都有不同的配置,你可别想在主的conf里面一次性写完,这真的有病。
 
2、除非像Wordpress这种,到处都是需要被执行的.php文件。使用MVC框架的项目,甚至应该集中允许具体某个、某几个文件是作为php处理的,其他的.php请求,注意,不是不处理,而是通过rewrite到指定的php文件来处理(下载就傻眼了)。
 
3、对于像wordpress这种,在配置vhost的php处理时,一定要注意,哪些是禁止运行php的地方,进行集中拦截:
 
01
location ~ \.php {
02
        root   D:\\Development\\wwwroot;
03
        include         fastcgi_params;
04
        if ($uri ~ "/hello_world/no_php/") {
05
                rewrite . /hello_world/index.php break;
06
        }
07
        fastcgi_pass    127.0.0.1:9000;
08
        fastcgi_index   index.php;
09
        fastcgi_param   SCRIPT_FILENAME  $document_root$fastcgi_script_name;
10
}
暂时想到那么多,嗯

摘自 曾建凯的博客

相关内容

    暂无相关文章