跳到主要内容

安全设置

概述​

本文档介绍 PlantPulse 平台的安全设置方法。通过网络、传输、应用和数据层面的安全设置,可以确保平台安全运行。

重要: 工业控制系统 (ICS) 以可用性为最优先,因此在更改安全设置时,务必先在测试环境中验证,然后再应用到生产环境。

安全架构​

PlantPulse 的安全防护由 4 个层级组成。

层级安全领域主要功能
1. 网络防火墙、分段、VPN阻断外部访问,网络隔离
2. 传输HTTPS/TLS、加密通信数据加密
3. 应用认证、授权、过滤、会话用户认证及权限管理
4. 数据数据库访问控制、加密存储数据保护

TLS / 证书(自动管理)​

生成证书的是一个 plantpulse-certs 容器。当堆栈启动时,它最先运行,并通过 pd_ssl_prepare(plantpulse-datalake-cli 的 lib/lib-ssl.sh)创建所有材料,然后写入 pp-security 卷中。之后,它会持续运行,每 6 小时检查一次过期情况,并且只有在证书进入到期前 30 天时才会重新签发。其余容器将此卷以只读方式挂载并使用。

项目值
生成主体plantpulse-certs(常驻运行 — (healthy) 正常)
材料位置卷 pp-security,容器内路径 /var/security/plantpulse
读取方数据湖和应用 — 全部为 :ro
例外只有 plantpulse-plugin-opcua 是读写模式。因为启动时需要创建安全临时目录
设置compose 中的 PP_TLS_* 环境变量
重启后不会重新生成证书

只有当请求的 SAN 与 .certs_generated 中记录的不同时,pd_ssl_prepare 才会重新签发。因此, 即使重启容器,材料也不会改变。如果需要重新签发,请按以下流程明确操作。

这是 restart: unless-stopped 的情况 — 如果被人为停止,即使重启 Docker 守护进程也会保持停止状态。 这是为了在事故应对期间,手动放入的材料得以保留。

env.sh TLS 变量​

变量默认值说明
PP_TLS_ENABLEDtrueTLS 启用
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 应用程序 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_IPS 由 PP_HOST_IP · PP_SERVICE_IP · PP_MASTER_IP · PP_PUBLIC_IP · 127.0.0.1 组装而成。只要有一个格式不正确的值,openssl 就会拒绝整个扩展文件。请勿在非 NAT 的机器上填写 DOCKER_PP_EXTERNAL_IP。

如果应用连接后端时使用的名称不在 SAN 中,只会显示为«TimeoutException»

日志中不会有任何关于证书的信息。PP_TLS_SAN_DNS 的默认值是二十一个, 分为三组。

组别数量名称
当前使用的容器名称8localhost · plantpulse-datalake · plantpulse-server-web · plantpulse-batch-web · plantpulse-warehouse-web · plantpulse-plugin-opcua · plantpulse-plugin-aasx · plantpulse-proxy
改名前的旧名称8plantpulse-warehouse · plantpulse-server · plantpulse-batch · plantpulse-warehouse-s3 · plantpulse-opcua · plantpulse-aasx · plantpulse-plugin-opcua-server · plantpulse-plugin-aasx-server
工作节点5plantpulse-datalake-worker-1 ~ -5
工作节点名称已于 2026-09-09 加入

在此之前签发的证书中没有包含任何工作节点,工作节点 IP 也不在 IP SAN 中 (dev 41 实测:十六个 DNS 名称中工作节点 0 个)。因此,工作节点终止 TLS 时, 客户端既没有可验证的名称,也没有地址。如果集群仍在使用之前生成的证书,则需要进行重新签发。

如果更改了容器名称或直接指定了后端主机,也必须同时修改 SAN 列表。 如果将工作节点扩展到六个或更多,也需要在 PP_TLS_SAN_DNS 中添加这些名称 — 默认值最多为五个。 :::

pd_ssl_prepare 生成的产物:

/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 端口以 passthrough 方式转发,由 Broker 终结 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_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 头

暴力破解防御是 1 种基于 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 之外的任何其他 scheme。

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)​

使用 REST 完成与控制台 System > API 令牌(/token/index)界面相同的操作。用于 在自动布线脚本中创建和删除令牌。界面使用方法请参阅 安全管理。

HTTP路径功能
GET/api/v5/token令牌列表 — token 值会被掩码(前 8 位 + ****)
GET/api/v5/token/{token}单个查询 — 掩码
POST/api/v5/token签发
DELETE/api/v5/token/{token}回收 (revoke) — 执行者为 X-API-Key 所有者
POST/api/v5/admin/tokens/{token}/revoke仅限 ADMIN 回收 — 用于断开他人的令牌
为什么有两条回收路径

DELETE /api/v5/token/{token} 是清理自己的令牌的路径, 而 POST /api/v5/admin/tokens/{token}/revoke 是管理员断开他人令牌的路径。 后者只允许 ADMIN 角色通过 — 使用 API 角色令牌调用也会返回 403。 事故应对中使用的是后者。

两者都会记录 revoked_at 日志并使令牌缓存失效,因此会立即阻止。

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,并且审计日志中 会记录 1 条 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 安全头​

为启用 Web 浏览器的安全功能,设置 HTTP 响应头。

推荐的 HTTP 安全头​

头值说明
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 문만 로깅

Aster TSDB​

# /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
数据网络数据库、消息传递10.0.4.0/24
  • 请确保 OT 网络和 IT 网络不直接通信。
  • 将 PlantPulse 服务器置于 DMZ 中,仅允许其与两侧网络进行受限通信。
  • 将数据库置于数据网络中,并仅允许 PlantPulse 服务器访问。

VPN 访问​

如果需要远程访问管理控制台,请配置为通过 VPN 连接。

  • 管理控制台和 SSH 访问必须仅通过 VPN 允许。
  • VPN 账户应逐个签发,请不要使用共享账户。
  • 记录 VPN 连接日志并定期审查。

安全巡检清单​

每日巡检​

巡检项目确认方法
异常登录尝试在审计日志中检查登录失败次数
系统错误日志检查与安全相关的错误/警告日志
服务可用性检查健康检查 API 的响应

每周巡检​

巡检项目确认方法
用户账户状况检查未使用账户、非活动账户
权限变更历史在审计日志中检查权限变更
防火墙日志检查被阻止的访问尝试
SSL 证书有效期检查证书到期日

每月巡检​

巡检项目确认方法
OS 安全补丁确认已应用最新的安全更新
密码策略遵守情况检查长期未更改的密码
数据库访问权限检查 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)
# 관리 콘솔: 보안 관리 > 사용자 관리 > 상태 변경

# 의심 계정의 API 토큰을 즉시 회수 (ADMIN 전용 — 아래 참고)
curl -X POST http://localhost/api/v5/admin/tokens/<TOKEN>/revoke \
-H "X-API-Key: <ADMIN_API_KEY>"
产品中没有活动«会话»列表

没有用于列出登录会话或强制断开的界面,也没有相应的 API。会话只能等待过期或 重启服务器来断开。如果需要立即阻止,请禁用账户并回收 API 令牌 — 这是目前可用的手段。

关于谁在何时进入,请查看审计日志和下面第 2 步的 system.log grep。

第 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。