配置 Nginx 子域名的泛解析

简单记录:

需求描述

Web 服务器为 Nginx,希望配置泛子域名解析。 其实稍加修改,配置泛域名解析也不是难事。

  • 不影响主域名,domain.com 和 www.domain.com 的 root 依然为 www 目录
  • 子域名 sub.domain.com 的 root 希望放在 www-sub 目录下,其他子域名同理

解决及分析

在 Nginx 的配置文件中做如下配置(示意):

    server {
        server_name
            domain.com
            www.domain.com
            *.domain.com
        ;
 
        set $subdomain '';
        if ($host ~* (\b(?!www\b).+)\.domain.com) {
            set $subdomain -$1;
        }
 
        root    /home/user/www$subdomain/;
    }

解释:

首先,在定义 server_name 时使用通配符 * ,使 Nginx 接受任意子域名的访问。

然后,对 $host 进行分析,找到子域名的名称。 这个正则表达式是在网上抄的,目的是为了在匹配子域名的同时, 不匹配 www 开头的访问和不带 www 的访问。 前面的 set 是因为 Nginx 的 If 没有 Else,所以默认先将 $subdomain 置空。

最后,在定义 root 的时候,使用 $subdomain 变量。

由一个错误学到的一些php安全配置问题

错误

在MediaTemple主机从(dv)3.0升级到3.5之后,我遇到的第一个问题就是一个莫名奇妙的php错误:

[Sat Jul 12 04:51:27 2008] [error] [client 121.42.26.81] PHP Warning:  require_once(/var/www/vhosts/fwolf.com/include/fwolflib/func/config.php) [<a href='function.require-once'>function.require-once</a>]: failed to open stream: Operation not permitted in /var/www/vhosts/fwolf.com/httpdocs/info.php on line 4
[Sat Jul 12 04:51:27 2008] [error] [client 121.42.26.81] PHP Fatal error:  require_once() [<a href='function.require'>function.require</a>]: Failed opening required '/var/www/vhosts/fwolf.com/include/fwolflib/func/config.php' (include_path='.:/var/www/vhosts/fwolf.com/include') in /var/www/vhosts/fwolf.com/httpdocs/info.php on line 4

因为是migration过来的,所以require的这个文件肯定存在,并且apache用户也的确有权访问,那问题出在哪里呢?

如果换一种方式,require直接使用文件的全路径,错误信息就更清楚了:

<b>Warning</b>:  require_once() [<a href='function.require-once'>function.require-once</a>]: open_basedir restriction in effect. File(/var/www/vhosts/fwolf.com/include/fwolflib/func/config.php) is not within the allowed path(s): (/var/www/vhosts/fwolf.com/httpdocs:/tmp) in <b>/var/www/vhosts/fwolf.com/httpdocs/info.php</b> on line <b>4</b><br />
<br />
<b>Warning</b>:  require_once(/var/www/vhosts/fwolf.com/include/fwolflib/func/config.php) [<a href='function.require-once'>function.require-once</a>]: failed to open stream: Operation not permitted in <b>/var/www/vhosts/fwolf.com/httpdocs/info.php</b> on line <b>4</b><br />

<br />
<b>Fatal error</b>:  require_once() [<a href='function.require'>function.require</a>]: Failed opening required '/var/www/vhosts/fwolf.com/include/fwolflib/func/config.php' (include_path='.:/var/www/vhosts/fwolf.com/include') in <b>/var/www/vhosts/fwolf.com/httpdocs/info.php</b> on line <b>4</b><br />

原来是有个open_basedir限制,找了一下是在$HOME/conf/httpd.include里,这个文件是由plesk自动维护的:

<IfModule sapi_apache2.c>
    php_admin_flag engine on
    php_admin_flag safe_mode off
    php_admin_value open_basedir "/var/www/vhosts/fwolf.com/httpdocs:/tmp"
</IfModule>
<IfModule mod_php5.c>
    php_admin_flag engine on
    php_admin_flag safe_mode off
    php_admin_value open_basedir "/var/www/vhosts/fwolf.com/httpdocs:/tmp"
