CentOS 7 从零安装 WHMCS 笔记

Anson 教程 33 次浏览 评论已关闭

记录下 WHMCS 安装步骤,服务器环境为 CentOS 7,从零部署 LNMP 网站环境,然后上传 WHMCS 安装,以及完成初始化配置和安全增强。

系统基本配置

# 设置东八区时间
timedatectl set-timezone 'Asia/Shanghai'

# 更改主机名
hostnamectl set-hostname webserver.localdomain

# 绑定主机名
cat > /etc/hosts << "EOF"
127.0.0.1   webserver.localdomain webserver localhost.localdomain localhost
::1         webserver.localdomain webserver localhost.localdomain localhost
EOF

# 更新软件包
yum -y update && reboot

# 安装常用软件
yum install -y wget unzip screen lrzsz

配置 LNMP 网站环境

LNMP 环境配置参考这篇文章 https://www.hostarr.com/install-lnmp-via-ius/

安装 Ioncube 插件

# 下载 Ioncube 并解压到当前目录
wget https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz
tar -zxvf ioncube_loaders_lin_x86-64.tar.gz

# 复制插件文件到 PHP 扩展目录(选择与 PHP 版本相符或接近的版本)
# 可通过 php -i | grep extension_dir 查询当前 PHP 扩展目录路径
cp -Z /root/ioncube/ioncube_loader_lin_7.4.so /usr/lib64/php/modules

# 赋予插件文件可执行权限
chmod +x /usr/lib64/php/modules/ioncube_loader_lin_7.4.so

# 创建配置文件让 PHP 加载插件(可通过 php -i | grep 'additional .ini files' 查询当前 PHP 配置文件目录)
echo "zend_extension=/usr/lib64/php/modules/ioncube_loader_lin_7.4.so" > /etc/php.d/00-ioncube.ini

# 删除之前操作留下的多余文件
rm -rf /root/ioncube /root/ioncube_loaders_lin_x86-64.tar.gz

# 重启服务生效
nginx -s reload
systemctl restart php-fpm

# 验证 Ioncube 有无安装成功
php -v

下载 WHMCS 和中文语言包

WHMCS 安装包 https://s3.amazonaws.com/releases.whmcs.com/v2/pkgs/whmcs-8.1.3-release.1.zip

WHMCS 语言包 https://github.com/kaneawk/WHMCS-CN-translations

创建数据库、上传 WHMCS 程序及完成安装

数据库创建在之前 LNMP 教程里有介绍。下面是对网站目录的一些操作。

# 设置网站目录 SELinux 安全上下文为只读
semanage fcontext -a -t httpd_sys_content_t "/var/www/example(/.*)?"
restorecon -R -v /var/www/example

# 上传 WHMCS 文件后重命名配置文件名
mv /var/www/example/configuration.php.new /var/www/example/configuration.php

# 将网站目录用户/组改为 nginx
chown -R nginx:nginx /var/www/example

# 为必要文件/目录设置 SELinux 读写安全上下文
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/example/configuration.php"
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/example/attachments(/.*)?"
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/example/downloads(/.*)?"
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/example/templates_c(/.*)?"
restorecon -R -v /var/www/example

之后访问 https://example.com/install/install.php 开始安装。

安装完成后删除根目录下的 install 目录,并将 configuration.php 安全上下文改回只读,之后便可以登录管理员后台。

# 删除安装目录
rm -rf /var/www/example/install

# 将配置文件改回只读权限
semanage fcontext -a -t httpd_sys_content_t "/var/www/example/configuration.php"
restorecon -R -v /var/www/example

WHMCS 安全和初始化配置

保护可写目录

将所有可写目录移动到网站根目录上级,以避免 Web 访问。先创建要使用的新目录(如下)。

# 创建新的可写存储目录
mkdir -p /var/www/whmcs_writeable/attachments /var/www/whmcs_writeable/attachments/projects /var/www/whmcs_writeable/downloads /var/www/whmcs_writeable/templates_c

# 设置 SELinux 读写安全上下文
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/whmcs_writeable(/.*)?"
restorecon -R -v /var/www/whmcs_writeable

# 将目录所有者改为 nginx 用户/组
chown -R nginx:nginx /var/www/whmcs_writeable

