OpenWISP 1.3.2 配置参数详解
OpenWISP 1.3.2 settings.py 关键参数详解:数据库与 PostGIS、缓存会话与 Channels、Celery、OPENWISP_* 应用开关、时区语言、邮件与 RADIUS、静态媒体目录与生产加固模板。
OpenWISP 1.3.2 配置参数详解
目标:把 OpenWISP Controller(1.3.2)的
settings.py讲清楚——哪些参数决定数据库、缓存、队列、时区、语言、邮件、静态目录与OPENWISP_*应用行为,每项给出真实生效值与修改后的验证方法。文中生效值在/opt/openwisp/app/openwisp2/settings.py实例上实测,安装步骤见 《OpenWISP 1.3.2 Ubuntu 22.04 安装指南》。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS
- 应用版本:openwisp-controller 1.3.2、Django 5.2.18
- 数据库:PostgreSQL 14.24 + PostGIS 3.2.0(
django.contrib.gis.db.backends.postgis) - 缓存与队列:Redis 6.0.16(本机 6379)
- 应用服务器:uWSGI 2.0.20 + Nginx 1.18.0
- 项目路径:
/opt/openwisp/app,配置文件/opt/openwisp/app/openwisp2/settings.py - 运行用户:
www-data - 站点域名:
openwisp.example.com - 采集日期:2026-10-28
💡 说明:本文只讨论“改配置”,不重复安装过程。所有
manage.py命令都在/opt/openwisp/app目录、以www-data身份执行,虚拟环境为/opt/openwisp/venv。
二、前置条件
修改配置前需要确认三件事,否则改完无法验证:
- 服务可重启:三个 systemd 单元
uwsgi-openwisp、celery-openwisp、celery-beat-openwisp处于active。 - 数据库可连接:
psql -h 127.0.0.1 -U openwisp -d openwisp -c 'select 1'能返回结果。 - Redis 可用:
redis-cli ping返回PONG。
检查命令与实测回显:
systemctl is-active uwsgi-openwisp celery-openwisp celery-beat-openwisp
redis-cli ping
PGPASSWORD=q1w2e3r4 psql -h 127.0.0.1 -U openwisp -d openwisp -tAc "select current_database()"
实测回显:
active
active
active
PONG
openwisp
⚠️ 注意:配置改动后必须同时重启 uWSGI 与两个 Celery 单元。只重启 uWSGI 会导致 Web 端读到新配置、后台任务仍用旧配置(例如缓存库、队列地址不一致)。
三、参数查看与修改方式
OpenWISP 的所有配置都是 Django settings,没有独立的配置文件格式。查看与修改有三条路径:
路径一:直接编辑 settings.py(最常用)
sudo -u www-data vi /opt/openwisp/app/openwisp2/settings.py
路径二:diffsettings 查看“非默认项”
该命令只列出与 Django 默认值不同的配置,适合快速确认自己改了什么:
cd /opt/openwisp/app
sudo -u www-data /opt/openwisp/venv/bin/python manage.py diffsettings | head -20
实测输出节选:
ACCOUNT_ADAPTER = 'openwisp_users.accounts.adapter.EmailAdapter' ###
ACCOUNT_LOGOUT_REDIRECT_URL = 'admin:index' ###
ALLOWED_HOSTS = ['openwisp.example.com', '127.0.0.1', 'localhost']
ASGI_APPLICATION = 'openwisp2.asgi.application' ###
AUTH_USER_MODEL = 'openwisp_users.User'
BASE_DIR = '/opt/openwisp/app' ###
CACHES = {'default': {'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/6'}, ...}
CELERY_BROKER_URL = 'redis://127.0.0.1:6379/1' ###
LANGUAGE_CODE = 'zh-hans'
带 ### 的行表示该值被项目显式覆盖(或由第三方包注入),不带标记的行是 Django 默认值。这个标记能帮你区分“我改的”和“框架自带的”。
路径三:运行时读取
sudo -u www-data /opt/openwisp/venv/bin/python manage.py shell -c "from django.conf import settings as s; print(s.TIME_ZONE, s.LANGUAGE_CODE, s.CACHES['default']['LOCATION'])"
实测回显:
Asia/Shanghai zh-hans redis://127.0.0.1:6379/6
修改后的生效检查顺序:改文件 → manage.py check → 重启三个服务 → 用 shell 或页面验证。check 只做静态检查,不会应用配置,因此不能替代重启。
四、参数总览
把关键参数按“作用域”分组,先给全景,再逐章展开:
| 分组 | 参数 | 本文实测值 | 作用 |
|---|---|---|---|
| 基础 | DEBUG | False | 关闭调试,生产必须 |
| 基础 | ALLOWED_HOSTS | ["openwisp.example.com","127.0.0.1","localhost"] | 限制可访问域名 |
| 基础 | SECRET_KEY | 50 字符随机串 | 会话与签名密钥 |
| 数据库 | DATABASES.default.ENGINE | django.contrib.gis.db.backends.postgis | 空间数据库后端 |
| 数据库 | DATABASES.default.CONN_MAX_AGE | 0(默认) | 连接复用秒数 |
| 缓存 | CACHES.default.LOCATION | redis://127.0.0.1:6379/6 | 默认缓存库 |
| 缓存 | CACHES.sessions.LOCATION | redis://127.0.0.1:6379/8 | 会话库 |
| 会话 | SESSION_ENGINE | django.contrib.sessions.backends.cache | 会话存缓存 |
| 队列 | CELERY_BROKER_URL | redis://127.0.0.1:6379/1 | 任务队列 |
| 队列 | CELERY_RESULT_BACKEND | redis://127.0.0.1:6379/2 | 任务结果 |
| 队列 | CELERY_TASK_TIME_LIMIT | 600 | 单任务硬超时(秒) |
| WebSocket | CHANNEL_LAYERS.default | channels_redis,库 7 | 通知推送 |
| 本地化 | TIME_ZONE | Asia/Shanghai | 时区 |
| 本地化 | LANGUAGE_CODE | zh-hans | 界面语言 |
| 本地化 | USE_TZ / USE_I18N | True / True | 时区与国际化开关 |
| 静态 | STATIC_ROOT | /opt/openwisp/app/static | collectstatic 目标 |
| 媒体 | MEDIA_ROOT | /opt/openwisp/app/media | 上传文件目录 |
| 邮件 | EMAIL_HOST / EMAIL_PORT | 127.0.0.1 / 25 | SMTP 服务 |
| 应用 | OPENWISP_ADMIN_DASHBOARD_ENABLED | True | 后台仪表盘 |
| 应用 | OPENWISP_CONTROLLER_GROUP_PIE_CHART | True | 设备分组饼图 |
| 应用 | OPENWISP_USERS_AUTH_API | True | 用户认证 REST API |
| 应用 | OPENWISP_NOTIFICATIONS_EMAIL_ENABLED | True | 邮件通知 |
读表要点:Redis 的 5 个库各有分工,不要随意合并。合并后会出现“清缓存把会话清掉”“任务结果覆盖缓存键”这类隐蔽故障。
五、数据库与 PostGIS 参数
数据库配置决定平台能否启动。OpenWISP 使用 GeoDjango 的空间字段(设备位置、地理围栏),因此 ENGINE 必须指向 PostGIS 后端。
DATABASES = {
"default": {
"ENGINE": "django.contrib.gis.db.backends.postgis",
"NAME": "openwisp",
"USER": "openwisp",
"PASSWORD": "q1w2e3r4",
"HOST": "127.0.0.1",
"PORT": "5432",
}
}
逐项说明:
ENGINE:写成django.db.backends.postgresql能在无空间字段时启动,但迁移geo、openwisp_ipam应用时会报django.db.utils.ProgrammingError: type "geometry" does not exist。NAME/USER/PASSWORD:数据库名与账号,实测为openwisp/openwisp/q1w2e3r4。HOST/PORT:数据库与应用同机时用127.0.0.1,避免走 Unix Socket 时权限不一致。CONN_MAX_AGE:默认 0(每次请求新建连接)。生产建议 60,减少连接开销;但需确认 PostgreSQL 的max_connections足够(uWSGI 4 worker × 2 线程 + Celery 2 进程)。
验证数据库与空间扩展:
cd /opt/openwisp/app
sudo -u www-data /opt/openwisp/venv/bin/python manage.py shell -c "
from django.db import connection
with connection.cursor() as c:
c.execute('select postgis_version()'); print(c.fetchone()[0])
c.execute(\"select count(*) from information_schema.tables where table_schema='public'\"); print(c.fetchone()[0])
"
实测回显:
3.2 USE_GEOS=1 USE_PROJ=1 USE_STATS=1
57
57 张表覆盖 17 个应用,表数量可作为“迁移是否完整”的快速判据;不同版本表数会有增减,以 showmigrations 全部 [X] 为准。
💡 技术拓展:Django 连接 PostgreSQL 有三种驱动组合——
psycopg2(成熟、C 扩展)、psycopg3(新驱动、支持异步)、psycopg[binary](免编译预编译包)。Django 5.2 同时支持前两者;本文选psycopg[binary],原因是安装快、无需本机pg_config,升级时只需替换 wheel。
六、缓存、会话与 Channels
OpenWISP 把 Redis 当成三套独立设施:Django 缓存、会话存储、Celery 队列,外加 Channels 的 WebSocket 通道层。四者用不同的库号隔离:
| Redis 库 | 用途 | 配置项 | 实测占用 |
|---|---|---|---|
| 1 | Celery 任务队列 | CELERY_BROKER_URL | 3 个键 |
| 2 | Celery 结果后端 | CELERY_RESULT_BACKEND | 2 个键(带过期) |
| 6 | Django 默认缓存 | CACHES.default | 按业务写入 |
| 7 | Channels 通道层 | CHANNEL_LAYERS | 按通知推送写入 |
| 8 | 会话 | CACHES.sessions + SESSION_ENGINE | 2 个键(登录后) |
实测键分布:
redis-cli info keyspace
实测回显:
# Keyspace
db1:keys=3,expires=0,avg_ttl=0
db2:keys=2,expires=2,avg_ttl=85793874
db8:keys=2,expires=2,avg_ttl=1209113584
db8 的 2 个键来自实测登录(会话 + CSRF 相关),db1 的 3 个键来自 Celery worker/beat 心跳与任务投递,db2 的 2 个键带 TTL,符合“结果后端自动过期”的预期。
配置代码:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": f"{REDIS_URL}/6",
},
"sessions": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": f"{REDIS_URL}/8",
},
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "sessions"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {"hosts": [f"{REDIS_URL}/7"]},
}
}
逐项说明:
SESSION_ENGINE选cache后,会话不再落数据库,登录态完全依赖 Redis;Redis 重启会让所有用户掉线,这是可接受的取舍(可用cached_db折中)。SESSION_CACHE_ALIAS必须指向一个存在的缓存别名,写成default会让会话与业务缓存共享键空间。CHANNEL_LAYERS的 7 号库不能与缓存共用,否则FLUSHDB清缓存会同时清掉 WebSocket 通道。openwisp-notifications依赖 Channels 推送站内通知,缺CHANNEL_LAYERS时后台仍能用,但实时通知不工作。
验证缓存读写:
cd /opt/openwisp/app
sudo -u www-data /opt/openwisp/venv/bin/python manage.py shell -c "
from django.core.cache import caches
c = caches['default']; c.set('c1-probe','ok',30); print(c.get('c1-probe'))
"
实测回显:
ok
⚠️ 注意:如果 Redis 设了
maxmemory-policy allkeys-lru,会话键可能被业务缓存挤出。生产建议给会话单独实例,或在redis.conf里用volatile-lru并给会话键设置 TTL。
七、Celery 与异步任务
配置变更推送、通知分发、WHOIS 清理、VPN 密钥生成都在 Celery 里执行。相关配置分四类:
CELERY_BROKER_URL = f"{REDIS_URL}/1"
CELERY_RESULT_BACKEND = f"{REDIS_URL}/2"
CELERY_TASK_TIME_LIMIT = 600
CELERY_BEAT_SCHEDULE = {
"cleanup_unreferenced_whois_records": {
"task": "openwisp_controller.config.whois.tasks.cleanup_unreferenced_whois_records",
"schedule": crontab(hour=2, minute=0),
},
}
CELERY_BROKER_URL:任务投递地址。Redis 作 broker 时没有消息确认机制,任务可能因 worker 崩溃丢失;对配置推送这类可重试任务可接受,对计费类任务应换 RabbitMQ。CELERY_RESULT_BACKEND:保存任务返回值。OpenWISP 主要用它查推送结果,2 号库独立可避免结果键挤占缓存。CELERY_TASK_TIME_LIMIT = 600:单任务硬超时 10 分钟,超时后 worker 被杀。批量推送大配置时若经常触发,应调大该值或拆分批次。CELERY_BEAT_SCHEDULE:定时任务表。实测crontab(hour=2, minute=0)在diffsettings中显示为<crontab: 0 2 * * * (m/h/dM/MY/d)>。
验证 worker 在线与任务注册:
cd /opt/openwisp/app
sudo -u www-data /opt/openwisp/venv/bin/celery -A openwisp2 inspect ping --timeout 8
sudo -u www-data /opt/openwisp/venv/bin/celery -A openwisp2 inspect registered | head -6
实测回显:
-> celery@openwisp-lab: OK
pong
1 node online.
-> celery@openwisp-lab: OK
* openwisp_controller.config.tasks.auto_add_template_to_existing_configs
* openwisp_controller.config.tasks.bulk_invalidate_config_get_cached_checksum
* openwisp_controller.config.tasks.change_devices_templates
* openwisp_controller.config.tasks.create_vpn_dh
💡 技术拓展:任务队列的可靠性有三档——Redis 队列(快、无确认、可能丢任务)、RabbitMQ 带 ack(可重投、可能重复)、数据库队列(最慢、最稳)。OpenWISP 官方文档默认 Redis,本文沿用;若设备规模超过千台且推送不可丢,建议把 broker 换成 RabbitMQ,仅需改
CELERY_BROKER_URL。
八、OPENWISP_* 应用开关
OPENWISP_* 是 OpenWISP 自有命名空间,控制后台界面、API、设备接入与连接器行为。实测 settings 对象中共有 18 个 OPENWISP_* 键,其余未显式设置的项在运行时由组件代码取内置默认值。
后台与 API 开关
| 参数 | 实测值 | 默认值 | 说明 |
|---|---|---|---|
OPENWISP_ADMIN_DASHBOARD_ENABLED | True | True | 后台首页仪表盘 |
OPENWISP_CONTROLLER_GROUP_PIE_CHART | True | True | 设备分组饼图 |
OPENWISP_ORGANIZATION_USER_ADMIN | True | False | 组织内用户管理入口 |
OPENWISP_USERS_AUTH_API | True | False | 用户认证 REST API |
OPENWISP_CONTROLLER_API | 未显式设置 | True | Controller REST API 总开关 |
OPENWISP_CONTROLLER_API_HOST | 未显式设置 | None | 前后端分离时的 API 地址 |
设备接入与连接器
| 参数 | 默认值 | 说明 |
|---|---|---|
OPENWISP_CONTROLLER_BACKENDS | (netjsonconfig.OpenWrt, netjsonconfig.OpenWisp) | 可选的配置后端 |
OPENWISP_CONTROLLER_DEFAULT_BACKEND | OPENWISP_CONTROLLER_BACKENDS[0][0] | 新建配置的默认后端 |
OPENWISP_CONTROLLER_VPN_BACKENDS | OpenVPN / WireGuard / VXLAN over WireGuard / ZeroTier | VPN 服务端类型 |
OPENWISP_CONNECTORS | SSH / OpenWRT SNMP / Ubiquiti AirOS SNMP | 设备连接器 |
OPENWISP_UPDATE_STRATEGIES | OpenWRT SSH / OpenWISP 1.x SSH | 配置更新策略 |
OPENWISP_CONTROLLER_REGISTRATION_ENABLED | True | 允许设备自动注册 |
OPENWISP_CONTROLLER_MANAGEMENT_IP_ONLY | True | 仅用管理 IP 连接设备 |
OPENWISP_CONTROLLER_DEVICE_NAME_UNIQUE | True | 组织内设备名唯一(大小写不敏感) |
OPENWISP_CONTROLLER_HARDWARE_ID_ENABLED | False | 启用硬件序列号字段 |
SSH 超时与安全
| 参数 | 默认值 | 单位 | 说明 |
|---|---|---|---|
OPENWISP_SSH_CONNECTION_TIMEOUT | 5 | 秒 | TCP 建连超时 |
OPENWISP_SSH_AUTH_TIMEOUT | 2 | 秒 | 认证响应超时 |
OPENWISP_SSH_BANNER_TIMEOUT | 60 | 秒 | 等待 SSH Banner |
OPENWISP_SSH_COMMAND_TIMEOUT | 30 | 秒 | 命令阻塞读写超时 |
这些 SSH 超时在本文实例中均未显式写入 settings,运行时由组件内置默认值生效:
cd /opt/openwisp/app
sudo -u www-data /opt/openwisp/venv/bin/python manage.py shell -c "
from django.conf import settings as s
print(getattr(s, 'OPENWISP_SSH_COMMAND_TIMEOUT', '未设置(默认 30)'))
print(getattr(s, 'OPENWISP_CONNECTORS', '未设置(内置 SSH/OpenWRT SNMP/AirOS SNMP)'))
print(getattr(s, 'OPENWISP_CONTROLLER_API', '未设置(默认 True)'))
"
实测回显:
未设置(默认 30)
未设置(内置 SSH/OpenWRT SNMP/AirOS SNMP)
未设置(默认 True)
⚠️ 注意:
OPENWISP_CONTROLLER_WHOIS_ENABLED默认False。若开启,必须同时配置OPENWISP_CONTROLLER_WHOIS_GEOIP_ACCOUNT与OPENWISP_CONTROLLER_WHOIS_GEOIP_KEY,否则组件会抛ImproperlyConfigured,导致后台加载失败。
九、时区、语言、静态与媒体目录
这四项决定“界面长什么样、文件放哪里”,属于上线前必须确认的基础项。
TIME_ZONE = "Asia/Shanghai"
LANGUAGE_CODE = "zh-hans"
USE_TZ = True
USE_I18N = True
STATIC_URL = "/static/"
MEDIA_URL = "/media/"
STATIC_ROOT = os.path.join(BASE_DIR, "static")
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
验证时区与本地时间:
cd /opt/openwisp/app
sudo -u www-data /opt/openwisp/venv/bin/python manage.py shell -c "
from django.utils import timezone
print(timezone.localtime().isoformat())
"
实测回显:
2026-10-28T02:13:11.289809+08:00
+08:00 证明 Asia/Shanghai 已生效;数据库存 UTC(USE_TZ=True),页面按本地时区渲染。
验证语言:登录后访问设备列表,页面标题实测为 选择 Device 来修改 | OpenWISP Admin,中文菜单与“登录 | OpenWISP Admin”登录页说明 zh-hans 已生效。若想保持英文界面,改回 en-gb 并重启即可。
静态与媒体目录验证:
du -sh /opt/openwisp/app/static
sudo -u www-data /opt/openwisp/venv/bin/python manage.py findstatic admin/css/base.css --verbosity 2 | head -5
实测回显:
31M /opt/openwisp/app/static
Found 'admin/css/base.css' here:
/opt/openwisp/venv/lib/python3.10/site-packages/django/contrib/admin/static/admin/css/base.css
STATIC_ROOT是collectstatic的输出目录,不是源目录;源文件在各 Python 包的static/下。MEDIA_ROOT是用户上传目录,必须对www-data可写。实测上传探针文件后通过 Nginx 访问返回200 12:
echo "media-probe" > /opt/openwisp/app/media/probe.txt
sudo chown www-data:www-data /opt/openwisp/app/media/probe.txt
curl -s -o /dev/null -w "%{http_code} %{size_download}\n" -H "Host: openwisp.example.com" http://127.0.0.1/media/probe.txt
实测回显:
200 12
💡 说明:
MEDIA_URL只影响 URL 前缀,实际文件由 Nginx 的location /media/ { alias ... }直接返回,不经过 uWSGI。若把MEDIA_ROOT改到 Nginx 未配置的路径,页面上的图片与固件包会 404。
十、邮件与 RADIUS 联动
10.1 邮件
OpenWISP 用邮件发送邀请、通知与密码重置。配置项如下:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "127.0.0.1"
EMAIL_PORT = 25
EMAIL_USE_TLS = False
DEFAULT_FROM_EMAIL = "openwisp@openwisp.example.com"
实测发送测试邮件(本机未运行 SMTP 服务):
cd /opt/openwisp/app
sudo -u www-data /opt/openwisp/venv/bin/python manage.py sendtestemail admin@example.com
实测回显:
raise err
File "/usr/lib/python3.10/socket.py", line 833, in create_connection
sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused
这说明配置本身被正确读取,失败原因是 127.0.0.1:25 没有监听。两种修复:安装并启动本机 MTA(如 Postfix,监听 25);或把 EMAIL_HOST/EMAIL_PORT 指向企业 SMTP,并按需设置 EMAIL_USE_TLS/EMAIL_HOST_USER/EMAIL_HOST_PASSWORD(密钥类用环境变量注入,不写死在 settings)。
10.2 RADIUS 联动
Controller 本身不做 802.1X 或热点认证,认证由独立的 openwisp-radius 模块提供 REST API,FreeRADIUS 通过 rlm_rest 调用。本文实测环境未安装 openwisp-radius(pip list 中无该包),以下参数取自官方 settings 文档(查询日期 2026-10-28),配置方式为在同一个 settings.py 中追加:
| 参数 | 默认值 | 作用 |
|---|---|---|
FREERADIUS_ALLOWED_HOSTS | [] | 允许调用 RADIUS API 的 FreeRADIUS 主机 IP/网段 |
OPENWISP_RADIUS_API | True | 是否启用 RADIUS REST API |
OPENWISP_RADIUS_API_BASEURL | / | API 对外基地址(独立域名时改成 https://radius.example.com) |
OPENWISP_RADIUS_API_URLCONF | None | 独立 API 实例的 urlconf |
同机部署的最小写法:
INSTALLED_APPS += ["openwisp_radius"]
FREERADIUS_ALLOWED_HOSTS = ["127.0.0.1"]
OPENWISP_RADIUS_API = True
OPENWISP_RADIUS_API_BASEURL = "https://openwisp.example.com"
联动要点:
FREERADIUS_ALLOWED_HOSTS必须填 FreeRADIUS 所在主机地址;同机填127.0.0.1,跨机填内网 IP,绝不能留空。- 客户端地址取
REMOTE_ADDR,若 FreeRADIUS 前面有反向代理,代理必须把真实地址写入REMOTE_ADDR,X-Forwarded-For不被采信。 - RADIUS 与 Controller 共用 PostgreSQL 时,
openwisp_radius的迁移与 Controller 迁移在同一库执行,升级时按依赖顺序 migrate 即可。 - 安装该模块后需要重启三个服务,并用
manage.py showmigrations openwisp_radius确认迁移全部应用。
⚠️ 注意:不要在未安装
openwisp-radius的情况下把OPENWISP_RADIUS_*写进INSTALLED_APPS,否则manage.py check会报ModuleNotFoundError: No module named 'openwisp_radius'。
十一、生产配置模板
以下模板面向“4 vCPU、16 GB 内存、NVMe SSD、500 台 OpenWrt 设备”的生产场景,在本文基础上补齐 HTTPS 安全项、连接复用与日志轮转。安全项单独放在 settings_hardened.py 便于灰度:
from .settings import * # noqa
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"
DATABASES["default"]["CONN_MAX_AGE"] = 60
实测校验(--settings=openwisp2.settings_hardened):
cd /opt/openwisp/app
sudo -u www-data /opt/openwisp/venv/bin/python manage.py check --deploy --settings=openwisp2.settings_hardened
实测输出节选:
WARNINGS:
?: Geocoding service is experiencing issues or is not properly configured
System check identified 1 issue (0 silenced).
对比未加固配置的 5 条告警(HSTS、SSL 重定向、Session Cookie、CSRF Cookie、地理编码),加固后只剩地理编码一条,说明 4 条 HTTPS 告警已被消除。
模板选择说明:
| 场景 | 建议值 | 理由 |
|---|---|---|
| 500 台设备、4 vCPU | uWSGI processes=4、threads=2 | 每进程约 50 MB,4 进程够用 |
| 数据库同机、16 GB | CONN_MAX_AGE=60 | 减少握手;连接数 4×2+2 可控 |
| 有 TLS 终止 | SECURE_PROXY_SSL_HEADER 必须有 | 否则 Django 认为请求是 HTTP,重定向死循环 |
| 无 TLS(内网测试) | 不要启用 SECURE_SSL_REDIRECT | 否则登录页 301 到 https 无法访问 |
| 需要审计 | LOGGING 保留文件 handler + 轮转 | 默认只输出控制台,journald 也能收 |
⚠️ 注意:
SECURE_HSTS_SECONDS=31536000一旦下发,浏览器会在一年内强制 HTTPS,且includeSubDomains会影响所有子域。上线前确认域名与证书全部就绪,否则回退非常麻烦。
十二、常见问题
12.1 改了 settings 但页面没变化
原因:uWSGI 是多进程常驻,不会自动重载;Celery worker 也在启动时读取配置。
处理:systemctl restart uwsgi-openwisp celery-openwisp celery-beat-openwisp,再用 manage.py shell 读一次生效值确认。
12.2 check --deploy 报 5 条安全告警
现象:W004(HSTS)、W008(SSL 重定向)、W012(Session Cookie Secure)、W016(CSRF Cookie Secure)加一条地理编码告警。
处理:按第十一章模板补齐 4 个 HTTPS 项;地理编码告警按 12.3 处理。补齐后实测只剩 1 条。
12.3 地理编码告警是否必须消除
现象:?: Geocoding service is experiencing issues or is not properly configured。
原因:组件注册的地理编码健康检查访问 geocode.arcgis.com,隔离网络返回 Network is unreachable。
处理:不影响启动与业务。要么放通该域名,要么忽略并在设备位置手工填经纬度。
12.4 会话频繁掉线
排查顺序:redis-cli info keyspace 看 db8 是否有键;redis-cli config get maxmemory-policy 看是否 allkeys-lru;检查是否有人执行了 redis-cli flushall。若是内存淘汰导致,给会话单独实例或改 volatile-lru。
12.5 修改 LANGUAGE_CODE 后部分页面仍是英文
原因:部分第三方包(如 DRF 浏览页、django-allauth 邮件模板)自带翻译缺失,或浏览器 Accept-Language 优先。
处理:USE_I18N=True 时以请求语言为准;确认浏览器语言为中文,或在 MIDDLEWARE 前加 LocaleMiddleware 并设置 LANGUAGE_COOKIE_NAME 固定语言。
十三、参考链接
- Controller 全部配置项:
https://openwisp.io/docs/stable/controller/user/settings.html - RADIUS 配置项:
https://openwisp.io/docs/stable/radius/user/settings.html - Django settings 参考:
https://docs.djangoproject.com/en/5.2/ref/settings/ - Django 缓存框架:
https://docs.djangoproject.com/en/5.2/topics/cache/ - Celery 配置:
https://docs.celeryq.dev/en/stable/userguide/configuration.html - Channels 层:
https://channels.readthedocs.io/en/latest/topics/channel_layers.html - PostGIS 文档:
https://postgis.net/documentation/
站内延伸阅读:《OpenWISP 介绍与选型指南》、《OpenWISP 1.3.2 Ubuntu 22.04 安装指南》、《OpenWISP 1.3.2 常用 CLI 与运维命令详解》。