</IfModule>

看到没,只允许包含httpdocs下的文件。open_basedir影响的范围是fopen, require, include之类的函数,在一定程度上加强了安全防护。

问题

但open_basedir也有局限性,它不会影响那些执行系统命令的函数,比如exec, system,如果我想偷主机上另外一位同学的文件(内容),也不见得非要去用require包含过来或者种个hack过去,直接system('cat /path/to/file')不是更省事么?

system函数有时候还是能派上正当用场的,直接禁用不是什么好办法,现在流行chroot,就是用户的/就是自己的$HOME,压根儿就访问不到别人的文件,什么open_basedir, exec, dl都不用禁用,我觉得这才是安全和方便的最佳接合点。

以前用(dv)3.0的时候,手工配置使用fastcgi的php5就是这样,每个用户的cgi用自己的身份,在自己的chroot环境下运行。

不过plesk现在的版本8、将来的版本9都没有要直接使用fastcgi解析php的打算,在“更远的计划里”,才可怜兮兮的有这么一句:

Use PHP via FastCGI rather than Apache module

参见:Parallels Summit 2008 – Day 1,所以就只能自己动手了。

fastcgi

很走运,找到了一个2天前刚刚出炉的脚本:Script for using php-cgi instead of mod_php,专门针对plesk,禁用掉mod_php,然后用它来配置fcgi解析。

使用环境:Plesk 8.X on Centos 5.X,依赖:

  • 禁用mod_php,开启mod_fcgid
  • python-curl, PyXML
  • php开启–enable-fastcgi, –enable-force-cgi-redirect

文件需要解压到/root/bin/下,自己一个子目录,幸好我也是用这个bin目录的。

然后在Server -> Control Panel -> Event Manager里添加自定义事件,在增加、修改、删除domain的时候,自动调用这个脚本。(subdomain的删除没有包含,手工删除文件就可以了)设置好大概就是这个样子:

using php-cgi instead of mod_php, plesk event manager

还要把/etc/httpd/conf.d/php.conf删得只剩一行:

LoadModule php5_module modules/libphp5.so

并且在/etc/httpd/conf.d/fcgid.conf里加一句:

PHP_Fix_Pathinfo_Enable 1

不过,这种方法作到一半我就没有继续了,因为我想起来前几天一位朋友和我提到过的suPHP。

suPHP

个人感觉suPHP是最“正统”的解决方案,它是以文件属主用户的身份来运行,正好使用各个用户的权限实现访问限制。

没找到centos的mod_suphp包,只好下载suphp 0.6.3源码自己编译,不过之前要先修改src/apache2/mod_suphp.c,在324行替换掉两行内容:

//AP_INIT_ITERATE("suPHP_AddHandler", suphp_handle_cmd_add_handler, NULL, ACCESS_CONF, "Tells mod_suphp to handle these MIME-types"),
AP_INIT_ITERATE("suPHP_AddHandler", suphp_handle_cmd_add_handler, NULL, RSRC_CONF | ACCESS_CONF, "Tells mod_suphp to handle these MIME-types"),
//AP_INIT_ITERATE("suPHP_RemoveHandler", suphp_handle_cmd_remove_handler, NULL, ACCESS_CONF, "Tells mod_suphp not to handle these MIME-types"),
AP_INIT_ITERATE("suPHP_RemoveHandler", suphp_handle_cmd_remove_handler, NULL, RSRC_CONF | ACCESS_CONF, "Tells mod_suphp not to handle these MIME-types"),

然后就是编译安装那三板斧:

# ./configure\
--with-apxs=/usr/sbin/apxs\
--with-php=/usr/bin/php-cgi\
--with-logfile=/var/log/suphp.log\
--with-min-uid=10000\
--with-min-gid=10000\
--with-apache-user=apache\
--with-apr=/usr/bin/apr-1-config\
--with-setid-mode=owner\
--prefix=/usr\
--sysconfdir=/etc
# make
# make install

/etc/httpd/conf/httpd.conf中加入一句(这一句也可以放到后面的suphp.conf中):