然后转到 WHMCS 后台 -> 配置 -> 系统配置 -> 存储设置,先点“配置”选项卡添加新的本地存储目录(不用添加 templates_c 目录),再切回“设置”选项卡选择使用新的目录。

接着修改 configuration.php 配置文件,设置使用新的 templates_c 目录。

# 设置新的 templates_c 目录
sed -i "s|'templates_c'|'/var/www/whmcs_writeable/templates_c'|" /var/www/example/configuration.php

# 删除之前目录的 SELinux 读写安全上下文
semanage fcontext -d "/var/www/example/attachments(/.*)?"
semanage fcontext -d "/var/www/example/downloads(/.*)?"
semanage fcontext -d "/var/www/example/templates_c(/.*)?"
restorecon -R -v /var/www/example

# 再删除旧有目录
rm -rf /var/www/example/attachments /var/www/example/downloads /var/www/example/templates_c

保护 WHMCS 配置文件

configuration.php 文件权限设为 400(仅允许文件所有者只读)。

chmod 400 /var/www/example/configuration.php

注:如果设置后遇到问题,可尝试设置 440,其次是 444。如果之后要更新 WHMCS 密钥,则先临时将文件权限设为 755,更新后再改回去。

移动 Crons 目录和设置 Cron 作业

为防止 Web 访问 Crons 目录,需将其移动到其它目录。先创建目录(如下)。

# 创建新的只读存储目录
mkdir -p /var/www/whmcs_readable

# 设置 SELinux 只读安全上下文
semanage fcontext -a -t httpd_sys_content_t "/var/www/whmcs_readable(/.*)?"
restorecon -R -v /var/www/whmcs_readable

# 将目录所有者改为 nginx 用户/组
chown -R nginx:nginx /var/www/whmcs_readable

之后移动 Crons 目录,并在 Crons 配置文件指定新的存放目录。

# 移动 Crons 目录
mv -Z /var/www/example/crons /var/www/whmcs_readable

# 指定 WHMCS 安装目录
cat > /var/www/whmcs_readable/crons/config.php << "EOF"
<?php

$whmcspath = '/var/www/example/';

EOF

# 为新文件设置 nginx 用户/组
chown nginx:nginx /var/www/whmcs_readable/crons/config.php

# 指定 Crons 存放目录
echo -e "\n\$crons_dir = '/var/www/whmcs_readable/crons/';" >> /var/www/example/configuration.php

注:在移动 Crons 目录后,后续如果手动升级 WHMCS,需要将新的 Crons 目录文件手动覆盖旧文件。

最后,为使 WHMCS 可以执行自动化任务(例如生成续订发票等),我们需要设置 Cron 作业。

# 添加 Cron 计划任务
crontab -e

# 设置每 5 分钟运行 Cron 作业
*/5 * * * * /usr/bin/php -q /var/www/whmcs_readable/crons/cron.php

更改 admin 目录名称

为避免被猜到后台登录地址,建议更改 admin 目录名称。

# 在配置文件指定新目录名称(名称仅限小写字母,数字,下划线、连字符)
echo -e "\n\$customadminpath = 'my_custom_folder_name';" >> /var/www/example/configuration.php

# 重命名目录名称
mv /var/www/example/admin /var/www/example/my_custom_folder_name

注:在更改 admin 目录名称后,如果更新了 WHMCS,需将新的 admin目录文件覆盖到新目录。

限制 vendor 目录访问

该目录不应提供 Web 访问,需要添加拒绝访问指令。

# 编辑 Nginx 站点配置文件
vi /etc/nginx/conf.d/example.conf

# 在 server 位置内添加拒绝访问指令
server {
    ...
    location ^~ /vendor/ {
        deny all;
        return 403;
    }
    ...
}

# 重新加载 Nginx 服务生效
nginx -s reload

WHMCS Nginx 配置模板

最后附上适用 WHMCS8 的 Nginx 站点配置模板,以供参考(使用时建议删除中文注释)。

# HTTP 重定向到 HTTPS
server {
    listen      80;
    listen      [::]:80;
    server_name example.com www.example.com;
    return 301  https://www.example.com$request_uri;
}

