Sicherheitskonfiguration
Übersicht
Dieses Dokument beschreibt die Sicherheitskonfiguration der PlantPulse-Plattform. Durch Sicherheitseinstellungen auf Netzwerk-, Übertragungs-, Anwendungs- und Datenschicht kann die Plattform sicher betrieben werden.
Wichtig: In industriellen Steuerungssystemen (ICS) hat die Verfügbarkeit oberste Priorität. Validieren Sie alle Änderungen an den Sicherheitseinstellungen zunächst in einer Testumgebung, bevor Sie sie auf die Produktionsumgebung anwenden.
Sicherheitsarchitektur
Die Sicherheit von PlantPulse besteht aus 4 Schichten.
| Schicht | Sicherheitsbereich | Hauptfunktion |
|---|---|---|
| 1. Netzwerk | Firewall, Segmentierung, VPN | Sperrung von Außenzugriff, Netzwerktrennung |
| 2. Übertragung | HTTPS/TLS, Verschlüsselung | Verschlüsselung von Kommunikationsdaten |
| 3. Anwendung | Authentifizierung, Autorisierung, Filter, Sitzung | Benutzerauthentifizierung und Zugriffskontrolle |
| 4. Daten | DB-Zugriffskontrolle, Verschlüsselung | Schutz ruhender Daten |
TLS / Zertifikate (automatische Verwaltung)
Das Zertifikat wird von plantpulse-certs einem einmaligen Container erstellt. Er läuft beim Stack-Start zuerst und erstellt alle Materialien mit prepare-ssl.sh, schreibt sie in das Volumen pp-security und beendet sich dann selbst. Die übrigen Container mounten dieses Volumen schreibgeschützt.
| Element | Wert |
|---|---|
| Ersteller | plantpulse-certs (Einmalig — Exited (0) ist normal) |
| Materialspeicherort | Volumen pp-security, Container-Pfad /var/security/plantpulse |
| Leser | Datalake und App — beide :ro |
| Ausnahme | plantpulse-plugin-opcua-server nur Lese-/Schreibzugriff. Bei der Initialisierung muss ein sicheres temporäres Verzeichnis erstellt werden |
| Konfiguration | Umgebungsvariablen PP_TLS_* in Compose |
plantpulse-certs nicht neuDie Deklaration als restart: "no" ist kein Fehler. Ein Neustart erstellt Zertifikate neu. Führen Sie eine Neuausstellung nur aus, wenn sie erforderlich ist, indem Sie das folgende Verfahren explizit ausführen.
Dieser Container hat keinen Healthcheck (healthcheck: disable) — urteilen Sie nur nach dem Exit-Code. Nachfolgende Container warten nicht auf Health, sondern darauf, ob er normal beendet wurde (service_completed_successfully).
env.sh TLS-Variablen
| Variable | Standard | Beschreibung |
|---|---|---|
PP_TLS_ENABLED | true | TLS aktivieren |
PP_TLS_CERT_DIR | /var/security/plantpulse | Zertifikatverzeichnis |
PP_TLS_DOMAIN | plantpulse.io | Standard-Domain |
PP_TLS_KEYSTORE_PASSWORD | kopens123! (Änderung erforderlich) | Keystore-Passwort |
PP_TLS_TRUSTSTORE_PASSWORD | ${PP_TLS_KEYSTORE_PASSWORD} | Truststore-Passwort |
PP_TLS_VALID_DAYS | 3650 | Gültigkeitsdauer (Tage) |
PP_TLS_EC_GROUP | secp256r1 | ECDSA-Kurve |
PP_TLS_SIGALG | SHA256withECDSA | Signaturalgorithmus |
PP_TLS_OPCUA_APP_URI | urn:plantpulse:opcua:server | OPC-UA Application URI |
PP_TLS_NODE_NAMES | master worker-1 ... worker-5 | Clusterknotenname |
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/externe IP erforderlich) |
PP_TLS_FORCE_REGENERATE | false | true erzwingt Neugenerierung |
Zertifikaterstellungsverfahren
Dies wird während der Installation automatisch einmal durchgeführt. Normalerweise ist kein Handeln erforderlich. Führen Sie Folgendes nur durch, wenn sich die SAN geändert hat oder das Zertifikat abgelaufen oder beschädigt ist.
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 wird aus PP_HOST_IP · PP_SERVICE_IP · PP_MASTER_IP · PP_PUBLIC_IP · 127.0.0.1 zusammengesetzt. Wenn auch nur ein Wert das falsche Format hat, wird die gesamte Erweiterungsdatei von OpenSSL zurückgewiesen. Füllen Sie DOCKER_PP_EXTERNAL_IP nicht in Boxen ohne NAT aus.
Nirgends in den Logs wird etwas über Zertifikate gesagt. Compose fügt acht Containernamen (localhost · plantpulse-datalake · plantpulse-server-web · plantpulse-batch-web · plantpulse-warehouse · plantpulse-plugin-opcua-server · plantpulse-plugin-aasx-server · plantpulse-proxy) und fünf alte Namen vor der Umbenennung in die SAN-DNS-Liste ein. Wenn Sie Container umbenennen oder den Backend-Host direkt angeben, müssen Sie auch die SAN-Liste aktualisieren.
Von prepare-ssl.sh erzeugte Ausgaben:
/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/ # 클라이언트 인증서 (선택)
Externes CA-Zertifikat verwenden
Um anstelle eines selbstsigniert Zertifikates ein CA-Zertifikat von Ihrem Unternehmen oder Let's Encrypt zu verwenden:
# 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
Ablauf-Monitoring: Richten Sie ein externes Monitoring (
openssl x509 -enddate) ein, um 30 Tage vor Zertifikatablauf einen Alarm auszulösen.
Web-Sicherheitsfilter
PlantPulse schützt Web-Anfragen durch in web.xml definierte Sicherheitsfilter.
SecurityFilter
Dies ist der Kernfilter, der den Zugriff nicht authentifizierter Benutzer blockiert.
<!-- 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>
- Überprüft die Authentifizierung für alle Anfragen (
/*). - Pfade in
excludeUrlssind ohne Authentifizierung zugänglich. - Nicht authentifizierte Anfragen werden auf die Login-Seite umgeleitet.
XSSFilter
Ein Filter zur Verhinderung von Cross-Site-Scripting-Angriffen (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>
- Entfernt gefährliche Tags wie
<script>,<iframe>aus Anfrageparametern. - API-Pfade werden durch separate Eingabevalidierung geschützt und können aus dem Filter ausgenommen werden.
CSRF-Schutz
Token-basierte Validierung zur Verhinderung von 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>
- Validiert CSRF-Token bei POST-, PUT- und DELETE-Anfragen.
- Bei Formularanfragen ist der
_csrfParameter oder derX-CSRF-TOKENHeader erforderlich. - REST APIs (
/api/v5/) verwenden Token-Authentifizierung und sind von CSRF-Filterung ausgenommen.
EncodingFilter
Vereinheitlicht Zeichencodierung zur Verhinderung von Sicherheitslücken im Zusammenhang mit Codierung.
<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-Konfiguration
Die Benutzer sehen 80 / 443 wird von plantpulse-proxy(nginx) beendet. Der Web-Server-Container öffnet keine Host-Ports. Der Ort zum Ändern des Browser-Zertifikats ist daher nicht Tomcat, sondern der Proxy.
Proxy-Zertifikat — Standard ist selbstsigniert
Direkt nach der Installation ist 443 ohne Vorbereitung sofort offen. Der Proxy erstellt bei der ersten Initialisierung ein selbstsigniertes Zertifikat und schreibt es in das Volumen pp-proxy-certs.
Ein Neustart, der das echte Zertifikat des Kunden durch selbstsigniert zurücksetzt, ist ein stiller Fehler. Wenn die Datei bereits vorhanden ist, verwendet der Proxy sie wie sie ist.
Mit echtem Zertifikat austauschen
Sie müssen die Nginx-Konfiguration nicht ändern — der Pfad ist fest und Sie müssen nur die Datei austauschen.
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
| Element | Wert |
|---|---|
| Volumen | pp-proxy-certs |
| Container-Pfad | /etc/nginx/certs/server.crt · /etc/nginx/certs/server.key |
| Selbstsignierter CN | PP_PROXY_CERT_CN (Standard plantpulse.local) |
Setzen Sie die Zertifikatdatei als vollständige Kette (Fullchain) einschließlich Zwischenzertifikat.
Der Proxy leitet 1884 als Passthrough weiter und der Broker beendet TLS. Wenn Sie das Proxy-Zertifikat ändern, ändert sich daher nicht das Zertifikat, das der MQTT-Client validiert — das verwendet Material von plantpulse-certs.
(Referenz) Direktes Zertifikat auf dem Web-Server
Dies ist eine Referenz für Umgebungen, in denen der Web-Server ohne Proxy direkt offen liegt. In der Standard-Konfiguration reicht es, nur das Proxy-Zertifikat oben zu ändern. Das TLS-Material für den Backend-Abschnitt im Container wird bereits von plantpulse-certs erstellt.
Um HTTPS zu verwenden, muss ein SSL-Zertifikat installiert werden.
# 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-Connector
Fügen Sie einen HTTPS-Connector zu server.xml des Tomcat hinzu.
<!-- 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" />
Umleitung von HTTP zu HTTPS
Leiten Sie alle HTTP-Anfragen automatisch zu HTTPS um.
<!-- 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-Versionsverwaltung
Für Sicherheit wird empfohlen, nur TLS 1.2 und höher zuzulassen.
| TLS-Version | Status | Empfehlung |
|---|---|---|
| SSLv3 | Deaktiviert | POODLE-Sicherheitslücke |
| TLS 1.0 | Deaktiviert | Sicherheitslücken vorhanden |
| TLS 1.1 | Deaktiviert | Sicherheitslücken vorhanden |
| TLS 1.2 | Zulässig | Empfohlen |
| TLS 1.3 | Zulässig | Aktuell, sicherste |
API-Authentifizierung
Die PlantPulse REST API (/api/v5) wird durch api_key im Header aufgerufen. Es gibt zwei Wege, api_key zu erhalten. Die nachfolgenden Aufrufe sind danach identisch.
/api/v5/auth erstellt keine neue Zeichenkette. Wenn das eingereichte Token gültig ist, wird dieser Token-Wert direkt als api_key zurückgegeben (APIManager — api_key = token.getToken()). Das Token hat UUID-Format und ist nicht JWT.
Token-Authentifizierung (externe Integrationen · Automatisierung)
Erhalten Sie api_key mit einem zuvor ausgestellten API-Token. Dies ist der Pfad, der bei Betriebsintegrationen verwendet wird.
# 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>"
| Antwort | Bedeutung |
|---|---|
200 | Ausstellung erfolgreich |
400 | Body ist kein JSON oder token fehlt |
401 (E1100) | Token ungültig |
429 (E1101) | Brute-Force-Blockade. Retry-After Header ist enthalten |
Die Brute-Force-Verteidigung ist ein Zähler pro IP-Adresse. Bei jedem Fehler erhöht sich der Zähler für diese IP, und wenn die Grenze im Fenster überschritten wird, wird es mit 429 blockiert. Passen Sie dies in plantpulse-engine.properties an.
| Eigenschaft | Standardwert |
|---|---|
engine.api.v5.auth.bruteforce.enabled | true |
engine.api.v5.auth.bruteforce.ip_limit | 30 |
engine.api.v5.auth.bruteforce.window.seconds | 60 — wenn vorhanden, hat Priorität über window.minutes |
engine.api.v5.auth.bruteforce.window.minutes | 15 |
X-API-Key hat Vorrang; wenn nicht vorhanden, wird Authorization: Bearer <api_key> als Fallback gelesen.
Bearer-Fallback ist der Standard-Pfad für MCP-Clients, die nur diesen Header senden können, und der Wert ist das gleiche api_key. Andere Schemata als Bearer werden ignoriert.
ID/Passwort-Authentifizierung (nur Konsolen-SPA)
Erhalten Sie api_key durch Login mit ID und Passwort. Dies ist der Pfad, den der Konsolenbildschirm verwendet. Verwenden Sie Token-Authentifizierung für Server-zu-Server-Integration — dies würde dazu führen, dass unverschlüsselte Anmeldeinformationen jedes Mal gesendet werden, und die Brute-Force-Oberfläche wird breiter.
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": "..."}
Andere V5-Endpunkte verwenden das Format {data, meta, errors}, aber /api/v5/auth/login ist erfolgreich und fehlgeschlagen
unverschlüsseltes JSON. Wenn Sie einen gemeinsamen Parser verwenden, teilen Sie nur diesen Pfad.
expires_at ist Epoch-Millisekunden, und bei ewigen Token ist null.
Token-Ausstellung/-Widerruf API (/api/v5/token)
Führt die gleiche Aktion wie der Konsolenbildschirm System > API-Token (/token/index) über REST durch. Wird in automatischen Provisioning-Skripten verwendet, um Tokens zu erstellen und zu löschen. Für die Verwendung der Benutzeroberfläche siehe Sicherheitsverwaltung.
| HTTP | Pfad | Aktion |
|---|---|---|
GET | /api/v5/token | Token-Liste — Token-Wert ist maskiert(erste 8 Zeichen + ****) |
GET | /api/v5/token/{token} | Einzelabfrage — maskiert |
POST | /api/v5/token | Ausstellung |
DELETE | /api/v5/token/{token} | Widerruf |
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}'
| Body-Feld | Beschreibung |
|---|---|
login_id | Das Konto, das das Token erhält. Wenn weggelassen, wird es für den Aufrufer ausgestellt (X-API-Key Besitzer) |
ip | IP-Muster, die Token-Verwendung zulassen |
description | Verwendungsnotiz |
ttl_days | Gültigkeitsdauer (Tage). Wenn weggelassen, globaler Standard (engine.api.token.default_ttl_days), wenn 0 dann ewig, positive Zahl sind diese Tage |
Wenn die Rolle des anfordernden Kontos (X-API-Key Besitzer) nicht ADMIN ist, ist es 403 E1200 und ein TOKEN_ISSUE_DENIED wird im Audit-Log protokolliert. Dies verhindert Berechtigungserweiterung, bei der API-Rollen-Konten ihre eigenen oder fremde Tokens erstellen. Die Rolle des Zielkontos (login_id) ist unerheblich.
ttl_days erweitert diese Berechtigung nicht.
ttl_days notwendigWenn Sie den globalen Standard auf 0(ewig) ändern, werden alle von Kunden erstellten Tokens ebenfalls für immer. Um nur ein Automation-Token ewig zu halten, ohne die globale Einstellung zu ändern, gibt es diesen Ort.
Negative oder Nicht-Integer-Werte sind 400 VALIDATION_FAILED — eine merkwürdige Absorbierung führt stille zu einem ewigen Token, was gefährlicher ist.
Die erfolgreiche Ausstellungsantwort enthält das Token-Original einmal. Es erscheint nicht wieder in Listen oder Abfragen. Speichern Sie es daher, wenn Sie es erhalten. Die Ausstellung wird mit TOKEN_ISSUED im Audit-Log protokolliert, und ewige Tokens werden mit PERMANENT im Ablauf-Feld registriert.
Passwort-Sicherheit
Passwort-Hashing
PlantPulse speichert Benutzerkennwörter als SHA-256-Hash. Das ursprüngliche Passwort wird nicht auf dem Server gespeichert.
Empfohlene Passwortrichtlinie
| Element | Empfohlener Wert | Beschreibung |
|---|---|---|
| Mindestlänge | 8 Zeichen oder mehr | 12 Zeichen oder mehr empfohlen |
| Komplexität | 3 oder mehr Zeichentypen | Kombination aus Großbuchstaben, Kleinbuchstaben, Zahlen und Sonderzeichen |
| Änderungszyklus | 90 Tage | Vierteljährliche Änderung |
| Versionsverlauf | Letzte 3 | Wiederverwendung früherer Passwörter verhindern |
| Sperrung nach Fehlversuchen | 5 Versuche | Konto nach 5 aufeinanderfolgenden fehlgeschlagenen Versuchen sperren |
| Entsperrung | 30 Minuten oder Administratoren | Automatische oder manuelle Entsperrung |
HTTP-Sicherheits-Header
Sicherheitsheader in HTTP-Antworten werden gesetzt, um Sicherheitsfunktionen des Web-Browsers zu aktivieren.
Empfohlene Sicherheits-Header
| Header | Wert | Beschreibung |
|---|---|---|
X-XSS-Protection | 1; mode=block | Browser XSS-Filter aktivieren |
X-Content-Type-Options | nosniff | MIME-Typ-Sniffing verhindern |
X-Frame-Options | SAMEORIGIN | Clickjacking-Schutz |
Content-Security-Policy | default-src 'self' | Inhaltsquelle einschränken |
Strict-Transport-Security | max-age=31536000; includeSubDomains | HTTPS erzwingen (HSTS) |
Referrer-Policy | strict-origin-when-cross-origin | Referrer-Informationen einschränken |
Nginx-Konfigurationsbeispiel
Wenn Sie Nginx als Reverse Proxy verwenden, fügen Sie wie folgt Sicherheits-Header hinzu.
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-Methodenbeschränkung
Blockieren Sie unnötige HTTP-Methoden, um Informationslecks zu verhindern.
<!-- 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>
Hinweis: Die OPTIONS-Methode wird für CORS-Preflight-Anfragen verwendet. Wenn eine API-Integration mit externen Systemen erforderlich ist, muss die OPTIONS-Methode möglicherweise zulässig sein.
Sitzungssicherheit
Cookie-Sicherheitseinstellungen
<!-- 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>
| Einstellung | Wert | Beschreibung |
|---|---|---|
http-only | true | Verhindert Session-Hijacking durch XSS |
secure | true | Verhindert Cookie-Übertragung über HTTP (HTTPS erforderlich) |
tracking-mode | COOKIE | Verhindert Session-ID-Offenlegung in URLs |
Session Fixation-Schutz
PlantPulse regeneriert die Session-ID nach erfolgreichem Login, um Session Fixation-Angriffe zu verhindern. Diese Funktion ist in SecurityFilter integriert und wird automatisch ohne separate Konfiguration angewendet.
Datenbank-Sicherheit
PostgreSQL
Legen Sie in der Datei pg_hba.conf den Bereich der zulässigen Zugriffe fest.
# /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
Zusätzliche Sicherheitseinstellungen (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
Netzwerksicherheit
Firewall-Konfiguration
Lassen Sie in der Firewall nur die Ports zu, für die PlantPulse einen Außenzugriff benötigt.
# 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
Netzwerk-Segmentierung
In industriellen Umgebungen wird empfohlen, das Netzwerk wie folgt zu unterteilen.
| Netzwerk-Bereich | Verwendung | Beispiel |
|---|---|---|
| OT-Netzwerk | PLC, SCADA, Sensoren | 10.0.1.0/24 |
| DMZ | PlantPulse Web-Server, API | 10.0.2.0/24 |
| IT-Netzwerk | Benutzerzugriff, Verwaltung | 10.0.3.0/24 |
| Datennetzwerk | DB, Messaging | 10.0.4.0/24 |
- OT- und IT-Netzwerk sollten nicht direkt miteinander kommunizieren.
- Platzieren Sie den PlantPulse-Server in der DMZ und erlauben Sie nur eingeschränkte Kommunikation mit beiden Netzwerken.
- Platzieren Sie die Datenbank im Datennetzwerk und erlauben Sie den Zugriff nur vom PlantPulse-Server.
VPN-Zugriff
Wenn Sie von Remote auf die Verwaltungskonsole zugreifen müssen, konfigurieren Sie den Zugriff über VPN.
- Zugriff auf die Verwaltungskonsole und SSH ist nur über VPN zulässig.
- VPN-Konten sollten pro Person ausgestellt werden; verwenden Sie keine gemeinsamen Konten.
- Protokollieren und überprüfen Sie VPN-Zugriffslogs regelmäßig.
Sicherheits-Checkliste
Tägliche Inspektion
| Inspektionselement | Überprüfungsmethode |
|---|---|
| Verdächtige Login-Versuche | Fehlerhafte Login-Versuche im Audit-Log prüfen |
| System-Fehlerprotokolle | Sicherheitsbezogene Fehler/Warnprotokolle überprüfen |
| Dienstverfügbarkeit | Health-Check-API-Antwort überprüfen |
Wöchentliche Inspektion
| Inspektionselement | Überprüfungsmethode |
|---|---|
| Benutzerkonten-Status | Ungenutzte und inaktive Konten überprüfen |
| Berechtigungsänderungs-Verlauf | Berechtigungsänderungen im Audit-Log überprüfen |
| Firewall-Protokolle | Blockierte Zugriffsmeldungen überprüfen |
| SSL-Zertifikat-Gültigkeitsdauer | Zertifikat-Ablaufdatum überprüfen |
Monatliche Inspektion
| Inspektionselement | Überprüfungsmethode |
|---|---|
| OS-Sicherheits-Patches | Neueste Sicherheitsupdates überprüfen |
| Passwortrichtlinie-Einhaltung | Langfristig ungeänderte Passwörter überprüfen |
| DB-Zugriffsberechtigung | pg_hba.conf und andere Zugriffskontrolleinstellungen überprüfen |
| Backup-Daten-Sicherheit | Backup-Dateien-Verschlüsselung und Zugriffsberechtigung überprüfen |
| Port-Scan | Unnötig offene Ports überprüfen |
| TLS-Einstellungen | Schwache Protokolle/Cipher-Suites überprüfen |
Sicherheits-Vorfallreaktion
Wenn ein Sicherheitsvorfall auftritt oder verdächtigt wird, folgen Sie diesem Verfahren.
Stufe 1: Erkennung und Isolierung
# 의심 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>"
Stufe 2: Analyse
# 로그인 이력 조회
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
Stufe 3: Wiederherstellung
- Ändern Sie das Passwort für das betroffene Konto.
- Überprüfen Sie die Integrität der betroffenen Systeme.
- Stellen Sie bei Bedarf Daten aus dem Backup wieder her.
Stufe 4: Nachbereitung
- Erstellen Sie einen Vorfallbericht.
- Erarbeiten Sie Maßnahmen zur Wiederholung.
- Verstärken Sie die Sicherheitseinstellungen.
- Führen Sie Sicherheitsschulung für betroffene Personen durch.
Kontakt: Für Sicherheitsfragen kontaktieren Sie bitte webmaster@kopens.com.