BounCA 0.4.8 配置参数详解

逐项真机验证 services.yaml 六大段参数(secret/psql/email/superuser_signup/admin/key_algorithm/crl_distribution_url 等)与 Nginx/uWSGI 配置:含 yamlreader 列表叠加合并、DisallowedHost 两态调试、psql 端口故障注入、SAN 裸域名规则与 500 排障,附生产环境配置模板。

BounCABounCAPKI 证书信息安全2026/10/28

BounCA 0.4.8 配置参数详解

目标:把 BounCA 0.4.8 的全部运行参数讲透——services.yaml 六大配置段、Django 内置常量、Nginx 站点与 uWSGI 进程模型,每一项关键参数都给出真机改配置→重启→观测行为的生效验证(含错误页形态、算法切换、注册策略、数据库故障注入),并给出生产配置模板与排障实录。部署过程见《Ubuntu 22.04 安装 BounCA 0.4.8 指南》。

一、环境信息

实测机与 B1 相同:Ubuntu 22.04.5 LTS、BounCA 0.4.8(/srv/www/bounca)、PostgreSQL 14.24、Nginx 1.18.0、uWSGI 2.0.20,服务器内网地址 10.89.70.10,应用以 www-data 运行。本文所有「验证」均为真机操作:修改 /etc/bounca/services.yaml → systemctl restart uwsgi → 发起请求观测行为,回显照抄。

二、前置条件

已完成《Ubuntu 22.04 安装 BounCA 0.4.8 指南》全流程。BounCA 的行为由三份配置共同决定:

文件层内容
/etc/bounca/services.yaml应用主配置(人工维护)六大段:psql / django / admin / mail / certificate-engine / registration
/etc/nginx/sites-available/bounca接入层前端静态托管 + /api、/admin 反代 uWSGI socket
/etc/uwsgi/apps-available/bounca.ini进程层WSGI 进程模型、运行身份、socket 路径

三、参数查看与修改方式

3.1 配置加载链(源码级 + 实测)

bounca/settings.py 通过 yamlreader.yaml_load 按从低到高顺序合并三份文件(v0.4.8 起「Better parsing of services.yaml」):

  1. /srv/www/bounca/etc/bounca/services.yaml.defaults(内置默认,最低)
  2. /srv/www/bounca/etc/bounca/services.yaml(应用内,一般不存在)
  3. /etc/bounca/services.yaml(运维主配置,最高)

关键实测发现(容易踩坑):yamlreader 对字典逐键覆盖,但对列表是拼接合并而非替换。实测把 /etc/bounca/services.yaml 的 django.hosts 改成只剩 - localhost,运行时读取结果:

$ env/bin/python manage.py shell -c "from django.conf import settings; print(settings.ALLOWED_HOSTS)"
['localhost', '127.0.0.1', 'localhost']

内置 defaults 里的 localhost、127.0.0.1 始终生效,/etc 层只能追加条目、无法移除默认条目(重复项无害)。同理适用于所有列表型配置。

3.2 修改与生效方式

  1. 编辑 /etc/bounca/services.yaml(YAML 语法,段名大小写敏感,布尔用 True/False);
  2. 应用配置在 Python 进程导入时读取一次,必须重启进程才生效:systemctl restart uwsgi;
  3. 启动即校验:段缺失或 secret 未改会直接抛 ValueError 拒绝启动(见 8.3);
  4. 校验运行时取值:manage.py shell -c "from django.conf import settings; print(settings.XXX)"。

四、参数总览

配置段/文件参数作用默认值
psqldbname / username / password / host / portPostgreSQL 连接四要素+端口bounca / bounca / changeit / postgres / 5432
djangodebug调试模式(错误页形态、CORS 放开)False
djangosecret_keyDjango 会话/CSRF 签名密钥占位串(必须改)
djangohostsDjango ALLOWED_HOSTS 白名单[localhost, 127.0.0.1]
adminenabled是否挂载 /admin、/grappelli 路由True
adminsuperuser_signup是否开放 /accounts/signup/ 超管注册表单True(defaults)
mailhost / port / username / password / connectionSMTP 服务器与加密方式(none/tls/ssl)localhost / 无
mailadmin / from管理员告警收件地址、发件地址admin@example.com / no-reply@example.com
certificate-enginekey_algorithm密钥算法 rsa 或 ed25519rsa
registrationemail_verification注册邮箱验证策略 off/optional/mandatoryoff
settings.py 常量CRL_UPDATE_DAYS_FUTURECRL 有效期天数365
settings.py 常量EMAIL_EXPIRATION_WARNING_DAYS证书到期提醒提前天数(0.4.8 新增)30
Nginxserver_name / location /static / uwsgi_pass / uwsgi_*_timeout接入层见 5.8
uWSGI iniprocesses / thread / socket / uid / chdir / home进程模型8 / 4 / 见 5.9