# 根域名重定向到 WWW
server {
    listen      443 ssl http2;
    listen      [::]:443 ssl http2;
    server_name example.com;
    return 301  https://www.example.com$request_uri;

    # 证书文件,依序为 证书链+域名证书,域名证书密钥,DH 会话密钥
    ssl_certificate           /etc/pki/tls/certs/example.com.cer;
    ssl_certificate_key       /etc/pki/tls/private/example.com.key;
    ssl_dhparam               /etc/pki/tls/certs/dhparam.pem;

    # SSL 会话,协议版本,加密算法等参数
    ssl_buffer_size           4k;
    ssl_session_timeout       10m;
    ssl_session_cache         shared:SSL:10m;
    ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers               ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_stapling              on;
    ssl_stapling_verify       on;
    ssl_trusted_certificate   /etc/pki/tls/certs/example.com.cer;
}

# WWW 主站点
server {
    listen      443 ssl http2;
    listen      [::]:443 ssl http2;
    server_name www.example.com;
    root        /var/www/example;
    index       index.html index.htm index.php;

    # 证书文件,依序为 证书链+域名证书,域名证书密钥,DH 会话密钥
    ssl_certificate           /etc/pki/tls/certs/example.com.cer;
    ssl_certificate_key       /etc/pki/tls/private/example.com.key;
    ssl_dhparam               /etc/pki/tls/certs/dhparam.pem;

    # SSL 会话,协议版本,加密算法等参数
    ssl_buffer_size           4k;
    ssl_session_timeout       10m;
    ssl_session_cache         shared:SSL:10m;
    ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers               ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_stapling              on;
    ssl_stapling_verify       on;
    ssl_trusted_certificate   /etc/pki/tls/certs/example.com.cer;

    # 检查访问文件/目录是否存在,否则重定向 /index.php,若 URL 带参数则附加
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # 将 PHP 请求传递给 FastCGI 处理,这里引用默认配置
    include /etc/nginx/default.d/php.conf;

    # 引用 Let’s Encrypt 配置,允许申请/续期证书时访问特定目录
    include /etc/nginx/snippets/letsencrypt-acme-challenge.conf;

    # client 区链接重写
    location ~ /(images/em|invoice|login|password|account|store|download|knowledgebase|project_management|announcements|clientarea/ssl-certificates|user/(verification|accounts|profile|password|security|verify)|cart/(domain/renew)|cart/order|images/kb)/?(.*)$ {
        rewrite ^/(.*)$ /index.php?rp=/$1/$2;
    }

    # admin 区链接重写
    location ~ /my_custom_folder_name/(client!\.php|client/(.*)|search!\.php|search/(.*)|apps|billing|setup|user|services|addons|domains|utilities|logs|help!\.php|help/license|image/(recent|upload))/?(.*)$ {
        rewrite ^/(.*)$ /my_custom_folder_name/index.php?rp=/my_custom_folder_name/$1/$2;
    }

    # 设置脚本资源缓存时间
    location ~ .*\.(js|css)?$ {
        expires 12h;
    }

    # 设置图片资源缓存时间
    location ~ .*\.(png|jpg|jpeg|gif|bmp)$ {
        expires 30d;
    }

    # 拒绝访问模板文件
    location ~ .*\.(tpl|inc|cfg)?$ {
        deny all;
    }

    # 拒绝对隐藏文件访问
    location ~ /\. {
        deny all;
    }

    # 拒绝访问 vendor 目录
    location ^~ /vendor/ {
        deny all;
        return 403;
    }

    # 设置日志存儲位置
    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log warn;
}

注:里面的 Nginx 重写规则,设置后需在 WHMCS 其它选项 -> 系统相关 -> 系统清理 -> 清空模板缓存后方能生效。

限制搜索引擎爬取

如需控制搜索引擎爬取内容,需在网站根目录创建 robots.txt 文件设置。譬如拒绝爬取全站内容。

cat > /var/www/example/robots.txt << "EOF"
User-agent: *
Disallow: /
EOF

结束语

完成上述服务器配置后就可以开始使用了,接下来请配置 WHMCS 系统选项,支付网关,添加产品服务,工单支持设置,邮件发送服务,自动化任务等。