Sicherheitseinstellungen
Übersicht
Dieses Dokument beschreibt die Konfiguration der Sicherheitseinstellungen der PlantPulse Platform. Durch Sicherheitseinstellungen auf Netzwerk-, Übertragungs-, Anwendungs- und Datenebene können Sie die Platform sicher betreiben.
Wichtig: Bei industriellen Steuerungssystemen (ICS) hat die Verfügbarkeit höchste Priorität. Testen Sie Änderungen an Sicherheitseinstellungen daher unbedingt in einer Testumgebung, bevor Sie sie im Produktivbetrieb anwenden.
Sicherheitsarchitektur
Die Sicherheit von PlantPulse besteht aus 4 Schichten.
| Schicht | Sicherheitsbereich | Hauptfunktion |
|---|---|---|
| 1. Netzwerk | Firewall, Segmentierung, VPN | Blockierung externer Zugriffe, Netzwerktrennung |
| 2. Übertragung | HTTPS/TLS, Verschlüsselung | Verschlüsselung der Kommunikationsdaten |
| 3. Anwendung | Authentifizierung, Autorisierung, Filter, Session | Benutzerauthentifizierung und Berechtigungsverwaltung |
| 4. Daten | DB-Zugriffskontrolle, Verschlüsselung | Schutz gespeicherter Daten |
TLS / Zertifikate (automatische Verwaltung)
Das Erstellen der Zertifikate übernimmt ein einziger plantpulse-certs-Container. Er läuft als Erstes, wenn der Stack hochfährt, erzeugt alle Materialien mit pd_ssl_prepare (plantpulse-datalake-cli von lib/lib-ssl.sh) und schreibt sie in das pp-security-Volume. Danach bleibt er aktiv und prüft alle 6 Stunden auf Ablauf; nur wenn der Ablauf in weniger als 30 Tagen bevorsteht, wird neu ausgestellt. Alle übrigen Container mounten dieses Volume read-only.
| Element | Wert |
|---|---|
| Aussteller | plantpulse-certs (dauerhaft aktiv — (healthy) ist normal) |
| Materialpfad | Volume pp-security, Pfad im Container /var/security/plantpulse |
| Lesende Seite | Datalake und Anwendung — alle :ro |
| Ausnahme | Nur plantpulse-plugin-opcua hat Lese-/Schreibzugriff, da beim Start ein sicheres temporäres Verzeichnis angelegt werden muss |
| Konfiguration | Umgebungsvariable PP_TLS_* im compose |
pd_ssl_prepare stellt nur dann neu aus, wenn der angeforderte SAN sich unterscheidet von dem, was in .certs_generated vermerkt ist. Daher ändert sich das Material bei einem Container-Neustart nicht. Falls eine Neuausstellung nötig ist, führen Sie dies mit dem unten beschriebenen Verfahren explizit durch.
Es handelt sich um restart: unless-stopped — falls ein Mensch den Vorgang absichtlich gestoppt hat, bleibt er auch nach einem Neustart des Docker-Daemons gestoppt. So bleibt ein während der Incident Response manuell eingespieltes Material erhalten.
env.sh TLS-Variablen
| Variable | Standard | Beschreibung |
|---|---|---|
PP_TLS_ENABLED | true | TLS aktivieren |
PP_TLS_CERT_DIR | /var/security/plantpulse | Zertifikatsverzeichnis |
PP_TLS_DOMAIN | plantpulse.io | Standarddomäne |
PP_TLS_KEYSTORE_PASSWORD | kopens123! (muss geändert werden) | 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 | Cluster-Knotenname |
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 muss enthalten sein) |
PP_TLS_FORCE_REGENERATE | false | Bei true erzwungene Neuerstellung |
Verfahren zur Zertifikatserstellung
Da dies bei der Installation automatisch einmal durchgeführt wird, ist normalerweise keine weitere Aktion nötig. Führen Sie das Folgende nur aus, wenn sich der SAN geändert hat oder das Zertifikat abgelaufen bzw. 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. Schon ein einziger Wert mit falschem Format führt dazu, dass openssl die gesamte Extension-Datei ablehnt. Füllen Sie DOCKER_PP_EXTERNAL_IP nicht auf einer Nicht-NAT-Box aus.
Nirgendwo im Log erscheint ein Hinweis auf das Zertifikat. Der Standardwert von PP_TLS_SAN_DNS umfasst einundzwanzig Einträge in drei Gruppen.
| Gruppe | Anzahl | Namen |
|---|---|---|
| Aktuell verwendete Containernamen | 8 | localhost · plantpulse-datalake · plantpulse-server-web · plantpulse-batch-web · plantpulse-warehouse-web · plantpulse-plugin-opcua · plantpulse-plugin-aasx · plantpulse-proxy |
| Alte Namen vor der Umbenennung | 8 | plantpulse-warehouse · plantpulse-server · plantpulse-batch · plantpulse-warehouse-s3 · plantpulse-opcua · plantpulse-aasx · plantpulse-plugin-opcua-server · plantpulse-plugin-aasx-server |
| Worker-Knoten | 5 | plantpulse-datalake-worker-1 ~ -5 |
Bis dahin ausgestellte Zertifikate enthielten keinen einzigen Worker, und auch keine Worker-IPs im IP-SAN (dev 41 gemessen: unter 16 DNS-Namen 0 Worker). Dadurch hatten Clients beim TLS-Handshake mit einem Worker weder einen zu validierenden Namen noch eine Adresse. Cluster, die noch mit vorher erzeugten Zertifikaten arbeiten, müssen neu ausstellen.
Wenn Sie Containernamen geändert oder Backend-Hosts direkt angegeben haben, müssen Sie auch die SAN-Liste entsprechend anpassen.
Wenn Sie auf sechs oder mehr Worker skaliert haben, müssen Sie diese Namen zu PP_TLS_SAN_DNS hinzufügen —
der Standardwert deckt nur bis fünf ab.
:::
Von pd_ssl_prepare erzeugte Artefakte:
/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/ # 클라이언트 인증서 (선택)
Verwendung eines externen CA-Zertifikats
Um statt Selbstsignierung ein firmeneigenes Zertifikat bzw. ein Let's Encrypt CA-Zertifikat 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überwachung: Richten Sie eine externe Überwachung (
openssl x509 -enddate) ein, damit 30 Tage vor Ablauf des Zertifikats ein Alarm ausgelöst wird.
Web-Sicherheitsfilter
PlantPulse schützt Webanfragen über in web.xml definierte Sicherheitsfilter.
SecurityFilter
Der zentrale Filter, 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>
- Prüft bei jeder Anfrage (
/*) den Authentifizierungsstatus. - Auf die in
excludeUrlsangegebenen Pfade kann ohne Authentifizierung zugegriffen werden. - Nicht authentifizierte Anfragen werden zur Login-Seite umgeleitet.
XSSFilter
Ein Filter zur Verhinderung von Cross-Site-Scripting(XSS)-Angriffen.
<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 den Anfrageparametern. - Da für API-Pfade eine separate Eingabevalidierung gilt, können diese vom Filter ausgeschlossen werden.
CSRF-Schutz
Zur Abwehr von Cross-Site-Request-Forgery(CSRF)-Angriffen wird eine tokenbasierte Prüfung durchgeführt.
<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>
- Prüft das CSRF-Token bei POST-, PUT- und DELETE-Anfragen.
- Bei Formularanfragen ist der Parameter
_csrfoder der HeaderX-CSRF-TOKENerforderlich. - REST APIs (
/api/v5/) verwenden Token-Authentifizierung und sind daher vom CSRF-Filter ausgenommen.
EncodingFilter
Vereinheitlicht die Zeichenkodierung, um kodierungsbezogene Sicherheitslücken zu vermeiden.
<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-Einstellungen
Die für Benutzer sichtbaren Ports 80/443 werden von plantpulse-proxy (nginx) terminiert. Der Webserver-Container öffnet keinen Host-Port; der Ort, an dem das für den Browser sichtbare Zertifikat geändert wird, ist daher der Proxy, nicht Tomcat.
Proxy-Zertifikat — Standard ist selbstsigniert
Unmittelbar nach der Installation ist Port 443 ohne jede Vorbereitung sofort geöffnet. Der Proxy erzeugt beim ersten Start ein selbstsigniertes Zertifikat und legt es im Volume pp-proxy-certs ab.
Da es ein stiller Störfall wäre, wenn ein Neustart das echte Zertifikat des Kunden auf ein selbstsigniertes zurücksetzt, verwendet der Proxy die vorhandene Datei weiter, falls sie bereits existiert.
Austausch gegen ein echtes Zertifikat
Die nginx-Konfiguration muss nicht angepasst werden — die Pfade sind fest, es genügt, die Dateien auszutauschen.
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 |
|---|---|
| Volume | pp-proxy-certs |
| Pfad im Container | /etc/nginx/certs/server.crt · /etc/nginx/certs/server.key |
| Selbstsignierter CN | PP_PROXY_CERT_CN (Standard plantpulse.local) |
Legen Sie die Zertifikatsdatei als Kette (Fullchain) inklusive Zwischenzertifikat ab.
Der Proxy leitet 1884 als Passthrough weiter, und der Broker terminiert das TLS. Auch bei einem Wechsel des Proxy-Zertifikats ändert sich daher das vom MQTT-Client geprüfte Zertifikat nicht — dort wird das von plantpulse-certs erzeugte Material verwendet.
(Referenz) Direktes Zertifikat am Webserver
Das Folgende dient als Referenz für Umgebungen, in denen der Webserver ohne Proxy direkt exponiert wird. In der Standardkonfiguration genügt es, nur das Proxy-Zertifikat oben zu ändern. Das TLS-Material für die interne Backend-Strecke im Container wird bereits von plantpulse-certs erzeugt.
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 in server.xml von Tomcat einen HTTPS-Connector 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" />
Weiterleitung von HTTP zu HTTPS
Leitet alle HTTP-Anfragen automatisch zu HTTPS weiter.
<!-- 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
Aus Sicherheitsgründen wird empfohlen, nur TLS 1.2 oder höher zuzulassen.
| TLS-Version | Status | Empfehlung |
|---|---|---|
| SSLv3 | Verboten | POODLE-Schwachstelle |
| TLS 1.0 | Verboten | Vorhandene Schwachstellen |
| TLS 1.1 | Verboten | Vorhandene Schwachstellen |
| TLS 1.2 | Erlaubt | Empfohlen |
| TLS 1.3 | Erlaubt | Aktuellster, sicherster Standard |
API-Authentifizierung
Die PlantPulse REST API (/api/v5) wird mit einem api_key im Header aufgerufen. Es gibt zwei
Wege, einen api_key zu erhalten; die anschließende Aufrufweise ist identisch.
/api/v5/auth erzeugt keinen neuen String. Ist der übermittelte Token gültig, wird derselbe
Token-Wert unverändert als api_key zurückgegeben (APIManager — api_key = token.getToken()). Der Token liegt im
UUID-Format vor und ist kein JWT.
Token-Authentifizierung (externe Integration · Automatisierung)
Erhält den api_key mit einem im Voraus ausgestellten API-Token. Dies ist der Weg für produktive Integrationen.
# 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-Sperre. Der Header Retry-After wird mitgesendet |
Der Brute-Force-Schutz besteht aus einem einzigen IP-basierten Zähler. Bei jedem Fehlversuch
wird der Zähler für diese IP erhöht; wird der Grenzwert innerhalb des Zeitfensters überschritten,
erfolgt eine Sperre mit 429. Einstellbar über plantpulse-engine.properties.
| Property | Standardwert |
|---|---|
engine.api.v5.auth.bruteforce.enabled | true |
engine.api.v5.auth.bruteforce.ip_limit | 30 |
engine.api.v5.auth.bruteforce.window.seconds | 60 — hat, falls vorhanden, Vorrang vor window.minutes |
engine.api.v5.auth.bruteforce.window.minutes | 15 |
X-API-Key hat Priorität; ist er nicht vorhanden, wird als Fallback Authorization: Bearer <api_key> ausgelesen.
Der Bearer-Fallback ist der Standardweg für MCP-Clients, die nur diesen Header senden
können; der Wert ist derselbe api_key. Andere Schemata als Bearer werden ignoriert.
ID/Passwort-Authentifizierung (nur für die Konsolen-SPA)
Erhält den api_key durch Anmeldung mit Benutzername und Passwort. Dies ist der von der Konsolenoberfläche verwendete Weg — verwenden Sie für Server-zu-Server-Integrationen die Token-Authentifizierung, da sonst Klartext-Anmeldedaten bei jedem Aufruf mitgesendet werden und die Brute-Force-Angriffsfläche größer wird.
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 {data, meta, errors}-Format, aber /api/v5/auth/login liefert sowohl bei
Erfolg als auch bei Fehler reines JSON ohne Envelope. Falls Sie einen gemeinsamen Parser
verwenden, behandeln Sie diesen Pfad gesondert.
expires_at ist in Epoch-Millisekunden angegeben, bei einem permanenten Token ist es null.
API zur Ausstellung/zum Widerruf von Token (/api/v5/token)
Führt per REST dieselben Vorgänge aus wie der Konsolenbereich System > API-Token (/token/index).
Wird von automatisierten Provisionierungsskripten zum Erstellen und Löschen von Token verwendet.
Zur Bedienung über die Oberfläche siehe
Sicherheitsverwaltung.
| HTTP | Pfad | Funktion |
|---|---|---|
GET | /api/v5/token | Token-Liste — der Wert von token ist maskiert (erste 8 Zeichen + ****) |
GET | /api/v5/token/{token} | Einzelabfrage — maskiert |
POST | /api/v5/token | Ausstellung |
DELETE | /api/v5/token/{token} | Widerruf (revoke) — ausführbar durch den X-API-Key-Eigentümer |
POST | /api/v5/admin/tokens/{token}/revoke | Widerruf nur für ADMIN — zum Sperren fremder Token |
DELETE /api/v5/token/{token} dient dazu, eigene Token zu bereinigen, und
POST /api/v5/admin/tokens/{token}/revoke dient dazu, dass ein Administrator fremde Token sperrt.
Letzterer akzeptiert nur die Rolle ADMIN — mit einem Token der Rolle API ergibt der Aufruf 403.
In der Incident Response wird Letzterer verwendet.
Beide vermerken revoked_at und invalidieren den Token-Cache, wodurch die Sperre sofort wirksam wird.
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 | Konto, das den Token erhält. Wird es weggelassen, wird der Token für den Aufrufer (den Eigentümer von X-API-Key) ausgestellt |
ip | IP-Muster, für die die Verwendung des Tokens erlaubt ist |
description | Verwendungszweck-Notiz |
ttl_days | Gültigkeitsdauer (Tage). Wird sie weggelassen, gilt der globale Standardwert (engine.api.token.default_ttl_days); bei 0 ist der Token permanent, bei einem positiven Wert gilt diese Anzahl Tage |
Hat das anfragende Konto (der Eigentümer von X-API-Key) nicht die Rolle ADMIN, folgt
403 E1200, und im Audit-Log wird ein Eintrag TOKEN_ISSUE_DENIED vermerkt. Damit soll verhindert
werden, dass ein API-Rollenkonto sich selbst oder fremde Konten durch Rechteausweitung
Token ausstellen kann. Die Rolle des Zielkontos (login_id) spielt dabei keine Rolle.
ttl_days erweitert diese Berechtigung nicht.
ttl_days nötig warWürde man den globalen Standardwert auf 0 (permanent) ändern, würden auch alle vom
Kunden über die Oberfläche erstellten Token permanent. Dieses Feld existiert, damit man
für die Provisionierung nur einen einzelnen permanenten Token benötigt, ohne den globalen
Wert anfassen zu müssen. Negative oder nicht-ganzzahlige Werte führen zu 400 VALIDATION_FAILED —
da es riskanter wäre, ungültige Werte stillschweigend zu akzeptieren und dadurch versehentlich
permanente Token zu erzeugen.
Die erfolgreiche Ausstellungsantwort enthält den Token-Klartext genau einmal. In der Liste
oder Einzelabfrage erscheint er nicht erneut — sichern Sie ihn also sofort. Die Ausstellung
wird im Audit als TOKEN_ISSUED vermerkt; bei permanenten Token wird an der Stelle des
Ablaufdatums PERMANENT eingetragen.
Passwortsicherheit
Passwort-Hashing
PlantPulse speichert Benutzerpasswörter als SHA-256-Hash. Das Originalpasswort wird nicht auf dem Server gespeichert.
Empfohlene Passwortrichtlinie
| Element | Empfehlung | Beschreibung |
|---|---|---|
| Mindestlänge | mindestens 8 Zeichen | 12 Zeichen oder mehr empfohlen |
| Komplexität | Kombination aus mindestens 3 Zeichentypen | Groß-/Kleinbuchstaben, Zahlen, Sonderzeichen — mindestens 3 davon |
| Änderungsintervall | 90 Tage | vierteljährliche Änderung |
| Historienverwaltung | letzte 3 | Wiederverwendung früherer Passwörter untersagt |
| Sperre nach Fehlversuchen | 5 Versuche | Kontosperre nach 5 aufeinanderfolgenden Fehlversuchen |
| Entsperren | nach 30 Minuten oder durch Administrator | automatische oder manuelle Entsperrung |
HTTP-Sicherheitsheader
Zur Aktivierung der Sicherheitsfunktionen des Webbrowsers werden HTTP-Antwortheader konfiguriert.
Empfohlene Sicherheitsheader
| Header | Wert | Beschreibung |
|---|---|---|
X-XSS-Protection | 1; mode=block | Aktiviert den XSS-Filter des Browsers |
X-Content-Type-Options | nosniff | Verhindert MIME-Type-Sniffing |
X-Frame-Options | SAMEORIGIN | Verhindert Clickjacking |
Content-Security-Policy | default-src 'self' | Beschränkt Inhaltsquellen |
Strict-Transport-Security | max-age=31536000; includeSubDomains | Erzwingt HTTPS (HSTS) |
Referrer-Policy | strict-origin-when-cross-origin | Beschränkt Referrer-Informationen |
Nginx-Konfigurationsbeispiel
Wenn Nginx als Reverse-Proxy verwendet wird, fügen Sie die Sicherheitsheader wie folgt 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;
}
Einschränkung von HTTP-Methoden
Blockiert 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 Methode OPTIONS wird für CORS-Preflight-Anfragen verwendet. Bei erforderlicher API-Integration mit externen Systemen muss die OPTIONS-Methode ggf. zugelassen werden.
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-Diebstahl über XSS |
secure | true | Verhindert die Übertragung von Cookies über HTTP (HTTPS erforderlich) |
tracking-mode | COOKIE | Verhindert die Offenlegung der Session-ID in der URL |
Schutz vor Session Fixation
PlantPulse erzeugt bei erfolgreichem Login eine neue Session-ID, um Session-Fixation-Angriffe zu verhindern. Diese Funktion ist fest in SecurityFilter integriert und wird automatisch angewendet, ohne dass eine separate Konfiguration erforderlich ist.
Datenbanksicherheit
PostgreSQL
Legen Sie in der Datei pg_hba.conf fest, welche Zugriffe erlaubt sind.
# /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 문만 로깅
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
Netzwerksicherheit
Firewall-Konfiguration
Lassen Sie in der Firewall nur die Ports zu, die für externe Zugriffe auf PlantPulse tatsächlich benötigt werden.
# 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
Netzwerksegmentierung
In industriellen Umgebungen wird empfohlen, das Netzwerk wie folgt zu trennen.
| Netzwerkbereich | Verwendungszweck | Beispiel |
|---|---|---|
| OT-Netzwerk | PLC, SCADA, Sensoren | 10.0.1.0/24 |
| DMZ | PlantPulse-Webserver, API | 10.0.2.0/24 |
| IT-Netzwerk | Benutzerzugriff, Verwaltung | 10.0.3.0/24 |
| Datennetzwerk | DB, Messaging | 10.0.4.0/24 |
- Achten Sie darauf, dass OT-Netzwerk und IT-Netzwerk 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 aus.
VPN-Zugriff
Falls ein Fernzugriff auf die Verwaltungskonsole erforderlich ist, konfigurieren Sie den Zugriff über VPN.
- Erlauben Sie den Zugriff auf die Verwaltungskonsole und SSH ausschließlich über VPN.
- Vergeben Sie VPN-Konten pro Person und vermeiden Sie gemeinsam genutzte Konten.
- Protokollieren Sie VPN-Zugriffe und überprüfen Sie diese regelmäßig.
Checkliste für Sicherheitsprüfungen
Tägliche Prüfung
| Prüfpunkt | Prüfmethode |
|---|---|
| Ungewöhnliche Anmeldeversuche | Anzahl fehlgeschlagener Logins im Audit-Log prüfen |
| Systemfehlerprotokolle | Sicherheitsrelevante Fehler-/Warnprotokolle prüfen |
| Verfügbarkeit der Dienste | Antwort der Health-Check-API prüfen |
Wöchentliche Prüfung
| Prüfpunkt | Prüfmethode |
|---|---|
| Status der Benutzerkonten | Ungenutzte und inaktive Konten prüfen |
| Verlauf von Berechtigungsänderungen | Berechtigungsänderungen im Audit-Log prüfen |
| Firewall-Protokolle | Blockierte Zugriffsversuche prüfen |
| Gültigkeit der SSL-Zertifikate | Ablaufdatum der Zertifikate prüfen |
Monatliche Prüfung
| Prüfpunkt | Prüfmethode |
|---|---|
| Sicherheitspatches des Betriebssystems | Anwendung der neuesten Sicherheitsupdates prüfen |
| Einhaltung der Passwortrichtlinie | Lange nicht geänderte Passwörter prüfen |
| DB-Zugriffsrechte | Zugriffskontrollen wie pg_hba.conf prüfen |
| Sicherheit der Backup-Daten | Verschlüsselung und Zugriffsrechte der Backup-Dateien prüfen |
| Port-Scan | Unnötig geöffnete Ports prüfen |
| TLS-Einstellungen | Schwache Protokolle/Cipher-Suites prüfen |
Reaktion auf Sicherheitsvorfälle
Gehen Sie bei einem eingetretenen oder vermuteten Sicherheitsvorfall nach folgendem Verfahren vor.
Schritt 1: Erkennung und Isolation
# 의심 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>"
Weder eine Oberfläche noch eine API listen Login-Sessions auf oder beenden sie zwangsweise. Sessions enden erst durch Ablauf oder einen Server-Neustart. Falls eine sofortige Sperrung erforderlich ist, deaktivieren Sie das Konto und widerrufen Sie den API-Token — das sind derzeit die verfügbaren Mittel.
Wer sich wann angemeldet hat, ersehen Sie aus dem Audit-Log
und mittels grep auf system.log im nachfolgenden Schritt 2.
Schritt 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
Schritt 3: Wiederherstellung
- Ändern Sie die Passwörter kompromittierter Konten.
- Überprüfen Sie die Integrität der betroffenen Systeme.
- Stellen Sie bei Bedarf Daten aus dem Backup wieder her.
Schritt 4: Nachsorge
- Erstellen Sie einen Vorfallbericht.
- Legen Sie Maßnahmen zur Vermeidung erneuter Vorfälle fest.
- Verstärken Sie die Sicherheitseinstellungen.
- Führen Sie eine Sicherheitsschulung für die betroffenen Mitarbeiter durch.
Kontakt: Bei sicherheitsrelevanten Fragen wenden Sie sich bitte an webmaster@kopens.com.