LoadModule suphp_module modules/mod_suphp.so

关闭safe_mode,并且注释掉下面两句:

safe_mode = Off
#AddType application/x-httpd-php .php
#AddType application/x-httpd-php-source .phps

创建suphp的conf文件,使用源码中的conf文件模板:

# cp doc/suphp.conf-example /etc/httpd/conf.d/suphp.conf

修改之:

<Directory /var/www/vhosts>
    RemoveHandler x-httpd-php
#   php_admin_value engine off
    AddHandler x-httpd-php .php .php3 .php4 .php5
    suPHP_AddHandler x-httpd-php
    suPHP_Engine On
    suPHP_ConfigPath /etc/php.ini
</Directory>

禁用mod_php,把php.conf文件换一个扩展名就行了:

# cd /etc/httpd/conf.d
# mv php.conf php.conf.bak

创建suPHP的配置文件/etc/suphp.conf,这个文件和用于apache配置的conf是不一样的,其内容如下,可根据具体环境设定参数:

[global]
;Path to logfile
logfile=/var/log/suphp.log

;Loglevel
;loglevel=info
;info, warn, error
loglevel=warn

;User Apache is running as
;webserver_user=wwwrun
webserver_user=apache

;Path all scripts have to be in
docroot=/var/www/vhosts/

;Path to chroot() to before executing script
;chroot=/mychroot

; Security options
;allow_file_group_writeable=false
allow_file_group_writeable=true
;allow_file_others_writeable=false
allow_file_others_writeable=true
;allow_directory_group_writeable=false
allow_directory_group_writeable=true
;allow_directory_others_writeable=false
allow_directory_others_writeable=true

;Check wheter script is within DOCUMENT_ROOT
check_vhost_docroot=true

;Send minor error messages to browser
;errors_to_browser=false
errors_to_browser=true

;PATH environment variable
env_path=/bin:/usr/bin

;Umask to set, specify in octal notation
;umask=0077
umask=0022

; Minimum UID
;min_uid=100
min_uid=10000

; Minimum GID
;min_gid=100
; Consider of psacln, psaserv
min_gid=200

; Use correct permissions for mod_userdir sites
;handle_userdir=true


[handlers]
;Handler for php-scripts
;x-httpd-php=php:/usr/bin/php
x-httpd-php=php:/usr/bin/php-cgi

;Handler for CGI-scripts
x-suphp-cgi=execute:!self

现在,重启apache,就可以啦!如果发现返回空页面,并且错误log中有如下内容:

Premature end of script headers:

那有可能是因为你把cli模式的php可执行文件拿过来当cgi模式的用了,注意他们的区别:

# php -v
PHP 5.2.6 (cli) (built: May  2 2008 16:06:40) 

# php-cgi -v
PHP 5.2.6 (cgi-fcgi) (built: May  2 2008 16:01:17)

把正确的cgi模式php执行文件设定到/etc/suphp.conf中即可。

chroot的疑惑

由于以前为了安全,ssh权限都是限定在chroot环境下,这样用户无法访问自己$HOME之外的内容。使用了suPHP之后,虽然php文件是以用户身份运行的,但却不是chroot的环境。也就是说,“理论上”在php文件执行的时候,可以访问其他用户的文件,这不也是个安全隐患么?