五、核心参数分类详解

5.1 psql 段:数据库连接

psql:
  dbname: bounca
  username: bounca
  password: q1w2e3r4
  host: 127.0.0.1
  port: 5432
  • 缺任一必需键(dbname/username/password/host)启动即抛 ValueError: Key 'xxx' not found in 'psql' section;连不上库时 Web 侧统一 500、命令行抛 OperationalError。
  • 生效验证(端口故障注入):把 port 改成 5433 重启后,管理后台返回 500;命令行侧报错:
psycopg2.OperationalError: connection to server at "127.0.0.1", port 5433 failed: Connection refused
django.db.utils.OperationalError: connection to server at "127.0.0.1", port 5433 failed: Connection refused

恢复 5432 重启后 /admin/login/ 回到 200。BounCA 仅支持 PostgreSQL(ENGINE=django.db.backends.postgresql_psycopg2),连接复用 CONN_MAX_AGE=10 秒。

5.2 django 段:调试与安全基线

debug(默认 False)。实测对比两种错误形态——伪造 Host: evil.example.com 请求 /admin/login/:

  • debug: False:精简错误页(143 字节,不泄露任何配置):
<!doctype html>
<html lang="en">
<head>
  <title>Bad Request (400)</title>
</head>
<body>
  <h1>Bad Request (400)</h1><p></p>
  • debug: True:Django 调试页,直接回显敏感信息(截取命中关键词):
DisallowedHost at /
Invalid HTTP_HOST header ...
DEBUG = True
ALLOWED_HOSTS

同时 debug=True 时 404 也变成带 URLconf 的调试页(Page not found at /),且 CORS_ORIGIN_ALLOW_ALL=True、日志切到 ./logs/。生产必须 False。

secret_key:占位串不改直接拒绝启动(实测回显见 8.3)。生成方式 python3 -c "import secrets; print(secrets.token_urlsafe(48))";更换后所有会话/Cookie 失效。

hosts:ALLOWED_HOSTS 白名单,语义见 3.1(只能追加)。未列入名单的 Host 访问 Django 路由(/api、/admin)被拒 400;但注意 location / 的前端静态文件由 Nginx 直接返回、不经过该白名单。实测把白名单缩减为 localhost 后,127.0.0.1 仍被放行(defaults 叠加),而 evil.example.com 被拒。

5.3 admin 段:管理后台开关

enabled(默认 True)。实测改为 False 重启:

$ curl -s -o /dev/null -w "%{http_code}\n" -H "Host: localhost" http://127.0.0.1/admin/login/
404

/admin/ 整体下线。安全注意(实测):同一状态下 /grappelli/ 仍返回 200(grappelli 后台资源路由未被同开关拦住),若以内网封禁为策略,建议在 Nginx 层同步 location /grappelli { return 404; }。

superuser_signup:True 时开放 /accounts/signup/ 超管自注册表单与 /accounts/login/ 跳转(实测两路由 200)。官方 README 声明该表单仅在库中无任何用户时可用(空库首装场景),日常应保持 False。

5.4 mail 段:SMTP 与告警

mail:
  host: localhost
  # port: 587
  # username: <user>
  # password: <password>
  # connection: none   # none / tls / ssl
  admin: admin@example.com
  from: no-reply@example.com
  • connection: tls → EMAIL_USE_TLS=True、默认端口 587;ssl → EMAIL_USE_SSL=True、默认端口 465;不配则明文 25。
  • admin 同时是 ADMINS 配置:DEBUG=False 下出现 500 时 Django 会尝试向该地址发告警邮件——所以 mail 段在生产必须可用,否则故障告警静默丢失。
  • 生效验证(间接实证):registration.email_verification: mandatory 且本机无 SMTP 时,注册请求在发送验证邮件环节报 500(回显见 5.6);证明 mail 段被真实使用。证书到期提醒(0.4.8,EMAIL_EXPIRATION_WARNING_DAYS=30)与 CRL 巡检命令同样走该通道。

