メインコンテンツまでスキップ

セキュリティ設定

概要

このドキュメントでは、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_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 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_REGENERATEfalsetrue の場合は強制再生成

証明書生成手順

インストール時に自動で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
SAN に無効な IP が1つでも混在すると、証明書が「1枚も」生成されません

PP_TLS_SAN_IPSPP_HOST_IP · PP_SERVICE_IP · PP_MASTER_IP · PP_PUBLIC_IP · 127.0.0.1 から構成されます。形式が正しくない値が1つでもあると、openssl が拡張ファイル全体を拒否します。DOCKER_PP_EXTERNAL_IP を NAT ではなくボックスのアドレスで埋めないでください。

アプリがバックエンドに接続する際に使用する名前が SAN に無い場合、「TimeoutException」としてのみ表示されます

ログのどこにも証明書についての情報は出現しません。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
自己署名 CNPP_PROXY_CERT_CN (デフォルト plantpulse.local)

証明書ファイルは、中間証明書を含むチェーン(fullchain) として入力してください。

MQTT TLS(1884)はプロキシが終端しません

プロキシは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_key はトークン値そのものです

/api/v5/auth は新しい文字列を生成しません。提出されたトークンが有効な場合、そのトークン値を api_key として返します(APIManagerapi_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.enabledtrue
engine.api.v5.auth.bruteforce.ip_limit30
engine.api.v5.auth.bruteforce.window.seconds60 — あれば window.minutes より優先
engine.api.v5.auth.bruteforce.window.minutes15
2種類のヘッダ

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 の場合は永続、正の数の場合はその日数
発行は ADMIN のみ可能です

リクエストアカウント(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-Protection1; mode=blockブラウザ XSS フィルタを有効化
X-Content-Type-OptionsnosniffMIME タイプスニッフィング防止
X-Frame-OptionsSAMEORIGINクリックジャッキング(Clickjacking)防止
Content-Security-Policydefault-src 'self'コンテンツソース制限
Strict-Transport-Securitymax-age=31536000; includeSubDomainsHTTPS 強制 (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-onlytrueXSS を通じたセッション横取り防止
securetrueHTTP でクッキーが送信されないようにする (HTTPS 必須)
tracking-modeCOOKIEURL にセッション 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
DMZPlantPulse ウェブサーバ、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 までお問い合わせください。