Psono v7.5.3 安全加固指南

Psono v7.5.3 安全加固实测:关闭注册与强制多因子、限流收紧与 429 实测、官方 TLS/CSP 反代基线、防火墙端口矩阵、审计日志开启与 Splunk 外发、服务降权与 600 权限验证、恢复私钥离线治理与官方升级回滚流程,实测于 2026-10-24。

Psono密码管理自托管安全2026/10/24

Psono v7.5.3 安全加固指南

目标:给已按部署篇跑通的 Psono v7.5.3 实例一套可落地的加固清单——认证与访问控制、传输加密、网络防火墙、审计日志、服务降权、密钥治理与补丁策略,全部基于官方文档语义并逐项在 Ubuntu 22.04 + v7.5.3 环境实测(2026-10-24),实测命令与回显随节附上。

一、环境信息

  • 基线:本专栏《Psono v7.5.3 Docker Compose 部署指南》交付的双服务形态(组合镜像 + PostgreSQL 18,Web 端口 8153),配置基线见《Psono v7.5.3 配置参数详解》
  • Psono:v7.5.3(Build 5638426c),CE 社区版
  • 演示账号:root@example.com / q1w2e3r4(仅实验用途,生产必须替换强口令)
  • 加固目标画像:公网可达的团队密码库——口令即最高价值资产,按「减小暴露面、强认证、可审计、可恢复」四条主线展开

二、身份认证与访问控制

2.1 关闭自助注册与自助找回

企业实例账号应全部管理员制。settings.yaml 三个口子:

ALLOW_REGISTRATION: False
ALLOW_LOST_PASSWORD: False
REGISTRATION_EMAIL_FILTER: ['example.com']

配套收紧搜索面(官方 Supported Features 默认即「不允许」,保持关闭):ALLOW_USER_SEARCH_BY_USERNAME_PARTIAL、ALLOW_USER_SEARCH_BY_EMAIL 不设或设 False。验证:改参重启后 /server/info/ 响应中 management: true 不变而注册入口消失(Web 客户端 config.json 中 allow_registration 同步设 false 才是完整闭环——两个层面:服务端 API 口子 + Web 客户端展示口子)。

2.2 强制多因子认证

默认 2FA 类型白名单(实测 /server/info/ 回显):

"allowed_second_factors": ["yubikey_otp", "webauthn", "google_authenticator", "duo"]

settings.yaml 加固项:

MULTIFACTOR_ENABLED: True
ALLOWED_SECOND_FACTORS: ['yubikey_otp', 'google_authenticator', 'webauthn']

官方注释原文:MULTIFACTOR_ENABLED 开启后「Normally only one of the configured second factors needs to be solved. Setting this to True forces the client to solve all」——即强制全部已配置因子通过,管理员账号建议开启。实测基线值:multifactor_enabled: False(info JSON 回显),加固即把它翻转为 True 并逐用户绑定 2FA。管理门户内可按用户/分组强制策略(Policies)。

2.3 限流收紧

官方默认限流 15 组(完整默认值表见配置篇 5.4 节),公网暴露时优先收紧登录与注册组:

THROTTLE_RATE_LOGIN: '24/day'
THROTTLE_RATE_REGISTRATION: '3/minute'