5.5 certificate-engine 段:密钥算法

certificate-engine:
  key_algorithm: rsa   # 或 ed25519

生效验证(算法切换实测):改为 ed25519 重启后经 API 签发根证书,下载私钥用 openssl 核验:

$ openssl pkey -in root-ed.key -text -noout | head -2
ED25519 Private-Key:
priv:

切回 rsa 后签发的证书:

$ openssl x509 -in gitlab.lab.internal.pem -noout -text | grep Public-Key
                Public-Key: (2048 bit)

规律(与 services.yaml.example 注释一致):rsa 下根/中间 CA 4096 位、叶子证书 2048 位;ed25519 全链现代算法但兼容性差(官方提示 macOS 等场景支持不佳),不确定就保持 rsa。算法是全局开关,切换后仅影响新签发的证书。

5.6 registration 段:注册邮箱验证

email_verification 三态实测(注册端点 POST /api/v1/auth/registration/):

  • off(默认):不需要 email,注册即返回会话 key:
{"key":"d84e1e208d1aba6cb06dceb3683301f020424dcd"}
http=201
  • mandatory:不带 email 直接 400:
{"email":["This field is required."]}
http=400

带 email 但 SMTP 不可用(本机无邮件服务)→ 500(验证邮件发送失败):

<!doctype html>
<html lang="en">
<head>
  <title>Server Error (500)</title>
</head>

结论:mandatory/optional 必须先保证 5.4 的 SMTP 可用,否则注册功能整体瘫痪;内网无邮件服务就保持 off。

5.7 settings.py 内置常量

常量值实证
CRL_UPDATE_DAYS_FUTURE365吊销客户端证书后续签 CRL,openssl crl -text 显示 Last Update: Oct 28 04:51:37 2026 GMT / Next Update: Oct 28 04:52:40 2027 GMT,恰为 +365 天
EMAIL_EXPIRATION_WARNING_DAYS300.4.8 changelog「Send email when certificate is about to expire (Default 30 days)」;send_expire_email 管理命令按此阈值筛选(实测见 D 篇)
TIME_ZONEEurope/Amsterdam硬编码,注意与北京时间差 6~7 小时,读日志/报表时换算
CRL 更新入口API PUT /api/v1/certificates/<pk>/crl0.4.5 起「Generate CRL file updates via button」

5.8 Nginx 站点参数(/etc/nginx/sites-available/bounca)

发行包模板关键段:

server {
  listen 80;
  listen [::]:80;
  server_name _;                       # 发行包默认写死 bounca,必须改
  location /static {
      root /srv/www/bounca/media;      # 注意 root 指向 media,URL 前缀是 /static
      include mime.types;
  }
  location /api {
      include uwsgi_params;
      uwsgi_read_timeout 9600;         # 证书签发耗时长,超时放宽到 160 分钟
      uwsgi_send_timeout 9600;
      uwsgi_pass unix://run/uwsgi/app/bounca/socket;
  }
  location /admin { ... 同上 uwsgi_pass ... }
  location / {
      root /srv/www/bounca/front/dist; # Vuetify 预构建前端
      include mime.types;
      try_files $uri $uri/ /index.html;
  }
}
  • 生效验证:/ 返回 front/dist 的 index.html(不经过 Django,因此 Host 白名单不拦截静态页);/static/admin/css/base.css 200;/api/ 200。404 排障时先分清「Nginx 404」还是「Django 404」。
  • 启用 HTTPS:模板内已带注释好的 443 ssl 段(ssl_certificate /etc/nginx/ssl/fullchain.pem),放开并注释 80 段即可。

5.9 uWSGI 进程参数(/etc/uwsgi/apps-available/bounca.ini)

[uwsgi]
thread=4
master=1
processes=8
vacuum=true
uid = www-data
gid = www-data
chmod-socket = 700
chown-socket = www-data
socket = /run/uwsgi/app/bounca/socket
logto = /var/log/uwsgi/bounca.log
chdir = /srv/www/bounca
home  = /srv/www/bounca/env
module = bounca.wsgi:application
env = DJANGO_SETTINGS_MODULE=bounca.settings