为了这个问题,我翻阅了好多资料,却发现很少人提起这个东西,suPHP安装不复杂,介绍的也不少,就是没有和chroot搭配的,倒是有人提出和fastcgi搭配使用。后来和michael沟通后才突然醒悟,suPHP的伪装身份和chroot是两种机制,之间没有什么联系,所以也就不存在什么配套使用的问题。至于不想让用户访问别人的文件,完全可以通过设定文件权限来实现嘛,不过还是要在安全方面比以前更加留心:

  • $HOME下系统自动创建的目录,一般属主都是user:psaserv或者root:root,有些对所有人都有rx权限(755),有些则是750权限,私密文件不要往755权限的目录下放。这些目录一般不宜改为750权限,因为有些文件是其他系统服务需要读取的。
  • $HOME下httpdocs, private等目录默认就是禁止所有人访问的,保持这样不要更改,并且httpdocs下的文件你就是搞成777权限,别人也访问不到。
  • 用户自建的文件、目录一般为user:psacln权限,主机上所有host用户所属组都是psacln,所以如果不想让别人访问,又没有上级目录的权限限制的话,一定调整为700权限。
  • 为了使用更方便,可以把$HOME目录的属主设为用户本身,比如chown fwolf:psaserv /var/www/vhosts/fwolf.com,不过这就需要一个个的单独开通了。
  • 如果发现其他系统文件中有泄密的,或者其他用户没有设置好权限,存在安全隐患,请及时告诉我或者相应用户,这样我们才是和谐的一家人嘛 🙂

取消chroot,还有一个好处就是用户几乎能够使用主机上的所有命令了,不像以前那样用哪个就需要把哪个设置到chroot的jail中,方便多了。

chroot的取消不是自动的,我已经给所有用户加上了可指定/bin/bash作为登录shell的权限,用户在plesk的站点设置中,把ssh用户的登录更换为/bin/bash即可,当然如果对安全没有信心,觉得chroot也够用的用户可以保留。

其他

suphp比suexec(就是原来dv3.0升php5的方法)要快一点;比suphp更快的还有suphp_mod_php;再快一些的是mpm-peruser,不过安装配置的麻烦程度也随之递增。

相比而言,suPHP速度还算可以接受(对于负载不是很大的站),配置方便,不用修改每个virtualhost的参数(就是$HOME/conf/vhost.conf),直接改apache的总conf就ok了,当然也比上面fastcgi方式下用event触发脚本来实现更加简洁。

参考

生成用于web服务器的openssl证书

启用https,就一定要有ssl证书,MT主机上有一个默认的证书,但是签署给plesk这个域名的,并且没有根证书认证,所以,自己搞个CA,给自己签个假证书用吧,至少好看些。

说实话,关于证书这些我也是一知半解,以前弄过apache的ssl,但那只是简单的处理,没有CA什么的,这次我上网搜集了不少资料,走了一个算是更“高级”一点的方式吧,不过出来的证书使用起来没有大差别。

注:所有操作在/big2/tools/ca下进行。

准备根证书

准备一些空目录和文件,作用如下:

  • certs/ 保存颁发的所有证书的副本
  • index.txt 跟踪已颁发的证书,初始为空
  • openssl.cnf openssl和根证书的配置文件
  • private/ CA证书的私钥
  • serial 最后一次颁发的证书的序列号,初始值01,也可以是00等其它值

openssl.cnf内容如下,我一气儿弄了10年的有效期:

[ ca ]
default_ca = FwolfCA

[ FwolfCA ]
dir = /big2/tools/ca
certificate = $dir/cacert.pem
database = $dir/index.txt
new_certs_dir = $dir/certs
private_key = $dir/private/cakey.pem
serial = $dir/serial

default_crl_days= 7
default_days = 3650
default_md = sha1

policy = FwolfCA_policy
x509_extensions = certificate_extensions

[ FwolfCA_policy ]
commonName = supplied
stateOrProvinceName = supplied
stateOrProvinceName = supplied
countryName = supplied
emailAddress = supplied
organizationName= supplied
organizationalUnitName = optional

[ certificate_extensions ]
basicConstraints= CA:false

# 下面是根证书的配置信息

[ req ]
default_bits = 4096
default_keyfile = /big2/tools/ca/private/cakey.pem
default_md = sha1
prompt = no
distinguished_name = root_ca_distinguished_name
x509_extensions = root_ca_extensions

[ root_ca_distinguished_name ]
commonName = Fwolf CA
stateOrProvinceName = The Earth
# countryName只能是两位字母
countryName = CN
emailAddress = one_mail_of_fwolf@gmail.com
#organizationName = Root Certification Authority
organizationName = Fwolf CA Root
[ root_ca_extensions ]
basicConstraints = CA:true

