セキュリティ設定
概要
このドキュメントでは、PlantPulseプラットフォームのセキュリティ設定方法を説明します。ネットワーク、転送、アプリケーション、データレベルのセキュリティ設定により、プラットフォームを安全に運用できます。
重要: 産業制御システム(ICS)は可用性が最優先であるため、セキュリティ設定を変更する際は、必ずテスト環境で検証してから本番環境に適用してください。
セキュリティアーキテクチャ
PlantPulseのセキュリティは4つのレイヤで構成されます。
| レイヤ | セキュリティ領域 | 主要機能 |
|---|---|---|
| 1. ネットワーク | ファイアウォール、セグメンテーション、VPN | 外部接続ブロック、ネットワーク分離 |
| 2. 転送 | HTTPS/TLS、暗号化 | 通信データの暗号化 |
| 3. アプリケーション | 認証、認可、フィルタ、セッション | ユーザ認証と権限管理 |
| 4. データ | DB接続制御、暗号化 | 保存データの保護 |
TLS / 証明書 (自動管理)
証明書を生成するのは**plantpulse-certs ワンショットコンテナ1個**です。スタック起動時に最初に実行されて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 の場合は強制再生成 |
証明書生成手順
インストール時に自動で1回実行されるため、通常は何もする必要がありません。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 から構成されます。形式が正しくない値が1つでもあると、openssl が拡張ファイル全体を拒否します。DOCKER_PP_EXTERNAL_IP を NAT ではなくボックスのアドレスで埋めないでください。
ログのどこにも証明書についての情報は出現しません。compose は SAN DNS にコンテナ名8個(localhost · plantpulse-datalake · plantpulse-server-web · plantpulse-batch-web · plantpulse-warehouse · plantpulse-plugin-opcua-server · plantpulse-plugin-aasx-server · plantpulse-proxy)と改名前の旧名前5個を一緒に追加します。コンテナ名を変更したか、バックエンドホストを直接指定した場合は、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
期限切れ監視: 証明書期限30日前にアラームが発生するよう、外部監視(
openssl x509 -enddate)を設定してください。
ウェブセキュリティフィルタ
PlantPulse は web.xml に定義されたセキュリティフィルタを通じてウェブリクエストを保護します。
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
Cross-Site Scripting(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 対策
Cross-Site Request Forgery(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)が終端します。 ウェブサーバコンテナはホストポートを開かないため、ブラウザ用の証明書を変更する場所は 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 が生成したリソースを使用します。
(参考) ウェブサーバに直接証明書を設定する場合
以下は、ウェブサーバをプロキシなしで直接公開する環境向けの参考です。標準構成では上記のプロキシ証明書のみ変更すれば十分です。コンテナ内バックエンド間の 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 を取得するパスは2つあり、その後の呼び出し方法は同じです。
/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 単位のカウンタ1種です。失敗するたびにその 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/Password 認証 (コンソール SPA 専用)
ID・パスワードでログインして 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 はエポックミリ秒で、永続トークンの場合は null です。
トークン発行・失効 API (/api/v5/token)
コンソールSystem > API トークン(/token/index)画面と同じ処理を REST で実行します。自動配線スクリプトでトークンを作成・削除する際に使用します。画面の使用方法については
セキュリティ管理 を参照してください。
| HTTP | パス | 処理内容 |
|---|---|---|
GET | /api/v5/token | トークン一覧 — 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 が1件記録されます。API ロールアカウントが自身または他人のトークンを生成する
権限昇格を防ぐためです。対象アカウント(login_id)のロールは無関係です。
ttl_days はこの権限を拡張しません。
ttl_days が必要な理由グローバルデフォルトを 0(永続)に変更すると、顧客が画面で作成したトークンまですべて永続になります。
自動配線用トークン1個のみを永続にしたいのに、グローバルを変更する必要をなくすための項目です。
負の数または整数でない値は400 VALIDATION_FAILED です — 不正な値を黙って受け入れて、静かに
永続トークンになる方がより危険だからです。
発行成功レスポンスにはtoken 原文が1回含まれます。一覧・照会では再び表示されないため、
ここで受け取って保管してください。発行は監査に TOKEN_ISSUED として記録され、永続トークンは期限欄に
PERMANENT として記録されます。
パスワードセキュリティ
パスワードハッシング
PlantPulse はユーザパスワードを SHA-256 ハッシュとして保存します。元のパスワードはサーバに保存されません。
パスワードポリシー推奨値
| 項目 | 推奨値 | 説明 |
|---|---|---|
| 最小長 | 8文字以上 | 12文字以上推奨 |
| 複雑度 | 3種類以上の組合せ | 英大文字/小文字、数字、特殊文字の中から3種類 |
| 変更周期 | 90日 | 四半期ごとの変更 |
| 履歴管理 | 最直近3個 | 以前のパスワード再利用禁止 |
| 連続失敗ロック | 5回 | 5回連続失敗時にアカウントロック |
| ロック解除 | 30分または管理者 | 自動解除または手動解除 |
HTTP セキュリティヘッダ
ウェブブラウザのセキュリティ機能を有効化するため、HTTP レスポンスヘッダを設定します。
推奨セキュリティヘッダ
| ヘッダ | 値 | 説明 |
|---|---|---|
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 メソッドを許可する必要があります。
セッションセキュリティ
クッキーセキュリティ設定
<!-- 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 でクッキーが送信されないようにする (HTTPS 必須) |
tracking-mode | COOKIE | URL にセッション ID が露出しないようにする |
Session Fixation 防止
PlantPulse はログイン成功時にセッション ID を再生成し、Session Fixation 攻撃を防止します。この機能は 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 ウェブサーバ、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 までお問い合わせください。