限流机制实测(配置篇 5.4 节已复现 429):将 THROTTLE_RATE_HEALTH_CHECK 临时设 '2/hour' 后,第二次请求健康检查即返回 429——证明限流引擎真实生效且计数在 Redis(throttle_health_check_<IP> 键)。加固时注意健康检查组保持默认 61/hour:组合镜像自带每 120 秒一次的健康探测与人工请求共享配额(实测镜像 HEALTHCHECK 定义:Interval: 120s,探测 curl -f http://localhost:80/server/healthcheck/),调得太低会导致服务被误判不健康。

2.4 管理门户访问面收敛

管理门户(/portal/)与用户端同端口同服务,门户面通过反代层收敛(通用 Nginx 实践,非官方文档要求):在反代 server 块对 /portal/ 增加 IP 白名单 allow 192.168.146.0/24; deny all;(或仅限办公网段/VPN),用户端保持公网。服务端侧对应验证:MANAGEMENT_ENABLED: True 必须保留(门户 API),但 API 面同样被反代白名单覆盖。

三、传输加密(TLS)

3.1 官方反代安全配置

生产必须以官方反代文档的 Nginx 配置为基线(doc.psono.com「Installation Reverse Proxy」,2026-10-24 实查转录,安全相关行节选):

server {
    listen 443 ssl http2;
    server_name psono.example.com;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;

    # Comment this in if you know what you are doing
    # add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

    add_header Referrer-Policy same-origin;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    add_header Content-Security-Policy "default-src 'none';  manifest-src 'self'; connect-src 'self' https://static.psono.com https://api.pwnedpasswords.com ...; img-src 'self' https://favicon.psono.com data:; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'self'; frame-ancestors 'none'; child-src 'self'";

    client_max_body_size 256m;
}

关键差异与如何选型:官方默认给出 80→443 跳转(return 301 https://$host$request_uri)、TLS 1.2/1.3 双协议、现代 ECDHE 套件与 OCSP 装订;HSTS 官方特意注释掉并标注「if you know what you are doing」——全域且准备好长期 HTTPS 才开启,否则回滚困难;CSP 中 connect-src 白名单包含 api.pwnedpasswords.com(泄露口令查询)与若干对象存储域名,自建文件服务器时按官方注释把文件服务器域名加入 connect-src。

3.2 内部链路加密

服务内 Nginx→Daphne 为本地回环明文(同容器内),无需 TLS;反代→服务段若跨主机,建议用 WireGuard/VPC 内网(官方 EE Gateway 亦是此思路)。config.json 的 backend_servers[].url 与 settings.yaml 的 WEB_CLIENT_URL/HOST_URL 必须同步改为 https:// 域名,否则浏览器混合内容拦截。

四、网络与防火墙

4.1 官方防火墙白名单

官方安装文档「Installation behind Firewall」给出的端口矩阵(2026-10-24 实查原文):

方向端口用途收紧建议
入站(公网)80/443用户访问 Web/服务端仅反代层暴露,443 为主
入站(内部)10200管理面直连端口(官方示例映射)改为仅内网/本地回环,或复用 443 + /portal/ 白名单
入站(内部)5432PostgreSQL仅限 Psono 服务网段访问,禁止公网
出站123/UDPNTP(默认 time.google.com,可换源)换源为可达 NTP
出站53DNS 解析正常放行
出站443haveibeenpwned(api.pwnedpasswords.com)、favicon 服务、静态资源关闭中心安全报告后可再收窄

实测佐证(本栈端口面):

docker compose ps --format 'table {{.Name}}\t{{.Ports}}'

实测回显:

psono-753-stack-psono-db-1       5432/tcp
psono-753-stack-psono-server-1   0.0.0.0:8153->80/tcp, [::]:8153->80/tcp

数据库服务无宿主机端口映射(仅栈内网络可达),对公网面只留 8153 一个入口——符合最小暴露原则;生产替换为 443 经反代。(注意区分:宿主机若另有系统级 PostgreSQL 监听 0.0.0.0:5432,与 Psono 栈无关,属宿主面遗留暴露,应在宿主防火墙单独处理。)

4.2 服务内挂载只读

实测部署已将配置文件以只读方式挂载(docker inspect 回显):

/opt/data/quzuo/tmp/psono/stack/settings.yaml -> /root/.psono_server/settings.yaml RW=false
/opt/data/quzuo/tmp/psono/stack/config.json -> /usr/share/nginx/html/config.json RW=false
/opt/data/quzuo/tmp/psono/stack/config.json -> /usr/share/nginx/html/portal/config.json RW=false

加固含义:即使服务进程被攻破,配置文件(含全部服务端密钥)不可从运行时改写。

五、审计日志

5.1 审计事件模型

服务端内建细粒度审计事件(官方「Audit Log」文档事件清单,2026-10-24 实查节选):每类操作以 SUCCESS/ERROR 成对记录,覆盖管理动作(ADMIN_CREATE_USER_*、ADMIN_DELETE_USERS_*、ADMIN_DELETE_SESSIONS_*)、共享协作(ACCEPT_SHARE_RIGHT_*、ACCEPT_MEMBERSHIP_*、ACCEPT_FILE_REPOSITORY_RIGHT_*)、紧急码(ACTIVATE_EMERGENCY_CODE_*)、API 密钥(ADD_SECRET_TO_API_KEY_*)等数十组事件。实测基线:/server/info/ 回显 log_audit: False,即默认不落审计日志。

5.2 开启与外发

开启外发后实例的安全敏感动作全部可追溯,官方支持三种外发目标(配置目录:Splunk、S3、Logstash,各一篇专项文档):

LOG_AUDIT: True
LOGAUDIT_SPLUNK: {
    'host': 'splunk.example.com',
    'port': 8088,
    'token': '<Splunk HEC token>',
    'source': 'psono',
}

(S3 用 LOGAUDIT_S3、Logstash 用 LOGAUDIT_LOGSTASH,结构同官方文档。)验证:开启后重启,/server/info/ 的 log_audit 翻转为 True,Splunk/S3/Logstash 侧确认收到事件流。

六、服务降权与最小权限

实测组合镜像的进程身份(容器内 ps 回显):

USER     PID  COMM
root       1  supervisord/sh
root      12  python3 (daphne)
root      16  nginx: master
nginx     17  nginx: worker
nginx     18  nginx: worker

事实:官方镜像内 daphne(Python 服务)以 root 运行、nginx worker 已降权为 nginx 用户——这是官方镜像的默认行为,官方文档未提供镜像级非 root 运行参数。不编造「支持非 root」,给出务实补偿:网络层最小暴露(4.1 节)+ 配置只读挂载(4.2 节)+ 反代与 Psono 服务分离部署 + 系统层对数据目录(pgdata/)与配置目录(settings.yaml)收紧权限。

配置文件权限实测:初始挂载时 settings.yaml 为 644,收紧为 600 后重启服务验证正常:

chmod 600 /opt/docker/psono/settings.yaml
ls -l /opt/docker/psono/settings.yaml

实测回显:

-rw------- 1 root root 2625 10月 24 14:03 settings.yaml
curl -s -o /dev/null -w "HTTP=%{http_code}\n" "http://127.0.0.1:8153/server/healthcheck/"

实测回显:

HTTP=200

600 权限下服务读写正常(服务内以 root 读取),而宿主机普通用户已不可读——密钥文件泄漏面收敛。

七、密钥治理与备份恢复

  • 恢复私钥离线化:官方 generateserverkeys 输出原文(部署篇 4.1 节实测转录):「Print this private key and store the printout securely offline. Do NOT copy it into settings.yml or keep it on the server. Anyone with this key may be able to decrypt protected recovery data」——ADMIN_RECOVERY_PRIVATE_KEY 仅纸质/离线介质保存。
  • settings.yaml 即第二把钥匙:官方注释原文「Backup this config and keep it secret. You need it if you ever need to restore your system with your old database」——数据库备份必须与 settings.yaml 成对保存,且 settings.yaml 权限 600、仅限运维可读(6. 节实测)。
  • 备份组合:PostgreSQL(pg_dumpall 或物理备份)+ settings.yaml 离线副本;演练恢复时先恢复数据库、再恢复配置、最后 manage.py migrate 对齐结构。
  • 恢复链路验证:管理员忘记口令/丢 2FA 时用恢复私钥解 ADMIN_RECOVERY_PUBLIC_KEY 保护的数据——恢复能力本身就是攻击面,私钥存放等级应等同生产根密钥。

八、补丁与版本策略

8.1 版本节奏与跟进策略

实测版本时间线(GitLab Tags,2026-10-24):约两个月 12 个版本(月均 5 个),以安全与缺陷修复为主。策略建议:按月跟进最新次版本,关注 GitLab Releases 安全公告;升级窗口避开业务高峰,升级前完成 7 节备份组合。

8.2 官方升级流程

官方 Update 文档流程(update-psono-ce,2026-10-24 实查)转译到 compose 形态:

cd /opt/docker/psono
# 1. 备份(7 节组合)
docker compose exec psono-db pg_dumpall -U psono > backup_$(date +%F).sql
# 2. 拉取新镜像
docker compose pull
# 3. 替换旧服务(组合镜像启动时自动 migrate)
docker compose up -d
# 4. 验证
curl -s "http://127.0.0.1:8153/server/info/" | grep -o '"version[^,]*'
curl -s -o /dev/null -w "health HTTP=%{http_code}\n" "http://127.0.0.1:8153/server/healthcheck/"

回滚路径:官方原文「If anything fails you should be able to restore the database, and start the old docker container again」——镜像标签改回旧版本 up -d,必要时用备份 SQL 恢复数据库。

九、常见问题

9.1 收紧健康检查限流后服务被判不健康

原因与数据(探测共享配额、第二次请求 429 实测)见 2.3 节与配置篇 5.4 节;修复:THROTTLE_RATE_HEALTH_CHECK 恢复默认 '61/hour'。

9.2 开启 HSTS 后部分内网客户端报证书错误

官方把 HSTS 行注释掉的原因即在此:includeSubdomains + 2 年 max-age 对全域强制 HTTPS。解决:先完成全域证书覆盖再启用;启用后短期无法回退(浏览器缓存 HSTS 策略)。

9.3 关闭注册后仍出现陌生账号

排查顺序:确认 settings.yaml ALLOW_REGISTRATION: False 且 Web 客户端 config.json allow_registration: false(两层口子);再查审计日志(5 节)ADMIN_CREATE_USER_* 与注册类事件定位创建来源;EE 环境另查 LDAP/SCIM 同步(目录侧自动建号属正常行为)。

十、参考链接