然后生成根证书:

$ openssl req -x509 -newkey rsa:4096 -out cacert.pem -outform PEM -days 3650 -config openssl.cnf

会提示输入密码以及确认密码。生成好以后可以验证一下(说是验证,其实就是看看内容):

$ openssl x509 -in cacert.pem -text -noout

给自己颁发证书

$ openssl req -newkey rsa:4096 -keyout office.fwolf.com.key.pem -keyform PEM -out   office.fwolf.com.req.pem -outform PEM -sha1

按提示输入两次密码,然后输入几项证书信息,注意其中organizationName必须输入,并且Common Name要和域名一致,比如:

Common Name (eg, YOUR name) []:*.fwolf.com

就生成了私钥key文件和请求req文件,然后把req文件提交给CA根证书签署(盖章):

$ openssl ca -in office.fwolf.com.req.pem -config openssl.cnf

输入根证书的密码,就会在certs/目录下生成.pem证书文件,文件名以serial中的序号开头,信息会存储在index.txt中。

这样生成的证书,在apache中配置需要两条语句,分别指定证书和私钥:

SSLEngine On
SSLCertificateFile /big2/tools/ca/certs/office.fwolf.com.cert.pem
SSLCertificateKeyFile /big2/tools/ca/certs/office.fwolf.com.key.pem

其实这两个文件是可以合并为一个文件的:

$ cat office.fwolf.com.key.pem office.fwolf.com.cert.pem > office.fwolf.com.pem

然后在配置apache的时候就只需要一句了:

SSLEngine On
SSLCertificateFile /big2/tools/ca/certs/office.fwolf.com.pem

其它

去掉证书的口令

现在证书基本上就可以使用了,再返回来说一个问题,就是在启动apache的时候会提示输入私钥的口令,要想去掉这个(一般都不会喜欢这样的),就要求在生成私钥的时候不要设置口令:

$ openssl req -newkey rsa:4096 -keyout office.fwolf.com.key.pem -keyform PEM -out   office.fwolf.com.req.pem -outform PEM -sha1 -nodes

生成根证书的时候还是建议带上个口令,提高安全性。

index.txt

另外,如果要清空index.txt的话,一定要清空到字节0,里面有一个字节都会导致openssl ca错误:

wrong number of fields on line 1 (looking for field 6, got 1, '' left)

证书吊销

$ openssl ca -revoke office.fwolf.com.cert.pem
# 生成CRL列表 $ openssl ca -gencrl -out exampleca.crl # 查看CRL列表信息 $ openssl crl -in exampleca.crl -text -noout # 验证CRL列表签名信息 $ openssl crl -in exampleca.crl -noout -CAfile cacert.pem 可以看到CRL的版本号为1,这是OpenSSL默认的,除非crl_extensions被指定在配置文件ca一节中。

上传到MT主机上应用

首先在Server > Certificates >Add New Certificate,填上Add New Certificate(自己起),然后选下面的Private key私钥文件和Certificate证书文件上传,就存到服务器上了。

然后返回证书列表,选中新上传证书前面的复选框,点上面的链接Make default for Web sites设置为网站默认证书,也可以通过Secure control panel将其设置为控制面板所使用的证书。

还没完,证书还得加到ip上才能生效,Server > IP Addresses >中修改ip地址属性,在SSL Certificate中选择刚才上传的证书,保存,就立刻生效了。

基于ssl/https协议的特性,一个ip上只能使用一个证书,所以合租用户是无法自己上传或者选择其它证书的,不过我现在使用的证书是签给“*”的,也就是所有域名都可以使用,“好看”一点。让我不理解的是,如果在访问一个域名时同意了这个证书,在访问另外一个域名的时候还得再同意一遍,也就是证书和域名是要配套使用的,和我原先的想法不太一致。

参考资料

HTTP–>HTTPS

我无法愤怒,因为不知道该生谁的气。

几天前,网站突然无法访问了,没有响应(不是reset),一开始还以为事网络的问题,后来发现不是,用其他国外主机作跳板可以正常访问,也就是说,在出国的网络出口被DROP了。

