iptables 黑名单迁移到 ipset:CentOS 6 实战

背景

博客服务器(阿里云 ECS,CentOS 6.3)运行着一个旧的防攻击脚本,通过分析 Apache access_log 识别恶意请求,逐条 iptables -A 封禁 IP。随着黑名单增长,iptables 逐条匹配的性能问题日益突出。

听说 iptables 黑名单到 1 万条性能就明显不行,而 ipset 支撑到 10 万条都没问题。决定迁移到 ipset 方案。

iptables vs ipset

iptables ipset
匹配方式 逐条遍历规则 哈希查找 O(1)
1万条IP 10000条规则 1条规则 + 1个集合
性能 规则多时线性下降 几乎无影响
动态更新 操作整个链 直接操作集合
存储 内核链表 内核哈希表

一句话:ipset 是 iptables 的 IP 集合加速器,规则多时用 ipset。

环境

  • 系统:CentOS 6.3 (Final)
  • 内核:2.6.32
  • iptables:v1.4.7
  • ipset:v6.11(已预装)
  • Apache:httpd,access_log 在 /var/log/httpd/access_log
  • cron:每天凌晨 3 点自动重启

踩坑记录(6个坑,全部填平)

坑一:iptables –matchset 语法不兼容

CentOS 6 的 iptables v1.4.7 不支持 --matchset,需要用旧语法 --set

# CentOS 7+ (新语法)
iptables -A INPUT -m set --matchset blacklist src -j DROP

# CentOS 6 (旧语法)
iptables -A INPUT -m set --set blacklist src -j DROP

坑二:ipset save 不支持 -f 参数

ipset v6.11 不支持 -f 参数指定输出文件,需要手动重定向:

# 错误(v6.11 不支持)
ipset save blacklist -f /etc/sysconfig/ipset

# 正确
ipset save blacklist > /etc/sysconfig/ipset

坑三:ipset save 默认输出到屏幕

ipset v6.11 的 save 命令默认输出到 stdout,不会自动写文件。脚本中必须用重定向 > 写入文件,否则重启后集合丢失。

坑四:旧脚本未做 iptables 持久化

原来的防攻击脚本每次 iptables -A 后没有执行 iptables-save,服务器每天凌晨 3 点自动重启,所有封禁规则全部丢失。迁移到 ipset 后修复了这个问题。

坑五:两个脚本同时跑

部署新脚本时忘了删除旧脚本的 cron 任务,导致两个脚本同时运行。旧脚本用 iptables,新脚本用 ipset,互相干扰。清理后正常。

坑六:cron 环境无 PATH 导致持久化文件被清空

这是最隐蔽的一个坑。cron 运行环境极简,PATH 变量不包含 /usr/sbin/sbin,导致 ipsetiptables-save 命令找不到。而 shell 重定向 > 会先清空目标文件再执行命令,命令找不到输出为空,文件就被清空了。

表现:手动运行脚本一切正常(交互 shell 有完整 PATH),但 cron 每次执行后 /etc/sysconfig/ipset/etc/sysconfig/iptables 都变成 0 字节。

修复:在脚本开头显式设置 PATH:

#!/bin/bash
PATH=/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/sbin
export PATH

坑七:iptables -C 不支持 -m set 导致规则重复添加

脚本用 iptables -C 检测规则是否已存在(幂等添加),但 CentOS 6 的 iptables v1.4.7 的 -C 命令不支持 -m set --set 参数,报错 option -C requires an argument,检测总是失败,每轮 cron 都会重复添加一条规则。

表现:iptables INPUT 链中出现 5 条相同的 blacklist DROP 规则。

修复:改用 iptables -S + grep 检测:

# 旧方式(CentOS 6 不支持)
if ! iptables -C INPUT -m set --set "$SET_NAME" src -j DROP 2>/dev/null; then

# 新方式(兼容 CentOS 6)
if ! iptables -S INPUT 2>/dev/null | grep -q "match-set $SET_NAME"; then

最终方案

防攻击脚本(block-bots-ipset.sh)

