安全配置
概述
本文档介绍 PlantPulse 平台的安全配置方法。通过网络、传输、应用程序和数据层级的安全配置,可以安全运营平台。
重要: 工业控制系统(ICS)优先确保可用性,因此在更改安全配置前,务必在测试环境中验证后再应用到运营环境。
安全架构
PlantPulse 的安全由 4 个层级组成。
| 层级 | 安全域 | 主要功能 |
|---|---|---|
| 1. 网络 | 防火墙、分段、VPN | 阻止外部访问、网络隔离 |
| 2. 传输 | HTTPS/TLS、加密 | 通信数据加密 |
| 3. 应用程序 | 认证、授权、过滤、会话 | 用户认证及权限管理 |
| 4. 数据 | DB 访问控制、加密 | 保护存储数据 |
TLS / 证书(自动管理)
证书生成是 plantpulse-certs 一次性容器。堆栈启动时最先执行,它通过 prepare-ssl.sh 生成所有证书材料,写入 pp-security 卷,然后 自动退出。其他容器以只读方式挂载该卷。
| 项目 | 值 |
|---|---|
| 生成主体 | plantpulse-certs(一次性 — Exited (0) 为正常状态) |
| 材料位置 | 卷 pp-security、容器内路径 /var/security/plantpulse |
| 使用方 | 数据湖和应用 — 全部 :ro |
| 例外 | plantpulse-plugin-opcua-server 仅有读写权限。启动时需要创建安全临时目录 |
| 配置 | compose 的 PP_TLS_* 环境变量 |
plantpulse-certs声明为 restart: "no" 并非错误。重新运行会 重新生成证书。仅在需要重新发放时按下述步骤明确执行。
本容器关闭了健康检查(healthcheck: disable),仅通过退出码判断。后续容器不等待 health 而是等待 «正常退出»(service_completed_successfully)。
env.sh TLS 变量
| 变量 | 默认值 | 说明 |
|---|---|---|
PP_TLS_ENABLED | true | 启用 TLS |
PP_TLS_CERT_DIR | /var/security/plantpulse | 证书目录 |
PP_TLS_DOMAIN | plantpulse.io | 默认域名 |
PP_TLS_KEYSTORE_PASSWORD | kopens123!(必须修改) | keystore 密码 |
PP_TLS_TRUSTSTORE_PASSWORD | ${PP_TLS_KEYSTORE_PASSWORD} | truststore 密码 |
PP_TLS_VALID_DAYS | 3650 | 有效期(天) |
PP_TLS_EC_GROUP | secp256r1 | ECDSA 曲线 |
PP_TLS_SIGALG | SHA256withECDSA | 签名算法 |
PP_TLS_OPCUA_APP_URI | urn:plantpulse:opcua:server | OPC-UA Application URI |
PP_TLS_NODE_NAMES | master worker-1 ... worker-5 | 集群节点名 |
PP_TLS_SAN_DNS | localhost,<hostname>,<domain> | SAN DNS |
PP_TLS_SAN_IPS | <HOST_IP>,<SERVICE_IP>,<PUBLIC_IP>,127.0.0.1 | SAN IP(必须包含 NAT/外网 IP) |
PP_TLS_FORCE_REGENERATE | false | 为 true 则强制重生成 |
证书生成步骤
安装时自动执行一次,通常无需操作。仅当 SAN 变更或证书过期/损坏时 执行以下步骤。
cd /opt/kopens/plantpulse-platform-docker
# 1. compose 의 PP_TLS_* 검토 (외부 노출 IP/도메인 포함)
vi compose/docker-compose.yml
# 2. 강제 재생성 — 원샷을 다시 돌립니다
PP_TLS_FORCE_REGENERATE=true \
docker compose -f compose/docker-compose.yml up --force-recreate plantpulse-certs
# 3. 자재를 읽는 쪽을 재시작
bin/restart.sh
PP_TLS_SAN_IPS 由 PP_HOST_IP · PP_SERVICE_IP · PP_MASTER_IP · PP_PUBLIC_IP · 127.0.0.1 组合。任何格式不匹配的值都会导致 openssl 拒绝整个扩展文件。不要在非 NAT 的主机上填写 DOCKER_PP_EXTERNAL_IP。
日志中不会出现证书相关信息。compose 会在 SAN DNS 中同时放入八个容器名(localhost · plantpulse-datalake · plantpulse-server-web · plantpulse-batch-web · plantpulse-warehouse · plantpulse-plugin-opcua-server · plantpulse-plugin-aasx-server · plantpulse-proxy)和改名前的旧名五个。若修改了容器名或直接指定后端主机,必须同时更新 SAN 列表。
prepare-ssl.sh 生成的产出物:
/var/security/plantpulse/
├── ca.crt / ca.key # Self-signed CA
├── server.jks / server.crt # Tomcat (서버)
├── cassandra.jks # Cassandra internode + client
├── kafka.jks # Kafka broker
├── mqtt.jks # HiveMQ
├── opcua/ # OPC-UA
│ ├── server.pem / server.key
│ └── private/ rejected/ trusted/
└── client/ # 클라이언트 인증서 (선택)
使用外部 CA 证书
若要使用公司 / Let's Encrypt CA 证书而非自签名证书:
# 1. CA 발급 인증서를 JKS keystore 로 변환
keytool -importcert -alias rootca -file company-ca.pem \
-keystore /var/security/plantpulse/server.jks \
-storepass ${PP_TLS_KEYSTORE_PASSWORD}
keytool -importkeystore -srckeystore company.p12 -srcstoretype PKCS12 \
-destkeystore /var/security/plantpulse/server.jks \
-deststoretype JKS -deststorepass ${PP_TLS_KEYSTORE_PASSWORD}
# 2. PP_TLS_FORCE_REGENERATE 가 false 인 상태로 재시작
./restart.sh
过期监控:建议设置外部监控(
openssl x509 -enddate),在证书过期前 30 天发出报警。
Web 安全过滤
PlantPulse 通过 web.xml 中定义的安全过滤器保护 Web 请求。
SecurityFilter
这是阻止未认证用户访问的核心过滤器。
<!-- WEB-INF/web.xml inside the webapp (bundled in the WAR — reset on deploy, so change it in the source) -->
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>com.kopens.plantpulse.server.web.filter.SecurityFilter</filter-class>
<init-param>
<param-name>excludeUrls</param-name>
<param-value>/api/v5/ping,/login,/css/,/js/,/images/</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 检查所有请求(
/*)的认证状态。 excludeUrls中指定的路径允许无认证访问。- 未认证的请求被重定向到登录页面。
XSSFilter
防止跨站脚本(XSS)攻击的过滤器。
<filter>
<filter-name>XSSFilter</filter-name>
<filter-class>com.kopens.plantpulse.server.web.filter.XSSFilter</filter-class>
<init-param>
<param-name>excludeUrls</param-name>
<param-value>/api/</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>XSSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 从请求参数中移除
<script>、<iframe>等危险标签。 - API 路径应用单独的输入验证,可从过滤器中排除。
CSRF 防护
基于令牌的验证防止跨站请求伪造(CSRF)攻击。
<filter>
<filter-name>CSRFFilter</filter-name>
<filter-class>com.kopens.plantpulse.server.web.filter.CSRFFilter</filter-class>
<init-param>
<param-name>excludeUrls</param-name>
<param-value>/api/v5/</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CSRFFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 对 POST、PUT、DELETE 请求验证 CSRF 令牌。
- 表单请求需要
_csrf参数或X-CSRF-TOKEN头部。 - REST API(
/api/v5/)使用令牌认证,从 CSRF 过滤器中排除。
EncodingFilter
统一字符编码,防止编码相关安全漏洞。
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.kopens.plantpulse.server.web.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
HTTPS 配置
用户看到的 80 / 443 由 plantpulse-proxy(nginx)终止。Web 服务器容器不暴露主机端口,因此更改浏览器证书的地方不是 Tomcat 而是代理。
代理证书 — 默认为自签名
安装后 443 立即开放,无需任何准备。代理首次启动时生成自签名证书并放入 pp-proxy-certs 卷。
若文件已存在,代理不会重置为自签名证书——这样的意外回退会造成 静默故障,代理会继续使用现有证书。
替换为真实证书
无需修改 nginx 配置——路径是固定的,仅需替换文件。
cd /opt/kopens/plantpulse-platform-docker
# pp-proxy-certs 볼륨의 server.crt / server.key 를 교체
docker cp /path/to/fullchain.crt plantpulse-proxy:/etc/nginx/certs/server.crt
docker cp /path/to/private.key plantpulse-proxy:/etc/nginx/certs/server.key
docker compose -f compose/docker-compose.yml restart plantpulse-proxy
| 项目 | 值 |
|---|---|
| 卷 | pp-proxy-certs |
| 容器内路径 | /etc/nginx/certs/server.crt · /etc/nginx/certs/server.key |
| 自签名 CN | PP_PROXY_CERT_CN(默认 plantpulse.local) |
证书文件应为 包含中间证书的完整链(fullchain)。
代理 直通 1884 流量,由代理终止 TLS。因此更改代理证书不影响 MQTT 客户端验证的证书——那一侧使用 plantpulse-certs 生成的材料。
(参考)直接将证书挂到 Web 服务器
以下是在 Web 服务器无代理直接暴露的环境中的参考。标准配置仅需更改上述代理证书即可。容器内后端段的 TLS 材料由 plantpulse-certs 一次性容器已生成。
使用 HTTPS 需要安装 SSL 证书。
# 1. 키스토어 생성 (자체 서명 인증서 - 테스트용)
keytool -genkeypair -alias plantpulse -keyalg RSA -keysize 2048 \
-validity 365 -keystore /opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks \
-storepass changeit -keypass changeit \
-dname "CN=plantpulse.example.com, OU=IT, O=Company, L=Seoul, ST=Seoul, C=KR"
# 2. 공인 인증서 가져오기 (운영 환경)
keytool -importcert -alias plantpulse -file /path/to/certificate.crt \
-keystore /opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks \
-storepass changeit
# 3. 중간 인증서(Chain) 가져오기
keytool -importcert -alias intermediate -file /path/to/intermediate.crt \
-keystore /opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks \
-storepass changeit
# 4. 인증서 확인
keytool -list -v -keystore /opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks \
-storepass changeit
Tomcat HTTPS 连接器
在 Tomcat 的 server.xml 中添加 HTTPS 连接器。
<!-- plantpulse-server/server/conf/server.xml -->
<!-- HTTP connector (redirects to HTTPS) -->
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="443" />
<!-- HTTPS connector -->
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="200"
SSLEnabled="true"
scheme="https"
secure="true"
keystoreFile="/opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks"
keystorePass="changeit"
clientAuth="false"
sslProtocol="TLSv1.2"
sslEnabledProtocols="TLSv1.2,TLSv1.3"
ciphers="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" />
HTTP 重定向到 HTTPS
自动将所有 HTTP 请求重定向至 HTTPS。
<!-- WEB-INF/web.xml inside the webapp (bundled in the WAR — reset on deploy, so change it in the source) -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Secure</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
TLS 版本管理
出于安全考虑,建议仅允许 TLS 1.2 及以上版本。
| TLS 版本 | 状态 | 建议 |
|---|---|---|
| SSLv3 | 禁用 | POODLE 漏洞 |
| TLS 1.0 | 禁用 | 存在漏洞 |
| TLS 1.1 | 禁用 | 存在漏洞 |
| TLS 1.2 | 允许 | 推荐 |
| TLS 1.3 | 允许 | 最新、最安全 |
API 认证
PlantPulse REST API(/api/v5)通过 在头部中传递 api_key 调用。获取 api_key 的方式有两种,之后的调用方式相同。
/api/v5/auth 不生成新字符串。若提交的令牌有效,直接返回该令牌值作为 api_key(APIManager — api_key = token.getToken())。令牌为 UUID 格式,不是 JWT。
令牌认证(外部集成 · 自动化)
使用预先发放的 API 令牌获得 api_key。此为运营集成路径。
# 1. api_key 발급 — 인증 헤더 없이 호출 가능(V5_Filter bypass 경로)
curl -X POST http://localhost/api/v5/auth \
-H "Content-Type: application/json" \
-d '{"token": "<API_TOKEN>"}'
# 200: {"data": {"api_key": "..."}, "meta": {"status": "OK", ...}, "errors": null}
# 2. 이후 모든 호출에 헤더로 싣는다
curl -X GET http://localhost/api/v5/asset \
-H "X-API-Key: <api_key>"
| 响应 | 含义 |
|---|---|
200 | 发放成功 |
400 | 请求体非 JSON 或缺少 token |
401(E1100) | 令牌无效 |
429(E1101) | 暴力破解防护。随附 Retry-After 头部 |
暴力破解防护是基于 IP 的单一计数器。每次失败时该 IP 的计数器增加,在时间窗口内超过限制时用 429 阻止。在 plantpulse-engine.properties 中调整。
| 属性 | 默认值 |
|---|---|
engine.api.v5.auth.bruteforce.enabled | true |
engine.api.v5.auth.bruteforce.ip_limit | 30 |
engine.api.v5.auth.bruteforce.window.seconds | 60 — 存在时优先于 window.minutes |
engine.api.v5.auth.bruteforce.window.minutes | 15 |
X-API-Key 为第一优先级,不存在时回退读取 Authorization: Bearer <api_key>。Bearer 回退是为了 只能发送该头部的 MCP 客户端 的标准路径,值相同都是 api_key。除 Bearer 外的其他方案会被忽略。
ID/密码认证(控制台 SPA 专用)
通过用户名和密码登录获得 api_key。此为控制台界面使用的路径,服务器间集成应使用令牌认证——以平文凭证形式每次都传输有更宽的暴力破解面。
curl -X POST http://localhost/api/v5/auth/login \
-H "Content-Type: application/json" \
-d '{"userId": "admin", "password": "<PASSWORD>"}'
# 200: {"api_key": "...", "expires_at": 1790000000000, "login_id": "admin"}
# 401: {"error": "..."}
其他 V5 端点采用 {data, meta, errors} 格式,但 /api/v5/auth/login 的成功和失败都返回 平文 JSON。若使用公用解析器,需单独处理此路径。expires_at 为 epoch 毫秒,永久令牌时为 null。
令牌发放·撤销 API(/api/v5/token)
与控制台 System > API 令牌(/token/index)界面功能相同的 REST 接口。自动化配置脚本中用于创建和删除令牌。界面用法见 安全管理。
| HTTP | 路径 | 功能 |
|---|---|---|
GET | /api/v5/token | 令牌列表 — 令牌值已掩码(前 8 字符 + ****) |
GET | /api/v5/token/{token} | 单个查询 — 掩码显示 |
POST | /api/v5/token | 发放 |
DELETE | /api/v5/token/{token} | 撤销(revoke) |
curl -X POST http://localhost/api/v5/token \
-H "X-API-Key: <ADMIN_API_KEY>" \
-H "Content-Type: application/json" \
-d '{"login_id": "svc-mes", "ip": "192.168.0.0/24",
"description": "MES", "ttl_days": 0}'
| 请求体字段 | 说明 |
|---|---|
login_id | 接收令牌的账户。省略时以调用者(X-API-Key 所有者) 身份发放 |
ip | 允许使用令牌的 IP 模式 |
description | 用途备注 |
ttl_days | 有效期(天)。省略时使用全局默认值(engine.api.token.default_ttl_days),0 时永久,正整数时为该日数 |
若请求账户(X-API-Key 所有者)的角色非 ADMIN,返回 403 E1200,审计日志记录一条 TOKEN_ISSUE_DENIED。目的是防止 API 角色账户为自己或他人发放令牌的权限提升。目标账户(login_id)的角色无关。ttl_days 不会扩展此权限。
ttl_days若全局默认值改为 0(永久),客户在界面创建的令牌也全部永久。为了仅让自动化令牌一个永久,避免修改全局设置,设置了此字段。非整数或负数会返回 400 VALIDATION_FAILED — 接纳异常值而静默生成永久令牌的风险更大。
发放成功响应包含 令牌原文(仅一次)。列表和查询中不再显示,请此时保管。发放在审计中记为 TOKEN_ISSUED,永久令牌在过期位置记为 PERMANENT。
密码安全
密码哈希
PlantPulse 使用 SHA-256 哈希存储用户密码。原始密码不在服务器上保存。
密码策略推荐值
| 项目 | 推荐值 | 说明 |
|---|---|---|
| 最小长度 | 8 字符以上 | 推荐 12 字符以上 |
| 复杂度 | 3 种以上组合 | 英文大小写、数字、特殊字符中至少 3 种 |
| 变更周期 | 90 天 | 按季度变更 |
| 历史管理 | 最近 3 个 | 禁止重复使用旧密码 |
| 连续失败锁定 | 5 次 | 5 次连续失败后锁定账户 |
| 解锁方式 | 30 分钟或管理员 | 自动解锁或手动解锁 |
HTTP 安全头部
设置 HTTP 响应头部以激活 Web 浏览器的安全功能。
推荐安全头部
| 头部 | 值 | 说明 |
|---|---|---|
X-XSS-Protection | 1; mode=block | 激活浏览器 XSS 过滤器 |
X-Content-Type-Options | nosniff | 防止 MIME 类型嗅探 |
X-Frame-Options | SAMEORIGIN | 防止点击劫持(Clickjacking) |
Content-Security-Policy | default-src 'self' | 限制内容源 |
Strict-Transport-Security | max-age=31536000; includeSubDomains | 强制 HTTPS(HSTS) |
Referrer-Policy | strict-origin-when-cross-origin | 限制推荐人信息 |
Nginx 配置示例
使用 Nginx 作为反向代理时,按以下方式添加安全头部。
server {
listen 443 ssl http2;
server_name plantpulse.example.com;
# SSL 인증서
ssl_certificate /etc/ssl/certs/plantpulse.crt;
ssl_certificate_key /etc/ssl/private/plantpulse.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 보안 헤더
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
proxy_pass http://127.0.0.1:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# HTTP → HTTPS 리다이렉트
server {
listen 80;
server_name plantpulse.example.com;
return 301 https://$host$request_uri;
}
HTTP 方法限制
阻止不必要的 HTTP 方法,防止信息泄露。
<!-- WEB-INF/web.xml inside the webapp (bundled in the WAR — reset on deploy, so change it in the source) -->
<security-constraint>
<web-resource-collection>
<web-resource-name>RestrictMethods</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>HEAD</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
说明: OPTIONS 方法用于 CORS 预检请求。若需与外部系统进行 API 集成,可能需要允许 OPTIONS 方法。
会话安全
Cookie 安全设置
<!-- WEB-INF/web.xml inside the webapp (bundled in the WAR — reset on deploy, so change it in the source) -->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only> <!-- block cookie access from JavaScript -->
<secure>true</secure> <!-- send cookies over HTTPS only -->
<max-age>1800</max-age> <!-- cookie lifetime (seconds) -->
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
| 设置 | 值 | 说明 |
|---|---|---|
http-only | true | 防止通过 XSS 窃取会话 |
secure | true | 防止 HTTP 中传输 Cookie(需 HTTPS) |
tracking-mode | COOKIE | 防止会话 ID 在 URL 中暴露 |
会话固定攻击防护
PlantPulse 在登录成功时重新生成会话 ID,防止会话固定攻击。此功能内置于 SecurityFilter,无需额外配置,自动应用。
数据库安全
PostgreSQL
在 pg_hba.conf 文件中设置访问许可范围。
# /opt/kopens/plantpulse-platform/plantpulse-storage/db/postgres/data/pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
# 로컬 접근 (Unix 소켓)
local all all md5
# 로컬호스트 접근
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
# PlantPulse 서버 접근 (특정 IP만 허용)
host plantpulse plantpulse 10.0.0.0/24 md5
# 그 외 접근 차단 (기본)
# host all all 0.0.0.0/0 reject
额外安全配置(postgresql.conf):
# 접속 제한
listen_addresses = '127.0.0.1' # 로컬만 허용 (기본값: '*')
max_connections = 200
# 인증 타임아웃
authentication_timeout = 60 # 초
# SSL 활성화
ssl = on
ssl_cert_file = '/path/to/server.crt'
ssl_key_file = '/path/to/server.key'
# 로그
log_connections = on
log_disconnections = on
log_statement = 'ddl' # DDL 문만 로깅
Cassandra
# /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/cassandra.yaml
# 인증 활성화
authenticator: PasswordAuthenticator
# 인가 활성화
authorizer: CassandraAuthorizer
# 클라이언트 암호화 (TLS)
client_encryption_options:
enabled: true
optional: false
keystore: /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/.keystore
keystore_password: changeit
truststore: /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/.truststore
truststore_password: changeit
protocol: TLS
algorithm: SunX509
cipher_suites: [TLS_RSA_WITH_AES_256_CBC_SHA]
# 노드 간 암호화
server_encryption_options:
internode_encryption: all
keystore: /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/.keystore
keystore_password: changeit
truststore: /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/.truststore
truststore_password: changeit
Redis(Valkey)
# /opt/kopens/plantpulse-platform/plantpulse-storage/db/valkey/conf/valkey.conf
# 비밀번호 설정
requirepass your_strong_password_here
# 바인드 주소 (로컬만 허용)
bind 127.0.0.1
# 보호 모드 활성화
protected-mode yes
# 위험 명령어 비활성화
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command DEBUG ""
rename-command SHUTDOWN PLANTPULSE_SHUTDOWN
网络安全
防火墙设置
仅在防火墙中允许 PlantPulse 需要外部访问的端口。
# firewalld 설정 예시
# 웹 서비스 (외부 접근 허용)
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
# 내부 서비스 (외부 접근 차단 - 기본값)
# 아래 포트들은 외부에서 접근하지 않도록 해 주세요
# PostgreSQL: 5432, Redis: 6379, Cassandra: 9042
# Kafka: 9092, MQTT: 1883
# OPC Agent (필요한 클라이언트 IP만 허용)
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="10.0.1.0/24"
port protocol="tcp" port="60000"
accept'
# 적용
sudo firewall-cmd --reload
# 확인
sudo firewall-cmd --list-all
网络分段
在工业环境中,建议按以下方式分离网络。
| 网络区域 | 用途 | 示例 |
|---|---|---|
| OT 网络 | PLC、SCADA、传感器 | 10.0.1.0/24 |
| DMZ | PlantPulse Web 服务器、API | 10.0.2.0/24 |
| IT 网络 | 用户访问、管理 | 10.0.3.0/24 |
| 数据网络 | DB、消息传递 | 10.0.4.0/24 |
- OT 网络和 IT 网络之间不应直接通信。
- 将 PlantPulse 服务器放在 DMZ 中,与两侧网络仅允许受限通信。
- 将数据库放在数据网络中,仅允许从 PlantPulse 服务器访问。
VPN 访问
需要远程访问管理控制台时,通过 VPN 进行配置。
- 管理控制台和 SSH 访问必须仅通过 VPN 允许。
- VPN 账户应按个人发放,不使用共享账户。
- 记录 VPN 访问日志并定期审查。
安全检查清单
日常检查
| 检查项 | 检查方法 |
|---|---|
| 异常登录尝试 | 在审计日志中检查登录失败次数 |
| 系统错误日志 | 检查安全相关错误/警告日志 |
| 服务可用性 | 检查健康检查 API 响应 |
周检
| 检查项 | 检查方法 |
|---|---|
| 用户账户现状 | 检查未使用、非活跃账户 |
| 权限变更历史 | 在审计日志中检查权限变更 |
| 防火墙日志 | 检查被阻止的访问尝试 |
| SSL 证书有效期 | 检查证书过期日期 |
月检
| 检查项 | 检查方法 |
|---|---|
| OS 安全补丁 | 确认应用最新安全更新 |
| 密码策略遵守 | 检查长期未更改的密码 |
| DB 访问权限 | 检查 pg_hba.conf 等访问控制设置 |
| 备份数据安全 | 确认备份文件加密及访问权限 |
| 端口扫描 | 检查不必要打开的端口 |
| TLS 设置 | 检查易受攻击的协议/密码套件 |
安全事故响应
若发生或怀疑发生安全事故,请按以下步骤应对。
第 1 步:检测和隔离
# 의심 IP 차단
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="의심IP" drop' --permanent
sudo firewall-cmd --reload
# 의심 사용자 계정 비활성화 (관리 콘솔 또는 DB)
# 관리 콘솔: 보안 관리 > 사용자 관리 > 상태 변경
# 현재 활성 세션 확인
curl -X GET http://localhost/api/v5/sessions \
-H "Authorization: Bearer <admin-token>"
第 2 步:分析
# 로그인 이력 조회
grep "LOGIN\|LOGOUT\|AUTH" /opt/kopens/plantpulse-platform/plantpulse-server/logs/system.log
# 접근 로그 분석
grep "의심IP" /opt/kopens/plantpulse-platform/plantpulse-server/server/logs/localhost_access_log.*.txt
# DB 접근 로그 (PostgreSQL)
grep "의심IP" /opt/kopens/plantpulse-platform/plantpulse-storage/db/postgres/data/log/postgresql-*.log
第 3 步:恢复
- 修改受损账户的密码。
- 确认受影响系统的完整性。
- 必要时从备份恢复数据。
第 4 步:事后处理
- 编写事故报告。
- 制定再发防止措施。
- 加强安全配置。
- 对相关人员进行安全培训。
联系方式: 安全相关问询请联系 webmaster@kopens.com。