我和我们合租的邻居们不会有什么过激的言论,也没有什么PORN的内容,咋就这样了呢。没有客服,原因也没法找了,只能先恢复访问,向MT提交了support request,很快(33分钟后)就得到了的IP,应用在主机上,谁知才2天,又完蛋了。

现在要了第3个IP(这次的响应速度更快,12分钟),同时准备把所有http访问重定向到https访问,算是有些强制吧,弄了个脚本定时执行。没办法,没有别的招数,如果再被封,我也回天无术了。

使用https方式访问网站,速度上没有太大的影响(会有一点点的,服务端加密和客户端解密要耗时),关键事客户端会弹出一个非信任证书的提示,接受或者同意或者永久同意即可,FF3会出一个提示页,上面也有按钮可以添加证书为永久信任证书。如果我们每个域名都单独购买证书的话也不是不可以,不过费用不菲。

http到https的导向其实很容易,用rewrite:

RewriteCond %{HTTPS} !on [NC]
RewriteRule (.*) https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

为了使用方便,专门搞了个小脚本,在每个网站的根下添加.htaccess文件,在所有子目录现有的.htaccess文件中添加/更新这段内容。

和wordpress配合使用的时候,遇到了一些麻烦,浪费了我2天2夜的时间,也许还浪费了1个IP,如果早弄好也许第2个IP就不会出事了。这个问题就是,rewrite后重定向到https又会被重定向回http,把rewriterule仔细得看过来看过去也没找到错误,后来发现问题在wordpress上,只要在设置中把2个网站地址更改为https开头就行了。原先的转向死循环就是因为rewriterule把http转向到了https,然后wordpress发现访问地址和网站地址不一样,又给转向回了http。

如果无法进入wordpress的管理界面,也可以直接更改数据库,表wp_options中,option_name为siteurl和home这两项,option_id分别是1和46。

再次赞 (MT)的服务,鄙视国内的网络侦探。

参考

好像其他人也有类似遭遇

Update @ 2008-06-26

取消了强制https,原因一来感觉https消耗的资源和影响的速度比预想中的要多,二来强制也只是相对的,有点知识的就能够绕开,三来没有证书(即使购买了证书也不可能所有域名合用),麻烦多多,FF里有吓人的警告,IE下要狂点确认,很多工具也不支持https,四来取消了强制并不代表https不可用,革命靠自觉,感觉有危险的时候用户自己就知道用https了。

mod_fcgid出现500错误的解决

MT上的php5是用mod_fcgid方式运行的,但运行一段时间之后,有时候会莫名其妙的出现500错误,也看不出是哪个php程序错误,不过html文件倒能正常解析,查看用户的$HOME/statistics/logs/error_log文件,会发现大量这样的内容:

[Fri Aug 10 01:00:43 2007] [error] [client 202.108.23.76] Premature end of scrip
t headers: index.php
[Fri Aug 10 01:01:20 2007] [error] [client 60.191.82.228] Premature end of scrip
t headers: index.php

而在系统的log文件/var/log/apache/error_log中,是这样的错误:

[Fri Aug 10 01:03:05 2007] [notice] mod_fcgid: process /var/www/vhosts/fwolf.com
/httpdocs/blog/index.php(21666) exit(communication error), terminated by calling
 exit(), return code: 120

G到一些资料,第一类错误是脚本执行被中断,没有返回完整的http header;第二类错误是cgi程序的执行被强行中止。根本原因是fastcgi会自动kill掉一些发呆的、长时间没有响应的进程(要不它比较快,比较省内存啊),但是fastcgi的默认idle时间设置过于苛刻(官方文档中每个选项都有默认的时间值),所以有些处理比较慢、需要调用外部资源的程序就有可能被误杀,所以先略微调整一下,继续观察:

<IfModule mod_fcgid.c>
    IdleTimeout 600
    ProcessLifeTime 3600
    MaxProcessCount 8
    DefaultMinClassProcessCount 3
    DefaultMaxClassProcessCount 3
    IPCConnectTimeout 30
    IPCCommTimeout 600
    #MaxRequestsPerProcess 500
