Password Changes (Credential Rotation)
Overview
PlantPulse uses service accounts to access internal infrastructure components (PostgreSQL · Cassandra · Valkey · MinIO · Kafka · MQTT · Hive/Kyuubi · Temporal). The initial values are identical across all installations, so we recommend changing them once during commissioning.
Important: Changing passwords out of order will prevent the platform from starting. Normally the automatic rotation(
bin/passwd.sh) below follows that order for you. Use the manual procedure only for values and worker environments that the tool does not handle. Always perform the work in a maintenance window and secure a backup beforehand.
Which keys to change and how to recover from failure are documented up to date in Data Lake — Changing Passwords · API Keys. This page is retained for the manual procedure of values that the tool does not handle.
Automatic Rotation — passwd.sh · rotate-secret.sh
The bin/passwd.sh(front-end) and bin/rotate-secret.sh(rotation engine) in the installation package perform server-side account changes, sidecar update, configuration re-render, and sequential restart in one go. It replaces steps 1–3 of the manual procedure. The container-side rotation engine is pd secret rotate in the data lake image, and the host script invokes it.
Start from the host, not inside a container. If you start inside a container and it dies, the progress record is lost and cannot be resumed.
cd /opt/kopens/plantpulse-platform-docker
# 하나만 변경
bin/rotate-secret.sh PP_PG_PASSWORD='새비밀번호'
# 여러 개 — 재시작 1회로 묶인다
bin/rotate-secret.sh PP_PG_PASSWORD='...' PP_CASSANDRA_PASSWORD='...'
# 커미셔닝 시 전체 생성
bin/rotate-secret.sh --all --generate
# 계획만 출력 — 아무것도 바꾸지 않는다
bin/rotate-secret.sh --dry-run --all --generate
Arguments must be in
변수=값format. Separating with spaces likePP_PG_PASSWORD '새값'will be rejected as알 수 없는 인자.
--all can only be used with --generate. --generate generates a random 20-character value per variable. First check the target with --dry-run — it only prints which variables are affected and does not touch the sidecar or server side.
Rotatable Variables
Only variables registered in the registry can be rotated. Providing an unregistered variable will reject it, not silently skip it.
| Environment Variable | rotator | Target Account |
|---|---|---|
PP_PG_PASSWORD | postgres | plantpulse |
PP_TEMPORAL_PASSWORD | postgres | temporal |
PP_HIVE_PASSWORD | postgres | hive |
PP_CASSANDRA_PASSWORD | cassandra | — |
PP_REDIS_PASSWORD | valkey | — |
PP_MINIO_PASSWORD | minio | — |
PP_CEP_API_KEY | apikey | — |
PP_DATA_GATEWAY_API_KEY | apikey | — |
PP_DATALAKE_ADMIN_PASSWORD | console | admin (admin console login, added 2026-09-05) |
PP_DATALAKE_ADMIN_API_KEY | apikey | — (admin console log endpoint, added 2026-09-05) |
--all --generate also targets only this list. Installation packages before 2026-09-05 do not have the last two — --list is the authoritative source.
Variables not yet registered — PP_API_KEY, PP_FLOW_WEBHOOK_API_KEY (for external callers, separate after validation endpoint is finalized), PP_SPARK_PASSWORD · PP_TSE_PASSWORD · PP_GRAVITINO_PASSWORD · PP_KESTRA_DB_PASSWORD · PP_KESTRA_ADMIN_PASSWORD, and TLS keystore/truststore passwords (separate, tied to certificate replacement). The script will reject these, so change them via manual procedure. The two query console logins (PP_DATA_GATEWAY_WEB_PASSWORD · PP_CEP_WEB_PASSWORD) are not rotation targets by decision, and are changed via sidecar update + restart → Web Console Login Accounts.
PP_MQ_PASSWORD (Kafka · HiveMQ) is covered separately in MQ rotation below.
Operator Front-End — passwd.sh
A front-end instead of calling rotate-secret.sh directly. The behavior is the same — it validates and calls the rotation engine once — but displays a list of what can be changed. All three products (platform · ai · studio) have this with the same name and same usage.
cd /opt/kopens/plantpulse-platform-docker
bin/passwd.sh --list # 키 + 아이디 + 현재값(마스킹) + 위치
bin/passwd.sh --list --show # 현재값 전체
bin/passwd.sh PP_MQ_PASSWORD # 값 생략 → 프롬프트 (권장)
bin/passwd.sh PP_PG_PASSWORD=<new-password>
bin/passwd.sh PP_CASSANDRA_PASSWORD=<new-password> PP_MINIO_PASSWORD=<new-password> # 묶으면 재시작 1회
bin/passwd.sh --dry-run PP_MQ_PASSWORD=<new-password>
You cannot call it by component names like mq, cassandra, postgres. Adding aliases would require maintaining a separate mapping table between names and variables alongside the registry, and each time two aliases point to the same variable ("kafka and hivemq share the same value"), special rules attach. When keys are the variables themselves, that problem vanishes.
Which variable belongs to which component is told by --list — that is why the list exists.
Keys you can change and where their canonical value lives.
| Key | Component | Changed By |
|---|---|---|
PP_PG_PASSWORD | PostgreSQL | Command — ALTER ROLE (psql) |
PP_TEMPORAL_PASSWORD | Temporal's backend PostgreSQL account | Command — ALTER ROLE (psql) |
PP_HIVE_PASSWORD | Hive metastore's PostgreSQL account | Command — ALTER ROLE (psql) |
PP_CASSANDRA_PASSWORD | Cassandra | Command — ALTER ROLE (cqlsh) |
PP_REDIS_PASSWORD | Valkey | File — plantpulse-storage/cache/valkey/conf/valkey.conf |
PP_MINIO_PASSWORD | MinIO | Startup env — MINIO_ROOT_PASSWORD |
PP_MQ_PASSWORD | Kafka + HiveMQ (share one value) | File — kafka/config/jaas.conf + mqtt/conf/auth.properties |
PP_CEP_API_KEY | CEP API Key | File — plantpulse-cep/config/plantpulse-cep.properties |
PP_DATA_GATEWAY_API_KEY | Data Gateway API Key | File — plantpulse-data-gateway/config/plantpulse-jdbc.properties |
The three branches mean:
- Command — the server account is canonical. Changed via SQL/CQL; config files are just copies for connection.
- File — that file is canonical. Changed only by re-render + restart.
- Startup env — injected at process startup. No runtime change API exists, so restart is the only way to apply it.
Do not change the ID (PP_*_USER). We only display it — changing the account name requires creating a new server-side role and transferring privileges, which is a separate task.
Omitting the value and entering via prompt is the recommended path — passwords do not appear in ps output or shell history.
Passing a key not in the registry will reject it, not silently skip it. Typos suggest similar keys.
MQ (Kafka · HiveMQ) Rotation (Design Stage)
PP_MQ_PASSWORD is a single value that is both the server-side credential store for both brokers and the connection password for 8 clients. Whether you call it kafka or hivemq, both change together.
If you provide kafka and hivemq with different values in a single command, it will be rejected — we do not silently decide which wins.
Kafka reads from static JAAS (kafka-jaas.conf), HiveMQ from a security extension (plantpulse-mq-auth.properties). Eight clients (plantpulse-mq · plantpulse-mqtt · plantpulse-batch · 2 plugin types + cluster/ variant) also receive the new value in the same rotation, so there is no gap where only the client is left with the old password.
Static JAAS cannot accept old and new passwords simultaneously. Disconnection occurs in the MQ path during sequential broker and consumer restarts. Zero-downtime rotation is out of scope — always perform this in a maintenance window.
BifroMQ is not a target. The actual MQTT broker is HiveMQ.
On Failure — Re-run the Same Command
We do not roll back. To undo would require reconnecting, but at failure time which credentials are valid is uncertain, so attempting to roll back corrupts the state further. Forward recovery only.
If it fails, re-run the same command exactly. The host /etc/kopens/rotation.journal (0600) has recorded both old and new values beforehand, so re-running determines each component's current state and continues from there. Components already changed to the new credential are skipped.
| Message | Meaning | Action |
|---|---|---|
probe=NEITHER | Cannot connect with either old or new credentials | Automation has no basis for judgment. A person must check the state |
apply 실패 | Server-side change failed, halt immediately | Fix the cause and re-run the same command |
verify 실패 | Server-side changed but cannot connect with new value | Most dangerous. Journal is left at APPLIED — a person must verify |
configure 실패 | Stop without restarting | Intended if it comes up with old config, then all auth fails |
If PP_WORKER_NODES is set, the script rejects and halts. Each worker node has its own sidecar that it injects, so if only the master changes, the worker remains with the old password and the cluster becomes half-operational. Partial success is the worst failure, so we do not silently handle only the master.
Multi-node is not yet supported. Follow the manual procedure for cluster (multi-node) environments.
Why Order Matters
Passwords exist in two places. They do not sync automatically.
- Changing only ① causes all clients to fail auth.
- Changing only ② causes the server to still demand the old password, failing auth.
- Skipping ③ works now, but at the next container recreation, the old password is injected again and startup fails. This is the easiest step to miss.
Therefore the order is always ① Server → ③ Sidecar → ② Config re-render → Restart.
Changeable Accounts
Those with «Target» in the 자동 회전 column are changed by passwd.sh. The manual procedure is for values outside that and for environments with workers.
| Environment Variable | Target | Server Change Required | Auto Rotation (Planned) |
|---|---|---|---|
PP_PG_PASSWORD | PostgreSQL account plantpulse | Required | Target |
PP_TEMPORAL_PASSWORD | PostgreSQL account temporal (Temporal backend) | Required | Target |
PP_HIVE_PASSWORD | PostgreSQL account hive (Hive metastore) + Hive/Kyuubi access auth | Required | Target |
PP_CASSANDRA_PASSWORD | Cassandra role | Required | Target |
PP_REDIS_PASSWORD | Valkey requirepass | Not required (config file) | Target |
PP_MINIO_PASSWORD | MinIO root credential | Not required (injected at startup) | Target |
PP_MQ_PASSWORD | Kafka SASL · MQTT broker | Not required (config file) | Target (MQ rotation) |
PP_TEMPORAL_PASSWORDis not a Temporal account itself. It is the DB account that Temporal uses when connecting to the backend PostgreSQL.
PP_HIVE_PASSWORDis one value used bidirectionally. It is the value the Hive metastore presents when connecting to PostgreSQL, and simultaneously the value clients validate against when connecting to HiveServer2/Kyuubi. So you must do both PostgreSQL account change and config re-render. Doing only one causes either metastore or HiveServer2 to die.
Manual Procedure
Use this procedure in these cases — variables not handled by automatic rotation (TLS keystore/truststore, Spark · TSE · Gravitino · Kestra accounts, etc.), and environments where the script rejects you due to worker nodes. Otherwise bin/passwd.sh replaces this procedure for you.
Step 1 — Change Server-Side Account
Connect with the old password and change it. Skipping this step makes everything else pointless.
PostgreSQL (3 accounts — change only the one you intend):
psql -U plantpulse -c "ALTER USER plantpulse PASSWORD '새비밀번호';"
psql -U temporal -d temporal -c "ALTER USER temporal PASSWORD '새비밀번호';"
psql -U hive -d hive -c "ALTER USER hive PASSWORD '새비밀번호';"
Cassandra:
cqlsh -u cassandra -p 구비밀번호 \
-e "ALTER ROLE cassandra WITH PASSWORD = '새비밀번호';"
Cassandra is inaccessible via both old and new passwords for ~2 seconds after change. This is due to the
cassandra.yamlincredentials_validity(default 2000ms) credential cache. Even if you see connection failure immediately after change, wait 3–5 seconds and check again — do not call it a failure.
Valkey · MinIO · Kafka · MQTT have no server-side accounts — the config file and startup environment variable are canonical, so this step is not needed.
⚠ Immediately after changing, verify connection with the new password
ALTER USER returns success (ALTER ROLE) even with an incorrect value. The success message means "the command ran," not "the intended value was entered."
The most common accident is quoting mistakes. Putting a shell variable inside single quotes prevents expansion and stores it literally.
# ✗ 위험 — 변수가 확장되지 않아 '$NEW_PW' 라는 문자열이 비밀번호가 된다.
# 그런데도 ALTER ROLE 은 성공을 반환한다.
psql -U hive -c 'ALTER USER hive PASSWORD "$NEW_PW";'
# ✓ 안전 — 값을 직접 적거나, 확장되는 문맥인지 확인한다
psql -U hive -c "ALTER USER hive PASSWORD '실제새비밀번호';"
So verify connection immediately after each account change. If you find out later after moving to the next step, the cause is harder to track down.
PGPASSWORD='새비밀번호' psql -U hive -h 127.0.0.1 -d postgres -w -tAc "SELECT 1"
# 1 이 나오면 정상. 인증 실패면 값이 의도와 다르게 들어간 것이다.
If connection fails, first verify the old password is still valid. If it is, the change did not apply. If neither works, an unintended value was entered, and you need to find that value and reconnect to correct it.
If the password contains a single quote (
'), it must be escaped as two inside SQL/CQL statements (a'b→'a''b').
Step 2 — Update Sidecar (Credential Store)
Skipping this step causes the old password to be re-injected at the next restart.
/etc/kopens/plantpulse-platform.env aloneThe platform's secrets are in /etc/kopens/ as 0600, outside the repo tree. There is only one file that is canonical.
ls -l /etc/kopens/
| File | Status |
|---|---|
/etc/kopens/plantpulse-platform.env | Canonical — shared convention across platform · ai · studio (plantpulse-<product>.env) |
/opt/kopens/plantpulse-platform.env | Used only 2026-08-25 ~ 08-29. We neither read nor write it — if it exists, the install script resets it to canonical |
platform.env.generated no longer exists (deleted 2026-08-22). For the same reason platform-credentials.txt was deprecated (2026-08-16) — it held the same secret in duplicate, but the rotation engine did not update that file, so even after changing the password, it reported the old value. Consult the sidecar alone when viewing values.
sudo vi /etc/kopens/plantpulse-platform.env
Edit the corresponding export PP_..._PASSWORD=... line with the new value. Keep file permissions at 0600.
sudo chmod 600 /etc/kopens/plantpulse-platform.env
Step 3 — Restart
From the host, restart the stack. Config re-render is part of the startup process.
cd /opt/kopens/plantpulse-platform-docker/bin
./restart.sh # graceful drain → 재기동 → 준비될 때까지 대기 (0 = 쓸 수 있다)
The data lake container re-renders templates when it starts. Therefore, if you edit component config files directly, they revert to template values on restart. Change settings in the host's
/etc/kopens/conf(bind-mounted toplantpulse-datalake-cli/config/templatesin the container).The six apps do not render templates — they receive values only from environment variables passed by compose.
Step 4 — Verify
# 플랫폼 전체 상태 (호스트에서) — 0 = 정상 / 2 = 비정상
cd /opt/kopens/plantpulse-platform-docker/bin
./status.sh
./ops-check.sh
# 각 컴포넌트가 새 비밀번호로 실제 접속되는지 (데이터레이크 컨테이너 안에서)
./shell.sh
/opt/kopens/plantpulse-platform/plantpulse-datalake-cli/bin/pd node psql -c "SELECT 1;"
/opt/kopens/plantpulse-platform/plantpulse-datalake-cli/bin/pd node cql -e "SELECT now() FROM system.local;"
exit
The health API should return OK or WARN.
curl -kfsS https://<server-ip>:4950/api/health | jq .status
Cluster (Multi-Node) Environment
Do not change only the master if worker nodes exist.
Each worker node has its own /etc/kopens/plantpulse-platform.env that it injects into containers. If you change only the master, the worker remains with the old password and the cluster is half-operational.
Perform steps 2 (sidecar update) and 3 (restart) identically on all worker nodes. Step 1 (server-side change) is a shared resource, so do it only once.
On Failure
| Symptom | Cause | Action |
|---|---|---|
| All components fail auth after change | Step 1 only, step 3 not done | Run bin/restart.sh on the host |
| Old password returns after restart | Step 2 (sidecar) skipped | Edit /etc/kopens/plantpulse-platform.env and restart. Sidecar beats both bin/env.sh and shell export |
| Only Cassandra connection fails | Credential cache (2 sec) | Check again after 3–5 seconds |
| Hive/Kyuubi works but metastore error | PostgreSQL hive account not changed | Do ALTER USER hive in step 1 |
ALTER ROLE succeeded but connection fails | Quoting mistake — unintended value stored | See verification procedure in step 1 — success return does not guarantee value accuracy |
| Only specific paths fail | Some config files not updated | Re-run ./configure.sh inside data lake container, then verify with command below |
Check that deployed config has no unsubstituted placeholders:
# 데이터레이크 컨테이너 안에서
grep -rn '\${PP_' /opt/kopens/plantpulse-platform/ \
--include='*.conf' --include='*.properties' --include='*.xml' --include='*.yaml' \
| grep -v '/plantpulse-datalake-cli/config/templates/'
# 아무것도 나오지 않아야 정상입니다
If you failed midway and changed only one side, first verify which password is valid.
PGPASSWORD='구비밀번호' psql -U plantpulse -h 127.0.0.1 -c "SELECT 1;" # 구 값으로 접속되는가
PGPASSWORD='새비밀번호' psql -U plantpulse -h 127.0.0.1 -c "SELECT 1;" # 신 값으로 접속되는가
- Old value connects → Step 1 did not apply. Repeat from step 1.
- New value connects → Step 1 is done. Perform only steps 2–3.
Control Center (ppctl) Operator Account
No container in the compose stack publishes port 9700 to the host. systemctl restart ppctl also does not apply to stacks running inside a container.
The content below is retained for environments that operate the Control Center separately. For starting, stopping, and controlling the platform in container environments, see the common verbs in System Startup and Shutdown (up.sh · down.sh · restart.sh · status.sh).
There is one more password, completely separate from the procedures above. The Control Center (ppctl) is a separate web screen running on port 9700 with its own login account. This account is neither the platform web console account nor one of the infrastructure service accounts above.
The Control Center is a screen that starts, stops, and controls the entire platform with root privilege.
If you do not set an environment variable, all installations have the same development default account (username admin).
You must change it during commissioning.
How to Change
Set it via two environment variables. Providing a value uses that instead of the default.
| Environment Variable | Purpose |
|---|---|
PP_CONTROL_USER | Control Center login username |
PP_CONTROL_PASSWORD | Control Center login password |
-
Set the above two values in the environment where you launch the Control Center (environment file in
plantpulse-startupor systemd unitppctl.serviceinEnvironment=). -
Restart the Control Center.
systemctl restart ppctl -
Verify that you can log into
https://[HOST]:9700with the new values.
This account has no change function in the UI. Edit the environment variable and restart — that is the only way to change it.
Do Not Enable PP_CONTROL_NOAUTH in Operations
Using PP_CONTROL_NOAUTH=1 (or JVM option -Dppctl.noauth=1) disables Control Center login entirely.
It is a switch for trusted network demos, and the default is off.
Anyone able to reach port 9700 can then control the entire platform without authentication, so never enable it in production and restrict port 9700 access to operator ranges via firewall.
Notes
- If the password contains a single quote (
'), it must be escaped as two inside SQL/CQL statements (a'b→'a''b'). - Keep change history and new passwords in a safe place.
/etc/kopens/platform-credentials.txtrecords the current value, but this file is plaintext, so permissions (0600) must be maintained. - CEP and Data Gateway API keys (
PP_CEP_API_KEY·PP_DATA_GATEWAY_API_KEY) work by both sides reading the same value and comparing it. Changing only one causes immediate 401, so change both together. Both keys are auto-rotation targets. /etc/kopens/rotation.journalleft by auto-rotation has old and new values encoded and recorded. It has the same sensitivity as the sidecar, so maintain permissions0600.
Related Documentation
- Security Configuration
- User Management — web console login accounts (separate from this document)
- Environment Variable Reference
- Troubleshooting