生效验证:8 个 worker 进程全部拉起(ps 计 10 个 uwsgi 进程 = 1 master + 8 worker + 计数差),socket 属主 www-data:

# ls -l /run/uwsgi/app/bounca/
-rw-r--r-- 1 root     root 5 Oct 28 04:03 pid
srwx------ 1 www-data root 0 Oct 28 04:03 socket

调优建议:processes 按 2×CPU 起步(证书签发是 CPU 密集型 RSA 运算),小内存主机同步下调;logto 目录需对 www-data 可写,属主不对是「worker 起不来/no python application found」的高频原因。

六、生产配置模板

适用基线:2 vCPU / 4GB 内存 / 40GB SSD 的单机内网 CA,数十个中间证书、千级叶子证书规模。

/etc/bounca/services.yaml:

psql:
  dbname: bounca
  username: bounca
  password: '<强口令>'
  host: 127.0.0.1
  port: 5432

admin:
  enabled: True
  superuser_signup: False        # 空库首装设 True 建号后立即改回

django:
  debug: False
  secret_key: '<python3 -c "import secrets; print(secrets.token_urlsafe(48))" 生成>'
  hosts:
    - ca.example.internal        # 实际访问域名(defaults 的 localhost/127.0.0.1 始终叠加生效)

mail:
  host: smtp.example.internal
  port: 587
  username: bounca@example.internal
  password: '<SMTP口令>'
  connection: tls
  admin: sre@example.internal
  from: no-reply@example.internal

certificate-engine:
  key_algorithm: rsa

registration:
  email_verification: off        # 邮箱验证改 mandatory 前先确认 SMTP 通

/etc/uwsgi/apps-available/bounca.ini 差异项:processes=4(2C 机型)、其余保持默认。 Nginx 差异项:server_name ca.example.internal;,并追加 /grappelli 封禁(见 5.3);启用 443 ssl 段。

七、常见问题(排障实录)

Q1:/etc/bounca/services.yaml 里删掉 django.hosts 的条目不生效? yamlreader 对列表做拼接合并:defaults 的 localhost、127.0.0.1 恒在(实测 print(settings.ALLOWED_HOSTS) 输出 ['localhost', '127.0.0.1', 'localhost'])。只增不删,重复无害。

Q2:改了 services.yaml 没效果? 配置在进程导入时读取,改完必须 systemctl restart uwsgi(Nginx 配置同理 restart nginx)。

Q3:启动报 ValueError: No 'psql' section found in services.yaml / Set an unique secret key in services.yaml 前者:/etc/bounca/services.yaml 缺失或段名写错,只剩内置 defaults(无 psql/mail/registration 段)——实测移走该文件后 manage.py migrate 即抛此错。后者:secret_key 仍是发行包占位串。两者都直接拒绝启动,属设计行为。

Q4:DisallowedHost: Invalid HTTP_HOST header 'xxx' 访问域名不在 ALLOWED_HOSTS。注意静态前端(location /)不受影响,被拒的是 /api、/admin;往 django.hosts 追加实际域名即可。

Q5:SAN 传 DNS:gitlab.lab.internal 这类带冒号前缀,接口 500/502? 实测定位:DN 字段白名单正则不允许冒号,且校验失败的报错消息里含 % 与 ^,Django ValidationError 做消息格式化时抛 ValueError: unsupported format character '^'(0.4.8 源码 certificate_engine/ssl/certificate.py 的 [@#$%^&+=_,-.] 消息串)。SAN 应传裸域名或 IP(如 gitlab.lab.internal、10.89.70.20),引擎按 DNSName/IPAddress 自动识别(实测裸名签发 201 成功)。

Q6:注册接口 500? email_verification 为 mandatory/optional 但 SMTP 不可用(实测本机无邮件服务时 500)。先修 mail 段或临时回退 off。

Q7:管理后台 404 但想临时开? admin.enabled: True + 重启;注意 /grappelli/ 不会被该开关关闭,封禁需 Nginx 层处理。

八、参考链接

外部来源:

  1. 官方 README(v0.4.8 发行包)配置与安装章节
  2. 发行包模板:etc/bounca/services.yaml.example、etc/nginx/bounca、etc/uwsgi/bounca.ini
  3. bounca/settings.py(v0.4.8 源码,配置加载与常量定义)
  4. yamlreader 库文档(合并语义):https://pypi.org/project/yamlreader/

本系列其他篇目: