跳到主要内容

安全配置

概述

本文档介绍 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_ENABLEDtrue启用 TLS
PP_TLS_CERT_DIR/var/security/plantpulse证书目录
PP_TLS_DOMAINplantpulse.io默认域名
PP_TLS_KEYSTORE_PASSWORDkopens123!(必须修改)keystore 密码
PP_TLS_TRUSTSTORE_PASSWORD${PP_TLS_KEYSTORE_PASSWORD}truststore 密码
PP_TLS_VALID_DAYS3650有效期(天)
PP_TLS_EC_GROUPsecp256r1ECDSA 曲线
PP_TLS_SIGALGSHA256withECDSA签名算法
PP_TLS_OPCUA_APP_URIurn:plantpulse:opcua:serverOPC-UA Application URI
PP_TLS_NODE_NAMESmaster worker-1 ... worker-5集群节点名
PP_TLS_SAN_DNSlocalhost,<hostname>,<domain>SAN DNS
PP_TLS_SAN_IPS<HOST_IP>,<SERVICE_IP>,<PUBLIC_IP>,127.0.0.1SAN IP(必须包含 NAT/外网 IP)
PP_TLS_FORCE_REGENERATEfalse为 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
SAN 中混入任何无效 IP 都会导致无法生成证书

PP_TLS_SAN_IPSPP_HOST_IP · PP_SERVICE_IP · PP_MASTER_IP · PP_PUBLIC_IP · 127.0.0.1 组合。任何格式不匹配的值都会导致 openssl 拒绝整个扩展文件。不要在非 NAT 的主机上填写 DOCKER_PP_EXTERNAL_IP

应用连接后端时使用的主机名如果不在 SAN 中会显示 «TimeoutException»

日志中不会出现证书相关信息。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
自签名 CNPP_PROXY_CERT_CN(默认 plantpulse.local

证书文件应为 包含中间证书的完整链(fullchain)

MQTT TLS(1884) 不由代理终止

代理 直通 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_key 就是令牌值

/api/v5/auth 不生成新字符串。若提交的令牌有效,直接返回该令牌值作为 api_keyAPIManagerapi_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
401E1100令牌无效
429E1101暴力破解防护。随附 Retry-After 头部

暴力破解防护是基于 IP 的单一计数器。每次失败时该 IP 的计数器增加,在时间窗口内超过限制时用 429 阻止。在 plantpulse-engine.properties 中调整。

属性默认值
engine.api.v5.auth.bruteforce.enabledtrue
engine.api.v5.auth.bruteforce.ip_limit30
engine.api.v5.auth.bruteforce.window.seconds60 — 存在时优先于 window.minutes
engine.api.v5.auth.bruteforce.window.minutes15
两个头部

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": "..."}
仅此响应不使用 envelope

其他 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 时永久,正整数时为该日数
发放仅限 ADMIN

若请求账户(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-Protection1; mode=block激活浏览器 XSS 过滤器
X-Content-Type-Optionsnosniff防止 MIME 类型嗅探
X-Frame-OptionsSAMEORIGIN防止点击劫持(Clickjacking)
Content-Security-Policydefault-src 'self'限制内容源
Strict-Transport-Securitymax-age=31536000; includeSubDomains强制 HTTPS(HSTS)
Referrer-Policystrict-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 方法。


会话安全

<!-- 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-onlytrue防止通过 XSS 窃取会话
securetrue防止 HTTP 中传输 Cookie(需 HTTPS)
tracking-modeCOOKIE防止会话 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
DMZPlantPulse Web 服务器、API10.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