AsmBB+Nginx+Postfix 组合实战指南
AsmBB v3.0+Nginx(自签TLS)+Postfix 三件套组合:FastCGI 联通、SMTP 参数写入与重启缓存坑、注册→激活邮件→激活登录→Atom 订阅端到端实测与 502/550 故障演练,全链路实测回显。
AsmBB+Nginx+Postfix 组合实战指南
目标:把 AsmBB 引擎、Nginx(含自签 TLS)与 Postfix 本地 MTA 组合成一台可生产的最小论坛栈,端到端打通「HTTPS 访问 → 注册 → 激活邮件 → 激活登录 → Atom 订阅」全链路,并完成引擎与邮件两条链的故障演练。本组合已在 Ubuntu 22.04.5 LTS + Nginx 1.18.0 + Postfix 3.6.4 + AsmBB v3.0 环境实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86-64),主机名
forum.quzuo.local - Web 前端:Nginx 1.18.0 (Ubuntu),80(HTTP)/ 443(HTTPS,自签证书,CN=forum.quzuo.local,TLSv1.3)
- 论坛引擎:AsmBB v3.0(manifest.uuid
3df85ed0b218e51a59fe3f02650ef7467050cc46),systemd 单元asmbb.service,套接字/var/www/asmbb/engine.sock - 邮件出站:Postfix 3.6.4-1ubuntu1.4,监听
0.0.0.0:25/[::]:25,本机投递到/var/mail/<user> - 数据:
/var/www/asmbb/board.sqlite(WAL 模式) - 演示账号:论坛管理员
admin/q1w2e3r4(邮箱root@quzuo.net) - 单组件安装细节不在本篇重复展开,见 《Ubuntu 22.04 安装 AsmBB v3.0 指南》;本篇聚焦组合与联通
二、角色拓扑与组件分工
浏览器 ──443──> Nginx ──fastcgi_pass unix──> asmbb engine(常驻,读写 board.sqlite)
│ │
└─80 静态: /images /templates └──25──> Postfix ──local──> /var/mail/<user>
| 组件 | 角色 | 关键接口 |
|---|---|---|
| Nginx | TLS 终结、静态加速、FastCGI 反代 | fastcgi_pass unix:/var/www/asmbb/engine.sock |
| AsmBB engine | 论坛全部业务逻辑与数据 | 自建 engine.sock;SMTP 客户端(smtp_addr:smtp_port) |
| Postfix | 本地 MTA:接收引擎投递、本机投递/外发 | 127.0.0.1:25,无需认证(官方 install.txt 前提) |
💡 技术拓展:论坛出站邮件的 MTA 选型常见三种——
方案 特点 适用 本机 Postfix/dma 等轻量 MTA 无认证、低延迟、可审计 单服务器自托管(本篇) 云厂商 SMTP 中继(SES 等) 高送达率,需认证与配额 对退信敏感的商业站 第三方邮箱 SMTP(需 AUTH) 简单但 AsmBB 不支持认证 不适用 AsmBB AsmBB 官方要求「可无认证直连的 SMTP」,故第三行不可用;本篇选第一种 Postfix。
三、前置条件
- 已按安装指南篇完成方案A(Nginx + systemd 常驻引擎),论坛可 HTTP 访问且管理员已创建(
admin/q1w2e3r4)。 - 主机名解析可用:
myhostname与论坛host参数将统一使用forum.quzuo.local。 - 25 端口空闲(
ss -ltn | grep :25无输出)。 - 用于收件验证的本地系统用户(Postfix 本机投递要求收件人存在):
root@forum:~# adduser --disabled-password --gecos "" tester
root@forum:~# id tester
uid=1000(tester) gid=1000(tester) groups=1000(tester)
四、逐组件部署
4.1 Nginx 与自签 TLS(排障实录:ssl 目录缺失)
签发自签证书。首次执行因 /etc/nginx/ssl 目录不存在而连环失败(实录):
root@forum:~# openssl req -x509 -newkey rsa:2048 -nodes -keyout /etc/nginx/ssl/asmbb.key -out /etc/nginx/ssl/asmbb.crt -days 365 -subj "/CN=forum.quzuo.local"
........+..+...+...+....+..+....+...........+.+...+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...........+.+..+...+............+......+....+............+.....+...+.+..............+.+..+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...+......+.....+.......+......+.....+.......+.....+...+.+...............+...+......+..............+...+.........+..........+......+...+.....+....+...+........+......+...+......+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
req: Can't open "/etc/nginx/ssl/asmbb.key" for writing, No such file or directory
root@forum:~# nginx -t
nginx: [emerg] cannot load certificate "/etc/nginx/ssl/asmbb.crt": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/nginx/ssl/asmbb.crt, r) error:10000080:BIO routines::no such file)
nginx: configuration file /etc/nginx/nginx.conf test failed
定位:openssl 不会自动创建输出目录。解决:先 mkdir -p /etc/nginx/ssl 再签发:
root@forum:~# mkdir -p /etc/nginx/ssl
root@forum:~# openssl req -x509 -newkey rsa:2048 -nodes -keyout /etc/nginx/ssl/asmbb.key -out /etc/nginx/ssl/asmbb.crt -days 365 -subj "/CN=forum.quzuo.local"
root@forum:~# ls -l /etc/nginx/ssl/
total 16
drwxr-xr-x 2 root root 4096 Nov 2 00:27 .
drwxr-xr-x 9 root root 4096 Nov 2 00:27 ..
-rw-r--r-- 1 root root 1135 Nov 2 00:27 asmbb.crt
-rw------- 1 root root 1704 Nov 2 00:27 asmbb.key
443 站点配置 /etc/nginx/sites-available/asmbb-tls(与 80 站点同根目录、同套接字):
server {
listen 443 ssl;
server_name forum.quzuo.local;
ssl_certificate /etc/nginx/ssl/asmbb.crt;
ssl_certificate_key /etc/nginx/ssl/asmbb.key;
root /var/www/asmbb;
location / {
fastcgi_pass unix:/var/www/asmbb/engine.sock;
include fastcgi_params;
}
}
逐项说明:证书私钥 asmbb.key 权限 600 属 root(Nginx master 以 root 读);server_name 与后续论坛 host 参数必须一致,否则激活邮件里的链接域名与实际不符。启用并验证:
root@forum:~# ln -sf /etc/nginx/sites-available/asmbb-tls /etc/nginx/sites-enabled/asmbb-tls
root@forum:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
root@forum:~# systemctl reload nginx
root@forum:~# curl -sk -o /dev/null -w "https HTTP %{http_code} size %{size_download}\n" https://127.0.0.1/
https HTTP 200 size 24151
root@forum:~# curl -sk https://127.0.0.1/ | grep -o '<title>[^<]*</title>'
<title>AsmBB: Threads list /</title>
root@forum:~# echo | openssl s_client -connect 127.0.0.1:443 -servername forum.quzuo.local 2>/dev/null | grep -E "subject=|Protocol"
subject=CN = forum.quzuo.local
Protocol : TLSv1.3
4.2 AsmBB 引擎(常驻模式)
按安装指南篇方案A部署后核对三件套——服务、套接字、资源姿态:
root@forum:~# systemctl status asmbb | head -6
● asmbb.service - AsmBB forum engine FastCGI script.
Loaded: loaded (/etc/systemd/system/asmbb.service; disabled; vendor preset: enabled)
Active: active (running) since Mon 2026-11-02 00:15:31 UTC; 9min ago
Main PID: 3214 (engine)
Tasks: 2 (limit: 2506)
Memory: 3.1M
root@forum:~# ls -l /var/www/asmbb/engine.sock
srw-rw-rw- 1 www-data www-data 0 Nov 2 00:15 /var/www/asmbb/engine.sock
root@forum:~# ps -eo pid,user,rss,vsz,cmd | grep [e]ngine
3214 www-data 2664 3208 /var/www/asmbb/engine
4.3 Postfix
root@forum:~# apt-get install -y postfix
Setting up cpio (2.13+dfsg-7ubuntu0.2) ...
Setting up ssl-cert (1.1.2) ...
Setting up postfix (3.6.4-1ubuntu1.4) ...
交互式配置选「Internet Site」、系统邮件名 forum.quzuo.local。关键配置核对:
root@forum:~# grep -E "^(myhostname|mydestination|inet_interfaces|mynetworks)" /etc/postfix/main.cf
myhostname = forum.quzuo.local
mydestination = $myhostname, forum.quzuo.local, localhost.quzuo.local, , localhost
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
inet_interfaces = all
逐项说明:mydestination 决定哪些域走本机投递(forum.quzuo.local 必须在列);mynetworks=127.0.0.0/8 让引擎从回环投递免认证,正合官方前提;inet_interfaces=all 同时服务外发 25 端口。启动并验证监听:
root@forum:~# systemctl start postfix
root@forum:~# systemctl is-active postfix
active
root@forum:~# ss -ltn | grep :25
LISTEN 0 100 0.0.0.0:25 0.0.0.0:*
LISTEN 0 100 [::]:25 [::]:*
Postfix 装完默认不自动启动(实录曾观测 inactive),systemctl enable postfix 避免重启后断链。
五、组件间联通配置
5.1 引擎 ↔ Postfix:写入 SMTP 参数
AsmBB 的 SMTP 配置存在 board.sqlite 的 Params 表(管理页 !settings 同源)。批量写入用 SQL 文件:
root@forum:~# cat > /root/smtp.sql <<'EOF'
insert or replace into Params(id,val) values ('forum_title','AsmBB Production Demo');
insert or replace into Params(id,val) values ('host','forum.quzuo.local');
insert or replace into Params(id,val) values ('smtp_addr','127.0.0.1');
insert or replace into Params(id,val) values ('smtp_port','25');
insert or replace into Params(id,val) values ('smtp_user','admin@forum.quzuo.local');
insert or replace into Params(id,val) values ('email_confirm','1');
EOF
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite < /root/smtp.sql
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select id,val from params where id in ('email_confirm','forum_title','host','smtp_addr','smtp_port');"
email_confirm|1
forum_title|AsmBB Production Demo
host|forum.quzuo.local
smtp_addr|127.0.0.1
smtp_port|25
5.2 关键一步:重启引擎使 Params 生效(排障实录)
直改库后论坛不会立即使用新参数——Params 在引擎启动时缓存。首轮注册 tester 后 mail.log 根本不存在,即参数未生效 + Postfix 未启动双重叠加:
root@forum:~# tail /var/log/mail.log
tail: cannot open '/var/log/mail.log' for reading: No such file or directory
root@forum:~# systemctl restart asmbb
root@forum:~# systemctl is-active asmbb
active
(mail.log 报错实录:Postfix 从未启动过故日志不存在;tail 输出首行回显省略为报错原文。)重启引擎后新参数即被读取,后续 6.2 的实发链路即为生效验证。
5.3 Postfix 本机投递自检
正式注册前先用 sendmail 验证 MTA 链路:
root@forum:~# echo "asmbb mta selftest" | sendmail tester@forum.quzuo.local
root@forum:~# tail -4 /var/log/mail.log
Nov 2 00:30:05 forum postfix/cleanup[5808]: 803B21603723: message-id=<20261102003005.803B21603723@forum.quzuo.local>
Nov 2 00:30:05 forum postfix/qmgr[5785]: 803B21603723: from=<root@forum.quzuo.local>, size=297, nrcpt=1 (queue active)
Nov 2 00:30:05 forum postfix/local[5811]: 803B21603723: to=<tester@forum.quzuo.local>, relay=local, delay=0.02, delays=0.01/0.01/0/0, dsn=2.0.0, status=sent (delivered to mailbox)
Nov 2 00:30:05 forum postfix/qmgr[5785]: 803B21603723: removed
root@forum:~# ls -l /var/mail/
total 16
drwxrwsr-x 1 root mail 4096 Nov 2 00:30 .
drwxr-xr-x 1 root root 4096 Nov 2 00:14 ..
-rw------- 1 tester mail 461 Nov 2 00:30 tester
status=sent (delivered to mailbox) 且 /var/mail/tester 出现,MTA 就绪。
六、组合场景端到端实测
6.1 注册触发激活邮件
以 tester2 为例(curl 模拟浏览器:取一次性 ticket → 提交注册表单):
root@forum:~# TICKET=$(curl -s -c /tmp/c.txt http://forum.quzuo.local/!register/ | grep -oE 'value="[0-9A-F]{32}"' | head -1 | sed 's/value=//;s/"//g')
root@forum:~# echo "TICKET=$TICKET"
TICKET=D517378C0034EA41A48354A898B3AD16
root@forum:~# curl -s -b /tmp/c.txt -o /dev/null -w "register HTTP %{http_code}\n" -D /tmp/r.txt -d "username=tester2&email=tester2@forum.quzuo.local&password=q1w2e3r4&password2=q1w2e3r4&ticket=$TICKET&submit.x=10&submit.y=10" http://forum.quzuo.local/!register/
register HTTP 302
root@forum:~# grep -i "^location" /tmp/r.txt
Location: /!message/user_created/
表单字段即注册页的 username/email/password/password2/ticket 五件套(submit.x/y 为提交按钮坐标,引擎用作反机器人校验)。
6.2 激活邮件内容
设置 smtp_user 后引擎实发,Postfix 队列与信箱回显:
root@forum:~# tail -3 /var/log/mail.log
Nov 2 00:42:19 forum postfix/qmgr[5785]: 7DED0160F0B8: from=<"admin@forum.quzuo.local"@forum.quzuo.local>, size=857, nrcpt=1 (queue active)
Nov 2 00:42:19 forum postfix/qmgr[5785]: 7F1CE160F0BB: from=<"admin@forum.quzuo.local"@forum.quzuo.local>, size=860, nrcpt=1 (queue active)
Nov 2 00:42:19 forum postfix/qmgr[5785]: 810FE160F0B8: from=<"admin@forum.quzuo.local"@forum.quzuo.local>, size=860, nrcpt=1 (queue active)
root@forum:~# cat /var/mail/tester2
From "admin@forum.quzuo.local"@forum.quzuo.local Mon Nov 2 00:42:19 2026
Return-Path: <"admin@forum.quzuo.local"@forum.quzuo.local>
X-Original-To: tester2@forum.quzuo.local
Delivered-To: tester2@forum.quzuo.local
Received: from forum.quzuo.local (localhost [127.0.0.1])
by forum.quzuo.local (Postfix) with SMTP id 7F1CE160F0BB
for <tester2@forum.quzuo.local>; Mon, 2 Nov 2026 00:42:19 +0000 (UTC)
From: "admin@forum.quzuo.local"@forum.quzuo.local
To: tester2@forum.quzuo.local
Date: Mon, 02 Nov 2026 00:42:19 GMT
Subject:
Message-Id: <20261102004219.7F1CE160F0BB@forum.quzuo.local>
Kontoaktivierungslink für http://forum.quzuo.local
Hallo, tester2.
...
Um Ihr Konto zu aktivieren, rufen Sie folgenden Link in Ihrem Webbrowser auf:
https://forum.quzuo.local/!activate/0D5BFD456C7471BC247E63C4CE84A402/
输出节选(实测首尾)。两个要点:①正文默认德语——default_lang=0 为德语模板,可在 !settings 改默认语言;②链接域名取自 host 参数且为 https,与 4.1 的 TLS 站点闭环。
6.3 激活并登录
root@forum:~# curl -s -o /dev/null -w "activate HTTP %{http_code}\n" -D /tmp/act.txt "https://forum.quzuo.local/!activate/0D5BFD456C7471BC247E63C4CE84A402/" -k
activate HTTP 302
root@forum:~# grep -i "^location" /tmp/act.txt
Location: /!message/congratulations
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select id,nick,email from users;"
1|admin|root@quzuo.net
2|tester2|tester2@forum.quzuo.local
激活后用户进入 Users 表。登录验证(同样需先取 ticket):
root@forum:~# curl -s -c /tmp/s.txt -o /dev/null -w "login HTTP %{http_code}\n" -D /tmp/l.txt -d "username=tester2&password=q1w2e3r4&ticket=$TICKET&submit.x=10&submit.y=10" http://forum.quzuo.local/!login
login HTTP 302
root@forum:~# grep -iE "^(set-cookie|location)" /tmp/l.txt
Set-Cookie: sid=EF375EE04BBA7BA339D9B035895EC9B6; HttpOnly; Path=/;
Location: /
会话以 sid Cookie 维持(HttpOnly);登录后首页出现 !logout 与 userinfo/tester2 入口。
6.4 Atom 订阅出口
root@forum:~# curl -s -o /dev/null -w "feed HTTP %{http_code} type %{content_type}\n" "http://127.0.0.1/!feed/"
feed HTTP 200 type application/atom+xml; charset=UTF-8
root@forum:~# curl -s "http://127.0.0.1/!feed/" | head -6
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>AsmBB Production Demo: All threads</title>
<subtitle type="html">
forum_title 参数即时反映在订阅源标题,全站 Atom 出口无需任何额外组件。
七、故障演练
7.1 引擎单点故障注入
root@forum:~# systemctl stop asmbb
root@forum:~# curl -s -o /dev/null -w "stop后 HTTP %{http_code}\n" http://127.0.0.1/
stop后 HTTP 502
root@forum:~# systemctl start asmbb
root@forum:~# curl -s -o /dev/null -w "start后 HTTP %{http_code}\n" http://127.0.0.1/
start后 HTTP 200
结论:引擎是栈内唯一单点,Restart=on-failure 覆盖崩溃场景,人工停止则全站 502;Nginx/Postfix 存活不受影响。
7.2 邮件链故障:550 收件人不存在与补救
注册 tester4 时 OS 用户不存在,Postfix 拒收:
root@forum:~# curl -s -b /tmp/c4.txt -o /dev/null -w "register HTTP %{http_code}\n" -d "username=tester4&email=tester4@forum.quzuo.local&password=q1w2e3r4&password2=q1w2e3r4&ticket=$TICKET&submit.x=10&submit.y=10" http://127.0.0.1/!register/
register HTTP 302
root@forum:~# tail -1 /var/log/mail.log
Nov 2 01:08:09 forum postfix/smtpd[8825]: NOQUEUE: reject: RCPT from localhost[127.0.0.1]: 550 5.1.1 <tester4@forum.quzuo.local>: Recipient address rejected: User unknown in local recipient table; from=<"admin@forum.quzuo.local"@forum.quzuo.local> to=<tester4@forum.quzuo.local> proto=SMTP helo=<forum.quzuo.local>
要点:注册已成功进入 WaitingActivation(550 是硬拒绝,引擎不会无限重投)。补救不依赖重发——先补建 OS 用户,再从库里取激活 secret 走链接:
root@forum:~# adduser --disabled-password --gecos "" tester4
Adding new user `tester4' (1003) with group `tester4' ...
Creating home directory `/home/tester4' ...
Copying files from `/etc/skel' ...
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select a_secret,nick,email from waitingactivation;"
F78E00AF5304FC1FBAEBE3572A845C59|tester4|tester4@forum.quzuo.local
root@forum:~# curl -s -o /dev/null -w "activate HTTP %{http_code}\n" -D /tmp/a.txt "http://127.0.0.1/!activate/F78E00AF5304FC1FBAEBE3572A845C59/"
activate HTTP 302
root@forum:~# grep -i "^location" /tmp/a.txt
Location: /!message/congratulations
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select id,nick,email from users;"
1|admin|root@quzuo.net
2|tester4|tester4@forum.quzuo.local
生产上更优解:为收信域配置真实邮箱账户/别名(virtual_alias_maps),或让 mydestination 域与 OS 用户解耦。
7.3 引擎崩溃自愈(kill -9)
root@forum:~# kill -9 3214
root@forum:~# systemctl status asmbb | head -2
Active: active (running) since Mon 2026-11-02 00:43:35 UTC; 742ms ago
Main PID: 5946 (engine)
root@forum:~# curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1/
HTTP 200
systemd 在 742ms 内拉起新实例(PID 3214→5946),新引擎重建 engine.sock 并接管,站点自愈。
八、常见问题
Q1:注册成功但 mail.log 毫无动静?
按序查三件事:smtp_user 是否为空(空则引擎静默不发,见运维手册 7.5);Params 改动后是否 systemctl restart asmbb(启动缓存);Postfix 是否 active(安装后默认不启动)。
Q2:激活邮件里是德语?
default_lang=0 即德语模板,!settings 中改默认语言后新邮件跟随变化。
Q3:激活链接是 https 但证书是自签的?
演示环境用自签;生产将 asmbb.crt/asmbb.key 换成 CA 证书(如 certbot 签发)即可,Nginx 配置不变。host 参数决定链接前缀,务必与证书域名一致。
Q4:25 端口要不要对公网开放?
出站场景 inet_interfaces 可收紧为 loopback-only(引擎走 127.0.0.1 投递即可);仅当需要接收外部邮件才监听 all。收紧后本篇 4.3 的 ss -ltn | grep :25 回显将只剩 127.0.0.1:25。
九、参考链接
- 官方 install.txt(SMTP 无认证前提):https://asm32.info/fossil/repo/asmbb/doc/trunk/install.txt
- 官方论坛邮件处理帖:https://board.asm32.info/email-handling-and-registration-without-valid-email.58/
- Postfix 官方基础配置:https://www.postfix.org/BASIC_CONFIGURATION_README.html
- Atom 格式规范:https://datatracker.ietf.org/doc/html/rfc4287
- 站内相关:《AsmBB 介绍与选型指南》、《Ubuntu 22.04 安装 AsmBB v3.0 指南》、《AsmBB 日常运维手册》