</IfModule>
  • IdleTimeout 发呆时限
  • ProcessLifeTime 一个进程的最长生命周期,过期之后无条件kill
  • MaxProcessCount 最大进程个数
  • DefaultMinClassProcessCount 每个程序启动的最小进程个数
  • DefaultMaxClassProcessCount 每个程序启动的最大进程个数
  • IPCConnectTimeout 程序响应超时时间
  • IPCCommTimeout 与程序通讯的最长时间,上面的错误有可能就是这个值设置过小造成的
  • MaxRequestsPerProcess 每个进程最多完成处理个数,达成后自杀,因为PHP最多只处理500次请求。不过这个是mod_fcgid 1.11版本添加的,我们主机上暂时不支持。

上述选项的确切作用我也不是十分清楚,先用着这个设置,再根据情况调整。

参考:

升级MT dv 3.0主机到php5

MT主机dv 3.0上的php默认是4,现在已经是php5时代了,不知道MT什么时候默认给配php5,现阶段官方的方式仍然是由用户自行升级,详细步骤见kb中的Upgrade to PHP5 on (dv) 3.0 Dedicated Virtual Servers

推荐使用suexec方式,这样每个用户执行cgi使用的都是自己的身份,即安全也便于管理。suexec要求的前提条件有三个,就是用户的主机设置开启如下三个选项:

  • PHP支持打开,安全模式safe_mode关闭。
  • CGI支持打开。
  • FastCGI支持打开。

具体步骤说起来也是很简单的,首先把/opt/php51下的php5执行文件拷贝到用户目录下,并设置相应权限:

cp /opt/php51/cgi-bin/php5 /var/www/vhosts/<domain>/bin/
cd /var/www/vhosts/<domain>/
chown -R <domain-user>:psacln bin/

其中<domain>是用户的域名,<domain-user>是用户的ssh/ftp用户名,然后编辑/var/www/vhosts/<domain>/conf/vhost.conf文件,添加如下内容:

AddHandler fcgid-script .php .php5
SuexecUserGroup <domain-user> psacln
<Directory /var/www/vhosts/<domain>/httpdocs>
     FCGIWrapper /var/www/vhosts/<domain>/bin/php5 .php
     FCGIWrapper /var/www/vhosts/<domain>/bin/php5 .php5
     Options ExecCGI
     allow from all
</Directory>

如果是https站点,除了httpdocs换成httpsdocs之外,配置文件名称为vhost_ssl.conf。最后启用设置:

/usr/local/psa/admin/sbin/websrvmng -a -v

就可以了,我们主机上现在的php5版本是5.1.4,还是不够新:)。

为了方便操作,自己写了个小脚本:

#! /bin/bash
# Automatic enable php5 support for mediatemple user(via suexec).

# Pre-prepare in plesk:
#   php support is checked, safe_mod 'on' is UNCHECKED
#   cgi support is checked.
#   fastcgi support is checked.

# Check username, must run with root
WHOAMI=`whoami`
if [ ! $WHOAMI == 'root' ]; then
    echo "This script must run with root."
    exit 1
fi