核心逻辑:

  1. 脚本开头设置 PATH(解决 cron 环境问题)
  2. 首次运行创建 ipset 集合(hash:ip, maxelem 100000)+ iptables 引用规则
  3. 增量读取 access_log(基于文件偏移量,只读新增部分)
  4. 用 awk 匹配 11 类攻击特征
  5. 新攻击 IP 通过 ipset add 加入集合
  6. 有新增则 ipset save > /etc/sysconfig/ipset + iptables-save > /etc/sysconfig/iptables 持久化
  7. iptables 引用规则用 iptables -S | grep 幂等检测,避免重复添加

11 条攻击检测规则

规则 检测内容 特征示例
1 5xx 服务器错误 注入触发崩溃
2 SQL 注入 UNION, SELECT, SLEEP(), DROP, INSERT
3 XSS 攻击 %3Cscript, javascript:, onerror=
4 路径穿越 ../, %2e%2e, etc/passwd
5 空 UA 扫描器 User-Agent 为 “-“
6 敏感文件扫描 .env, .git, .sql, .bak, wp-config, xmlrpc.php
7 HEAD 探测 HEAD 方法扫描
8 恶意爬虫 MJ12bot, YisouSpider, DotBot
9 攻击工具 sqlmap, nikto, nmap, acunetix, nessus, wpscan
10 RFI/LFI php://input, php://filter, data://
11 命令注入 ;wget, ;curl, eval(, system(, exec(

持久化方案

组件 持久化方式
ipset 集合 ipset save > /etc/sysconfig/ipset + rc.local 开机 ipset restore
iptables 规则 iptables-save > /etc/sysconfig/iptables + iptables 服务开机自启
脚本定时执行 cron 每 5 分钟一次
封禁日志 /var/log/block-bots.log,超过 10MB 自动轮转

迁移过程

  1. 检查环境:确认 ipset v6.11 已安装,CentOS 6.3
  2. 发现 iptables 为空:之前封的 IP 因重启全丢,旧脚本未持久化
  3. 部署新脚本:上传 block-bots-ipset.sh,首次运行创建集合 + iptables 引用
  4. 修复语法兼容:–matchset -> –set,-f -> 重定向
  5. 导入历史黑名单:从 /var/log/block-bots.log 提取 855 个 IP 导入 ipset
  6. 配置 cron:每 5 分钟执行,删除旧脚本 cron
  7. 配置持久化:ipset save + iptables-save + rc.local
  8. 修复 cron PATH 问题:脚本开头加 PATH 和 export
  9. 修复规则重复添加:iptables -C 改为 iptables -S + grep
  10. 全面检查:12 项检查脚本全部通过

最终效果

  • 855 个攻击 IP 已封禁
  • iptables 只需 1 条规则匹配整个集合
  • 每 5 分钟自动检测新攻击并封禁
  • 每天凌晨重启后自动恢复所有规则
  • 封禁在 iptables 层生效,攻击 IP 进不了 Apache,不消耗服务器资源

经验总结

  1. CentOS 6 语法差异多:iptables 用 --set 不用 --matchset,ipset save 不支持 -f,都要手动重定向
  2. 持久化要做全套:ipset save + iptables-save + rc.local + iptables 服务开机自启,缺一不可
  3. 部署新脚本要清理旧任务:两个脚本同时跑会互相干扰
  4. ipset save 的输出要重定向:v6.11 默认输出到屏幕,不写文件,重启后集合丢失
  5. cron 环境极简,必须手动设 PATH:否则 > 重定向先清空文件,命令又找不到,文件就变空了。这个坑最隐蔽,手动测试一切正常,只有 cron 执行时才出问题
  6. iptables -C 不支持 -m set:CentOS 6 的幂等检测要改用 iptables -S | grep,否则每轮 cron 都重复添加规则
  7. 检查脚本很重要:写了 12 项检查脚本,一次性发现所有遗漏

附录:检查脚本

写了 12 项检查脚本(check-ipset-status.sh),覆盖:

  1. ipset 版本
  2. 集合状态与 IP 总数
  3. iptables 引用规则
  4. iptables 持久化文件
  5. iptables 服务开机自启
  6. ipset 持久化文件
  7. rc.local 开机恢复
  8. cron 定时任务
  9. 脚本文件与语法检查
  10. 封禁日志状态
  11. 实时拦截统计
  12. 抽样验证已封禁 IP

阿里云自然语言运维实战:一次恶意爬虫攻击的完整防御

故障:博客网站突然变慢

今天下午,我的博客网站(挂在阿里云 ECS 上)访问变得非常慢。直觉告诉我:大概率是被爬虫盯上了。

和以往不同的是,这次我没有像往常一样打开终端敲命令、翻日志、查配置。而是打开阿里云控制台的 Workbench Agent,在对话框里用中文描述故障现象,Agent 自动接管了后续所有排查工作。

四轮排查:层层抽丝剥茧

整个排查过程分四轮,全程通过自然语言对话完成。我只需要描述现象、确认执行建议,Agent 自动生成命令、分析日志、配置防护。

第一轮:发现恶意爬虫

爬虫名称 请求次数 风险说明
MJ12bot 1892 超高频,无公司背书,典型内容采集器
YisouSpider 617 疑似伪造 User-Agent,不实为神马搜索
ris-bot 少量 伪造本地域名 ris.local,恶意扫描

Agent 没有简单地建议封 IP,而是指出关键思路:封 IP 不是好办法,因为攻击 IP 会变化,要根据特征来封。于是基于 User-Agent 特征创建了 Apache Rewrite 规则,返回 403 Forbidden。

第二轮:伪造的浏览器

拦截爬虫后 CPU 仍然高达 84%。Agent 排除已拦截对象,扫描日志中的异常 User-Agent:

User-Agent 请求次数 破绽
Chrome/142 (Win10) 5053 Chrome 真实最新稳定版 ~125
Chrome/145 (Win10) 4916 同上,版本号明显伪造
Edge/145 (Win10) 4922 Edge 也没有 145 版本

每批 4900~5000 次请求,伪装成正常浏览器,但在 Agent 眼里无所遁形。

第三轮:Slowloris 慢速攻击

CPU 仍未降低。Agent 换了个角度——直接检查 TCP 连接层的状态。用 ss -tnp 检查 80 端口时,发现关键证据:某个 IP 的 Recv-Q = 939, Send-Q = 0。客户端连接上了,但只发了一部分请求头就卡住了。

这就是经典的 Slowloris 慢速攻击。靠”挂” Apache 进程——大量 IP 每个建 2 个连接保持半开,慢慢把进程池占满。

Agent 给出方案:iptables 限制单 IP 并发 ≤20,Apache 层面关闭 KeepAlive。

同时发现 Facebook 爬虫群(Meta AS 网段 69.171.x.x)在反复探测 /opt/gforge5/www/robots.txt。每条 403 响应本身也在消耗 CPU。

一个老系统的天花板

Agent 建议启用 Apache 的 mod_reqtimeout 模块来原生防御 Slowloris。检查后发现当前系统是 CentOS 6.3,2020 年就已 EOL,最高只支持 httpd 2.2.x,装不了 mod_reqtimeout 或 mod_evasive。

AI 总结道:当前多层防护已经是这个系统 Apache 层面能做到的天花板了。

短期策略:现有防护基础上,开启阿里云 DDoS 基础防护。长期策略:迁移至 AlmaLinux 8+ 获得 httpd 2.4 原生防护能力。

最终生效的五层防护

措施 状态
UA 特征拦截 MJ12bot / YisouSpider / ris-bot / Chrome/14x → 403 [OK]
路径拦截 /opt/gforge5/ → 403 [OK]
连接限制 iptables 单 IP ≤20 并发 [OK]
静态黑名单 高频攻击 IP → DROP [OK]
服务调优 MaxClients=20 / Timeout=30 / KeepAlive Off [OK]

感受:运维方式的范式变化

这次从发现故障到完全解决,全程通过自然语言对话完成。

传统模式:SSH 登录 → tail 日志 → 分析 IP → awk 排序 → 写配置 → 校验 → 重载 → 再观察 → 循环。每个循环五到十分钟,四到五轮下来至少半小时,还容易遗漏。

对话模式:看到什么说什么。Agent 自动决定分析逻辑,自动写出正确的命令,自动配置规则,甚至主动发现用户没想到的问题。操作者只管做决策和确认。

第三部分:性能优化建议导致的崩溃事故

前文介绍了四层防御体系的搭建过程。然而在排查过程中,一起性能优化建议导致的崩溃事故让情况雪上加霜。

问题:MaxClients 被错误调高到 50

当时正常访问变慢,为了提升并发性能,AI 助手提出建议:MaxClients 20 → 50。这个数字看起来只是乘以 2.5 的变化,但对于一台内存仅 1.9GB 的老服务器来说,这是致命的:

MaxClients 50 的内存账:
  50 × 50MB (空闲) = 2.5 GB → 已超物理内存
  50 × 150MB (PHP)  = 7.5 GB → 直接炸
可用内存:1.9 GB + 0 GB Swap = 1.9 GB
需求 2.5 GB > 1.9 GB → OOM

系统反复 OOM 崩溃

MaxClients=50 导致系统内存快速耗尽,OOM Killer 反复杀 httpd 进程。新请求进来 → 起新进程 → 内存不足 → 杀进程 → 再来请求 → 循环。最终操作系统完全无法启动:

阿里云诊断报告:Linux 实例因内存溢出(OOM),操作系统无法正常启动。错误代码:1684829582。

关键时刻的正确决策:果断停掉了 80 端口 httpd 服务,切断了 OOM 循环。系统内存压力解除后,才通过阿里云 VNC 进入系统修复。

Workbench Agent 再次出手:精准定位 OOM 根因

进入系统后,通过 Workbench Agent 继续分析,精准定位了 OOM 根因。从 /var/log/messages 提取到 OOM 记录:

Jul  7 16:48:16 kernel: Out of memory: Kill process 1983 (httpd) score 27
Jul  7 16:48:23 kernel: Out of memory: Kill process 1985 (httpd) score 27
Jul  7 16:48:28 kernel: Out of memory: Kill process 1987 (httpd) score 27
...(每 3 秒被杀一个,连续 10+ 个)

Workbench Agent 正确判断出:每个 PHP 请求时进程内存飙升到 120~150MB,根本解决方案是降低 MaxClients + 增加 Swap。Workbench Agent 将 MaxClients 紧急降到 8,创建了 2GB Swap。后续在 8 和 20 之间做了平衡测试,最终确认为 MaxClients=20 + 2GB Swap。

最终修复结果

MaxClients: 50 (错误建议) → 8 (紧急) → 20 (平衡值)
Swap:       0 GB → 2 GB
Timeout:    60 → 30
KeepAlive:  On → Off

最终防护体系(七层)

层级 措施 防御目标
Apache UA 特征拦截 facebook / MJ12bot / YisouSpider / ris-bot / Chrome/14x / Go-http-client → 403 已知恶意爬虫 + 伪造浏览器
Apache 路径拦截 /opt/gforge5/ → 403 敏感目录探测
Apache 服务调优 MaxClients=20 / Timeout=30 / KeepAlive Off 防资源耗尽 + 防 Slowloris
网络层并发限制 iptables 单 IP ≤ 20 并发 防分布式 CC
自动封禁 cron 每 5 分钟扫描空 UA 高频 IP → DROP 动态封禁新攻击源
内存保护 2GB Swap + MaxClients=20 防 OOM 崩溃
目录列表禁用 Options -Indexes 防路径枚举

核心教训

教训一:优化建议必须算资源账

AI 助手在不知道服务器具体内存的情况下,直接建议 MaxClients 20 → 50,犯了想当然的错误。正确做法是先 free -m 查看内存,再计算:MaxClients = (可用内存 – 预留) / 单进程峰值内存。

教训二:错误的优化建议比攻击更危险

今天这个案例中,攻击者只是造成了访问变慢,而错误的优化建议直接导致了系统崩溃。性能调优必须先看资源余量,不能凭感觉给数字。

后续维护

所有爬虫规则统一维护在 /etc/httpd/conf.d/block-bots.conf。发现新威胁只需在该文件追加 RewriteCond 行并重启 Apache。

自动封禁脚本

通过 cron 每 5 分钟扫描一次,发现空 UA 或 4xx/5xx 高频探测的 IP,自动 DROP 封禁:

*/5 * * * * /usr/local/bin/block-bots.sh

脚本源码:

#!/bin/bash
# 增量读取日志,无时间过滤,只处理新增访问记录
LOG_FILE="/var/log/httpd/access_log"
BLOCK_LOG="/var/log/block-bots.log"
OFFSET_FILE="/tmp/log_offset.dat"
TMP_FILE="/tmp/hack_ip_$(date +%s).txt"
NEW_BLOCK=0
> "$TMP_FILE"
[ ! -f "$LOG_FILE" ] && exit 0
# 读取上次读取位置,无记录则从0开始
last_pos=0
[ -f "$OFFSET_FILE" ] && last_pos=$(cat "$OFFSET_FILE")
current_pos=$(stat -c %s "$LOG_FILE")
# 日志被切割/清空,重置偏移量
if [ "$current_pos" -lt "$last_pos" ]; then
    last_pos=0
fi
# 只读取增量内容,使用tail替代dd
if [ "$last_pos" -eq 0 ]; then
    cat "$LOG_FILE"
else
    tail -c +$((last_pos + 1)) "$LOG_FILE"
fi | awk '
{
    ua = $NF; req = $7; code = $9; is_hack = 0;
    # 规则1:5xx服务器错误(注入触发崩溃)
    if (code ~ /^50[0-9]$/) is_hack = 1;
    # 规则2:SQL注入特征(只用url编码%27,去掉裸'"'"'避免shell冲突)
    if (req ~ /%27|AND|OR|nvOpzp|%3C%27/) is_hack = 1;
    # 规则3:空UA扫描器
    if (ua == "-") is_hack = 1;
    # 规则4:敏感目录扫描
    if (req ~ /cgi-bin|\/blog\//) is_hack = 1;
    # 规则5:HEAD探测扫描
    if ($6 ~ /^HEAD$/) is_hack = 1;
    # 规则6:恶意爬虫UA
    if (ua ~ /MJ12bot|YisouSpider|Chrome\/14[0-9]|Go-http-client|DotBot/) is_hack = 1;
    if (is_hack) print $1;
}' | sort -u > "$TMP_FILE"
# 更新偏移量,记录本次读到的位置
echo "$current_pos" > "$OFFSET_FILE"
# 批量封禁恶意IP
while read -r ip; do
    # 校验合法IPv4
    if [[ ! $ip =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; then
        continue
    fi
    # 跳过已封禁IP
    if iptables -C INPUT -s "$ip" -j DROP 2>/dev/null; then
        continue
    fi
    iptables -A INPUT -s "$ip" -j DROP
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] BLOCK $ip | 增量日志检测高危注入/扫描攻击" >> "$BLOCK_LOG"
    NEW_BLOCK=1
done < "$TMP_FILE"
# 新增封禁IP则保存iptables规则
[ $NEW_BLOCK -eq 1 ] && iptables-save > /etc/sysconfig/iptables
# 清理临时文件
rm -f "$TMP_FILE"
# 封禁日志自动轮转(超过10MB备份)
LOG_SIZE=$(du -k "$BLOCK_LOG" 2>/dev/null | awk '{print $1}')
if [[ -n "$LOG_SIZE" && "$LOG_SIZE" -gt 10240 ]]; then
    mv "$BLOCK_LOG" "${BLOCK_LOG}.$(date +%Y%m%d)"
    > "$BLOCK_LOG"
fi

日志巡检命令

awk '$9 ~ /^(40[0-9]|50[0-9])$/ && !/MJ12bot|YisouSpider|ris-bot|Chrome\/14[0-9]|facebookexternalhit|meta-externalagent|Go-http-client|DotBot|facebot/' /var/log/httpd/access_log | tail -n 20

筛出”被拦截但不在已知名单里”的请求,就是新威胁线索。

最终 block-bots.conf

RewriteEngine On

# 1. 优先拦截空User-Agent(扫描器常用无UA请求)
RewriteCond %{HTTP_USER_AGENT} "^$"
RewriteRule .* - [F,L]

# 2. 合并所有恶意爬虫UA,一条规则搞定,消除重复
RewriteCond %{HTTP_USER_AGENT} "(facebookexternalhit|meta-externalagent|Facebot|facebook|MJ12bot|YisouSpider|ris-bot|Chrome/14[0-9]|Go-http-client|DotBot)" [NC]
RewriteRule .* - [F,L]

# 3. 拦截频繁HEAD探测 /blog/ 路径
RewriteCond %{REQUEST_METHOD} HEAD
RewriteRule ^/blog/ - [F,L]

# 4. 拦截SQL注入特征(同时匹配URI + QUERY_STRING)
RewriteCond %{REQUEST_URI} (\%27|'|AND|OR|UNION|SELECT|--|#|;) [NC,OR]
RewriteCond %{QUERY_STRING} (\%27|'|AND|OR|UNION|SELECT|--|#|;) [NC]
RewriteRule ^ - [F,L]

# 5. 高危扫描目录拦截
RewriteRule ^/cgi-bin/ - [F,L]

# 6. 业务敏感目录拦截
RewriteCond %{REQUEST_URI} "^/opt/gforge5/" [NC]
RewriteRule .* - [F,L]

修改后执行 service httpd restart 生效。

Gforge 迁移日志

背景: 原网站放在公司服务器上,由于备案过期被电信给封IP了; 后找上海电信重新备案,上海电信说域名是个人注册的,但公司的公网IP属于公司,两者不一致无法在上海电信备案,因此只能转到阿里云,阿里云是可以给个人网站备案的;

需求: 把Gforge平台 从公司服务器迁移到阿里云服务器

执行过程:

1、打包原系统上的数据文件

tar -zcvf gforge_var_lib.tar.gz  /var/lib/gforge

压缩后有1.9G;  压缩前2.3G

2、打包原系统上的数据库

命令操作:
数据的导出:pg_dump -U postgres(用户名)  (-t 表名)  数据库名(缺省时同用户名)  > c:\fulldb.sql

pg_dump -U gforgece gforge5** >/d01/gforge5ce_bak_20141105.sql

tar -czvf gforge5ce_bak_20141105.sql.tar.gz gforge5ce_bak_20141105.sql

压缩前144M,压缩后12M

数据的导入:psql -U postgres(用户名)  gforge5**数据库名(缺省时同用户名) < >/d01/gforge5ce_bak_20141105.sql C:\fulldb.sql

3、打包原系统上的程序文件

tar -zcvf gforge_opt.tar.gz /opt/gforge5

有12M

4、目标机上安装Gforge

上传  install-gforge-ce-561-src.zip 解压缩

1) 执行第一步   install-gforge-1-deps.php CENTOS5  会自动下载安装依赖包;

但 wv-1.0.3-1.fc4.x86_64.rpm 会自动下载却未自动安装,提示缺少wvText, 只要手动安装一下即可:

rpm -ivh wv-1.0.3-1.fc4.x86_64.rpm

执行第二步之前,先初始化  postgresql 数据库

service postgresql initdb

然后启动服务

# service postgresql start

把PostgreSQL 服务加入到启动列表

# chkconfig postgresql-9.2 on

# chkconfig –list|grep postgres

修改PostgreSQL 数据库用户postgres的密码(注意不是linux系统帐号)

PostgreSQL 数据库默认会创建一个postgres的数据库用户作为数据库的管理员,默认密码为空,我们需要修改为指定的密码,这里设定为’postgr**’。

# su – postgres

$ psql

# ALTER USER postgres WITH PASSWORD ‘postgres’;
# select * from pg_shadow ;

2) 安装Gforge DB

php  install-gforge-2-db.php

正常完成

3) 安装第3步

这步安装其实有点搞,因为选择阿里云服务器的时候选择的是带AMP的镜像,但这个镜像的默认应用都是安装到\alidata目录下的,包括httpd.conf的配置文件等都在这个目录下,而不是标准安装包的目标目录,但Gforge 安装的时候都是去修改默认安装配置的,这样就没法装了,方法是把\alidata下的APM安装全部卸载掉(有默认的readme.doc文件可参考),然后自己手动使用yum 来安装AMP;

安装完后在执行第三步;

php  install-gforge-3.php  CENTOS5

最后重启http服务时出错,把Gforge 虚拟主机apache配置中的php_value  # 注释掉可重启。

php_value  include_path  “.:/opt/gforge5:/opt/gforge5/lib:/opt/gforge5/lib/external” 在php.ini中已经配置了,这里可以去掉。

另外报缺少 ioncube 文件   ,参考 http://zpz.name/1927/解决

备注:apache 如果使用yum remove  httpd 卸掉,然后用 yum –y install httpd 重装后,其httpd.conf中的配置便复原了,使用yum install php 后,要配置Apache 加载php  ,否则访问.php就不执行出现源码了,可参考:http://blog.sina.com.cn/s/blog_70121e200100lq0h.html

第三步成功后,应该可以正常访问Gforge了。说明相同版本的环境已经Ok了,  然后我们在进行移植;

5、  还原

改名 /var/lib/gforge  -> /var/lib/gforge_bak20141113

改名 /opt/gforge5 –>  /opt/gforge5_bak20141113

然后把原系统上打包的文件上传,然后还原;

tar -xvf gforge_var_lib.tar.gz

tar –xvf gforge_opt.tar.gz

然后倒入数据库

先创建用户和数据库:

psql -U password
postgres=# create user “gforgec*” with password ‘gforges*f’ login in role “gforge”;

create database “gfoXXXXX”
with owner = gfoXXX
encoding =’UTF8′
tablespace = pg_default
connection limit = -1;

然后 上传 gforge5ce_bak_20141105.sql

导入:

psql -U gfoXXX  gfoXXX<  /gforge5ce_bak_20141105.sql

倒入完成后更新数据库连接配置:

/etc/gforge5-db-conf.php

使用新创建的用户名和倒入的数据库名。

6、测试

发现问题

问题1、用户登录时报错: ERROR: text search configuration “default” does not exist

解决方法:

切换到Gforge所用数据库:

psql -U gfoXXX -d gfoXXX

CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = english)

测试全文检索配置default 可用

gforXXXX=> select to_tsvector(‘default’,’hellobaby’);
to_tsvector
—————
‘hellobabi’:1
(1 row)

再次登录正常了;

参考: http://www.postgresql.org/docs/devel/static/sql-createtsconfig.html

问题2:站长管理,搜寻用户时报错:

Could not execute query [Native Error: ERROR: operator does not exist: integer ~~* integer LINE 5: AND (user_id ILIKE 0 ^ HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.] [User Info: SELECT DISTINCT COUNT(*) FROM “user” WHERE is_group=false AND (user_id ILIKE 0 OR unix_name ILIKE ‘%%’ OR email ILIKE ‘%%’ OR firstname ILIKE ‘%%’)]

原因:8.4版本不支持整数到字符串的隐式转换,详情参见:

http://www.postgresql.org/docs/8.3/static/release-8-3.html#AEN85667

解决方案1:

更改SQL:SELECT DISTINCT COUNT(*) FROM “user” WHERE is_group=false AND (user_id::text ILIKE ‘0’ OR unix_name ILIKE ‘%%’ OR email ILIKE ‘%%’ OR firstname ILIKE ‘%%’)

但每次碰到这样的老SQL 都要改,比较麻烦;

解决方案2:

创建一个函数,再添加整数 ilike 整数的操作符,一次性解决所有类似问题。

CREATE OR REPLACE FUNCTION of_integer_ilike_text(prm_integer integer,
prm_intege2 integer)   RETURNS boolean AS

declare
BEGIN
RETURN prm_integer::text ~~* prm_integer2::text;
END;

image

CREATE OPERATOR ~~*(
PROCEDURE = of_integer_ilike_integer,
LEFTARG = integer,
RIGHTARG = integer);

image

问题3:

发现TO-Do任务,在没有关闭的情况下不发提醒邮件:

原因:Cron 脚本未加到crontab中去。

解决方案:把需要cron运行的php脚本加到crontab中去:

>crontab -e

#########################
# GFORGE CRONJOBS
#########################
*/10 * * * *    cd /opt/gforge5 && /usr/bin/php -d include_path=”.:/opt/gforge5:/opt/gforge5/lib:/opt/gforge5/plugins:/opt/gforge5/lib/external” /opt/gforge5/bin/gforge cronjob  /opt/gforge5/cronjobs/send_notification_queue.php
*/15 * * * *    cd /opt/gforge5 && /usr/bin/php -d include_path=”.:/opt/gforge5:/opt/gforge5/lib:/opt/gforge5/plugins:/opt/gforge5/lib/external” /opt/gforge5/cron15.php
15 3 * * *      cd /opt/gforge5 && /usr/bin/php -d include_path=”.:/opt/gforge5:/opt/gforge5/lib:/opt/gforge5/plugins:/opt/gforge5/lib/external” /opt/gforge5/crondaily.php
#########################
# GFORGE SPHINX Job Index
#########################
15 1 * * *      . /usr/local/sphinx/index_gf_main_rotate.sh
*/15 * * * *    . /usr/local/sphinx/index_gf_delta_rotate.sh

后记:

这个迁移完成后 把 mysql 安装一下,然后把Blog迁移过来

安装mysql可以参考: http://www.2cto.com/database/201207/141878.html