# Check parameters
if [ $# -lt 2 ];then
    echo "Usage: `basename $0` ssh_user domain"
    exit 1
fi
USER=$1
DOMAIN=$2

# If given a wrong domain, exit
if [ ! -d /var/www/vhosts/$DOMAIN ]; then
    echo "Domain doesn't exists."
    exit 1
fi

# Copy php5 files
cp /opt/php51/cgi-bin/php5 /var/www/vhosts/$DOMAIN/bin/
cd /var/www/vhosts/$DOMAIN/
chown -R $USER:psacln bin/
cd conf

BEGIN="###_87b4e7fc -- Add by enable_php script, DON'T modify this section --"
END="# -- Enable_php modify section end -- 87b4e7fc_###"

# If vhost.conf doesn't exists, create it with default content
if [ -z vhost.conf ]; then
    echo "\\
$BEGIN\\
$END\\
" >> vhost.conf
else
    MARK=`grep "###_87b4e7fc" vhost.conf|wc -l`
    if [ $MARK -eq 0 ]; then
        # If vhost.conf exists & not mark, add replace mark
        echo "\\
$BEGIN\\
$END\\
" >> vhost.conf
    fi
fi
# Vhost_ssl.conf
if [ -z vhost_ssl.conf ]; then
    echo "\\
$BEGIN\\
$END\\
" >> vhost_ssl.conf
else
    MARK=`grep "###_87b4e7fc" vhost_ssl.conf|wc -l`
    if [ $MARK -eq 0 ]; then
        # If vhost.conf exists & not mark, add replace mark
        echo "\\
$BEGIN\\
$END\\
" >> vhost_ssl.conf
    fi
fi


STR="$BEGIN\\n\\
AddHandler fcgid-script .php .php5\\n\\
SuexecUserGroup $USER psacln\\n\\
<Directory \\/var\\/www\\/vhosts\\/$DOMAIN\\/httpdocs>\\n\\
    FCGIWrapper \\/var\\/www\\/vhosts\\/$DOMAIN\\/bin\\/php5 .php\\n\\
    FCGIWrapper \\/var\\/www\\/vhosts\\/$DOMAIN\\/bin\\/php5 .php5\\n\\
    Options \\+ExecCGI\\n\\
    allow from all\\n\\
<\\/Directory>\\n\\
<Directory \\/var\\/www\\/vhosts\\/$DOMAIN\\/httpsdocs>\\n\\
    FCGIWrapper \\/var\\/www\\/vhosts\\/$DOMAIN\\/bin\\/php5 .php\\n\\
    FCGIWrapper \\/var\\/www\\/vhosts\\/$DOMAIN\\/bin\\/php5 .php5\\n\\
    Options \\+ExecCGI\\n\\
    allow from all\\n\\
<\\/Directory>\\n\\
$END"

# Backup original configure
cp vhost.conf vhost.conf.`date +"%Y%m%d-%H%M%S"`
cp vhost_ssl.conf vhost_ssl.conf.`date +"%Y%m%d-%H%M%S"`

# Write configure information
sed -i -e ":begin; /###_87b4e7fc/,/87b4e7fc_###/ { /87b4e7fc_###/! { $! { N; b begin }; }; s/###_87b4e7fc.*87b4e7fc_###/$STR/g };" vhost.conf
sed -i -e ":begin; /###_87b4e7fc/,/87b4e7fc_###/ { /87b4e7fc_###/! { $! { N; b begin }; }; s/###_87b4e7fc.*87b4e7fc_###/$STR/g };" vhost_ssl.conf

# Active new configure
/usr/local/psa/admin/sbin/websrvmng -a -v

使用方法,用root用户执行:

enable_php5 <domain-user> <domain>

不支持subdomain的设置,其中sed替换多行内容的用法,可以参考我写的另外一篇文章用sed替换跨行内容

conf/vhost.conf这个文件存在之后,并不会自动被apache调用,执行/usr/local/psa/admin/sbin/websrvmng -a -v可以自动在conf/httpd.include文件中include vhost.conf了。而conf/httpd.include这个文件是由plesk维护的,用户不要直接修改它,会被plesk覆盖掉。

另外由于使用的是suexec方式执行,所以上传目录、cache等以前需要设置apache用户有可写权限的文件,现在要把owner设置为<domain-user>:psacln才行,和用户的其他文件权限一样就可以了,是不是管理起来更方便一些?

看了一下phpinfo,自动加载了位于/opt/php51/lib/php5/extensions/下的如下模块:

curl.so  gd.so        mysql.so    pdo.so        sockets.so  zlib.so
dom.so   iconv.so     mysqli.so   pdo_mysql.so  sqlite.so
ftp.so   mbstring.so  openssl.so  posix.so      xsl.so

基本上常用的都有了,但不知道/opt/php51这个目录下的内容MT什么时候给更新,还是说需要用户自己更新?

参考