Password Change (Credential Rotation)
Overview
PlantPulse uses service accounts to access internal infrastructure components (PostgreSQL · Aster TSDB · Valkey · RustFS · Kafka · MQTT · Hive/Kyuubi · Temporal). The initial values are identical across all installations, so changing them once at commissioning is recommended.
Important: If the password change is not done in the correct order, the platform will not start. Typically, the automatic rotation(
bin/passwd.sh) handles this sequence for you. The manual procedure is only used for values the tool doesn't cover or in environments with workers. Always perform this in a maintenance window, and ensure backups are taken before starting.
For details on which keys to change and how, including what to do on failure, refer to the latest version in Data Lake — Changing Passwords · API Keys. This page is kept for the manual procedure for values that that tool does not cover.
Automatic Rotation — passwd.sh · rotate-secret.sh
The bin/passwd.sh (front door) and bin/rotate-secret.sh (rotation engine) in the installation package perform server-side account changes, vault (sidecar) updates, configuration re-rendering, and sequential restarts in one go. This replaces steps 1–3 of the manual procedure exactly. The container-side rotation mechanism is pd secret rotate in the data lake image, and the host script calls it.
The operator entry point is the host. Starting inside the container means the progress log is lost when the container dies, making resumption impossible.
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
The arguments must be in the
변수=값format. If you separate them with a space likePP_PG_PASSWORD '새값', it will be rejected with알 수 없는 인자.
--all can only be used together with --generate. --generate generates a 20-character random value for each variable. First verify the target with --dry-run — it only prints which variables are affected and does not touch the sidecar or the server side.
Rotatable Variables
Only variables registered in the registry can be rotated. If you pass an unregistered variable, it will be rejected, not silently skipped.
| 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_S3_PASSWORD | rustfs | — |
PP_MQ_PASSWORD | mq | — (Kafka · HiveMQ together — see the section below) |
PP_CEP_API_KEY | apikey | — |
PP_DATA_GATEWAY_API_KEY | apikey | — |
PP_DATALAKE_ADMIN_PASSWORD | console | admin (Management console login, added 2026-09-05) |
PP_DATALAKE_ADMIN_API_KEY | apikey | — (Management 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 item after the verification endpoint is confirmed), PP_SPARK_PASSWORD · PP_TSE_PASSWORD · PP_GRAVITINO_PASSWORD · PP_KESTRA_DB_PASSWORD · PP_KESTRA_ADMIN_PASSWORD, and TLS keystore/truststore passwords (entangled with certificate replacement, separate item). These are rejected by the script, so change them using the manual procedure. The two query console logins (PP_DATA_GATEWAY_WEB_PASSWORD · PP_CEP_WEB_PASSWORD) are decided not to be rotation targets; they are changed via sidecar update + restart → Web Console Login Accounts.
PP_MQ_PASSWORD (Kafka · HiveMQ) is a rotation target, but it's the only item that causes disconnections, so it is handled separately in MQ Rotation below.
Operator Front Door — passwd.sh
This is a front door used instead of calling rotate-secret.sh directly. It only validates and then calls the rotation engine once, so the operation is the same; the difference is that it shows you a list of what you can change. The three products — platform · ai · studio — have this with the same name and 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_S3_PASSWORD=<new-password> # 묶으면 재시작 1회
bin/passwd.sh --dry-run PP_MQ_PASSWORD=<new-password>
You cannot use component names like mq, cassandra, postgres. If you had aliases, you'd need to maintain a mapping table between names and variables separately from the registry, and it would require special rules whenever two aliases point to the same variable ("kafka and hivemq are the same value"). If the key is the variable, that problem disappears.
--list tells you which variable corresponds to which component — that's why there's a list.
Here are the changeable keys and where the source of truth is for their values.
| Key | Component | How it is changed |
|---|---|---|
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 | Aster TSDB | Command — ALTER ROLE (cqlsh) |
PP_REDIS_PASSWORD | Valkey | File — plantpulse-storage/cache/valkey/conf/valkey.conf |
PP_S3_PASSWORD | RustFS | Startup env — RUSTFS_SECRET_KEY |
PP_MQ_PASSWORD | Kafka + HiveMQ (shares 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 categories mean the following:
- Command — The server account is the source of truth. Changed via SQL/CQL; the configuration file is just a connection copy.
- File — That file is the source of truth. Changed only via re-render + restart.
- Startup env — Injected at process startup. No runtime change API exists, so restart is the only way to apply it.
We do not change the IDs (PP_*_USER). We only display them — renaming an account is a different task that involves server-side role creation and permission transfer.
Omitting the value and entering it via prompt is the recommended path — the password is not left in ps output or shell history.
Passing a key not in the registry is rejected, not silently skipped. Typos will suggest similar keys.
MQ (Kafka · HiveMQ) Rotation
PP_MQ_PASSWORD one is 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 pass kafka and hivemq in a single command with different values, it's rejected — it won't silently decide which one wins.
Kafka reads values from static JAAS (kafka-jaas.conf), and HiveMQ from its security extension (plantpulse-mq-auth.properties). The 8 clients (plantpulse-mq · plantpulse-mqtt · plantpulse-batch · 2 plugin types + cluster/ variant) also receive the new values in the same rotation, so there's no hole where only a client keeps the old password.
Static JAAS cannot accept the old and new passwords simultaneously. While the brokers and consumers are restarted sequentially, disconnections occur on MQ paths. Zero-downtime rotation is not supported — 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 revert, you'd need to reconnect, but at the point of failure it's uncertain which credentials are valid, so attempts to revert further degrade the state. Only forward recovery is supported.
If it fails, re-run the same command as is. The host /etc/kopens/rotation.journal (0600) has the old and new values pre-recorded, so re-running will assess the current state of each component and continue from there. Components already changed to the new credentials are skipped.
| Message | Meaning | Action |
|---|---|---|
probe=NEITHER | Cannot connect with either the old or new credentials | No basis for the automation to decide. A human must check the state |
apply 실패 | Server-side change failed, stopped immediately | Fix the cause and re-run the same command |
verify 실패 | Server side changed but cannot connect with the new value | Most dangerous. There's a APPLIED entry in the journal — a human must investigate |
configure 실패 | Stopped without restarting | If it comes up with the old config, all authentication will fail, so this is intentional behavior |
If PP_WORKER_NODES is set, the script rejects and stops. Each worker node has its own sidecar, so rotating only the master leaves the workers with the old password, making the cluster half-functional. Partial success is the worst failure, so we don't silently process only the master.
Multi-node is not yet supported. Follow the manual procedure in Cluster (Multi-Node) Environments.
Why the Order Matters
The password exists in two places separately. They are not automatically synchronized.
- Changing only ① causes every client to fail authentication.
- Changing only ② causes the server to still require the old password, so authentication fails.
- Omitting ③ works for now, but the old password gets injected again on the next container recreation, causing startup failure. This is the most commonly missed step.
Therefore, the order is always ① Server → ③ Vault → ② Config regeneration → Restart.
Changeable Accounts
Items in the 자동 회전 column labeled «Target» are changed by passwd.sh. The manual procedure is for other values and for environments with workers.
Below are the seven infrastructure accounts. The four API keys (PP_CEP_API_KEY · PP_DATA_GATEWAY_API_KEY ·
PP_DATALAKE_ADMIN_PASSWORD · PP_DATALAKE_ADMIN_API_KEY) are also targets of the same tool; the full list of eleven is in Rotatable Variables above.
| Environment Variable | Target | Server-side Change Required | Automatic Rotation |
|---|---|---|---|
PP_PG_PASSWORD | PostgreSQL account plantpulse | Yes | Target |
PP_TEMPORAL_PASSWORD | PostgreSQL account temporal (Temporal backend) | Yes | Target |
PP_HIVE_PASSWORD | PostgreSQL account hive (Hive metastore) + Hive/Kyuubi connection auth | Yes | Target |
PP_CASSANDRA_PASSWORD | Aster TSDB role | Yes | Target |
PP_REDIS_PASSWORD | Valkey requirepass | No (configuration file) | Target |
PP_S3_PASSWORD | RustFS root credentials | No (injected at startup) | Target |
PP_MQ_PASSWORD | Kafka SASL · MQTT broker | No (configuration file) | Target (MQ Rotation) |
PP_TEMPORAL_PASSWORDis not a Temporal account itself. It's the DB account Temporal uses to connect to its backend PostgreSQL.
PP_HIVE_PASSWORDuses one value in both directions. It's the value the Hive metastore presents when connecting to PostgreSQL, and simultaneously the value validated when a client connects to HiveServer2/Kyuubi. So both the PostgreSQL account change and configuration regeneration are required. Doing only one will kill either the metastore or HiveServer2.
Manual Procedure
Use this procedure in the following cases — for variables not covered by automatic rotation (TLS keystore/truststore, Spark · TSE · Gravitino · Kestra accounts, etc.), and for environments with worker nodes where the script rejects execution. Otherwise, bin/passwd.sh handles this.
Step 1 — Server-side Account Change
Connect with the old password to make the change. Skipping this step makes everything else meaningless.
PostgreSQL (3 accounts — only the ones you want to change):
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 '새비밀번호';"
Aster TSDB:
cqlsh -u cassandra -p 구비밀번호 \
-e "ALTER ROLE cassandra WITH PASSWORD = '새비밀번호';"
Aster TSDB is unreachable with either the old or new password for about 2 seconds immediately after the change. This is due to the credentials cache in
cassandra.yaml(credentials_validity, default 2000ms). If you see a connection failure right after the change, do not treat it as a failure; re-check after 3–5 seconds.
Valkey · RustFS · Kafka · MQTT have no server-side stored accounts — the configuration files and startup environment variables are the source of truth, so this step is not needed.
⚠ Verify Access with the New Password Immediately After Changing
ALTER USER returns success (ALTER ROLE) even for an incorrect value. A success message means "the command was executed," not "the intended value was set."
The most common accident is quotation mark handling errors. If you put a shell variable inside single quotes, it won't be expanded and will be stored 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 access for each account right after changing it, in that same place. If you discover an issue after moving to the next step, the cause is harder to find.
PGPASSWORD='새비밀번호' psql -U hive -h 127.0.0.1 -d postgres -w -tAc "SELECT 1"
# 1 이 나오면 정상. 인증 실패면 값이 의도와 다르게 들어간 것이다.
If connection fails, first check whether the old password is still valid. If it is, the change wasn't applied. If both old and new fail, an unintended value was set, so you must find that value and re-connect to correct it.
If the password contains a single quote (
'), it must be escaped by doubling it within SQL/CQL statements (a'b→'a''b').
Step 2 — Vault (Sidecar) Update
If you skip this step, the old password will be re-injected on the next restart.
/etc/kopens/plantpulse-platform.env aloneThe platform's secrets are located outside the repo tree at /etc/kopens/, as 0600. There is only one source-of-truth file.
ls -l /etc/kopens/
| File | Status |
|---|---|
/etc/kopens/plantpulse-platform.env | Source of truth — common convention (plantpulse-<product>.env) for platform · ai · studio |
/opt/kopens/plantpulse-platform.env | Path used only from 2026-08-25 to 08-29. Not read or written — if present, the installation script reverts it to the source of truth |
platform.env.generated no longer exists (deleted 2026-08-22). For the same reason, platform-credentials.txt was also retired (2026-08-16) — it held the same secret in two copies, and the rotation engine didn't update that file, so it advertised the old value even after the password was changed. When viewing values, look at only the sidecar.
sudo vi /etc/kopens/plantpulse-platform.env
Modify the relevant export PP_..._PASSWORD=... line to the new value. Maintain file permissions 0600.
sudo chmod 600 /etc/kopens/plantpulse-platform.env
Step 3 — Restart
From the host, restart the stack. Config regeneration is included in the startup process.
cd /opt/kopens/plantpulse-platform-docker/bin
./restart.sh # graceful drain → 재기동 → 준비될 때까지 대기 (0 = 쓸 수 있다)
The data lake container re-renders templates at startup. So if you directly modify component configuration files, they will be reverted to the template values on restart. Change configuration on the host's
/etc/kopens/conf(bound mount to the container'splantpulse-datalake-cli/config/templates).The six apps do not render templates — they receive values only through environment variables passed by compose.
Step 4 — Verification
# 플랫폼 전체 상태 (호스트에서) — 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 is normal if it returns OK or WARN.
curl -kfsS https://<server-ip>:4950/api/health | jq .status
Cluster (Multi-Node) Environments
In environments with worker nodes, you must not change only the master.
Worker nodes also have their own /etc/kopens/plantpulse-platform.env and inject it into containers. If you change only the master, the workers remain with the old password and the cluster runs half-functional.
Perform Step 2 (sidecar update) and Step 3 (restart) identically on all worker nodes. Step 1 (server-side change) is on the shared resource, so it only needs to be done once.
On Failure
| Symptom | Cause | Action |
|---|---|---|
| Auth failure on all components right after change | Only Step 1 done, Step 3 not performed | bin/restart.sh on the host |
| Reverts to old password after restart | Step 2 (sidecar) omitted | Fix /etc/kopens/plantpulse-platform.env and restart. The sidecar overrides both bin/env.sh and shell exports |
| Only Aster TSDB connection fails | Credentials cache (2 seconds) | Re-check after 3–5 seconds |
| Hive/Kyuubi works but metastore errors | PostgreSQL hive account not changed | Perform ALTER USER hive from Step 1 |
ALTER ROLE succeeded but connection fails | Quotation error caused an unintended value to be stored | Refer to the verification procedure in Step 1 — success return doesn't guarantee the value is correct |
| Only specific paths fail | Some configuration files not updated | Re-run pd config render inside the data lake container, then check for leftovers with the command below |
Check for un-replaced placeholders in the deployed configuration:
# 데이터레이크 컨테이너 안에서
grep -rn '\${PP_' /opt/kopens/plantpulse-platform/ \
--include='*.conf' --include='*.properties' --include='*.xml' --include='*.yaml' \
| grep -v '/plantpulse-datalake-cli/config/templates/'
# 아무것도 나오지 않아야 정상입니다
If you fail midway and only one side is changed, first determine 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;" # 신 값으로 접속되는가
- Can connect with old value → Step 1 wasn't applied. Perform Step 1 again.
- Can connect with new value → Step 1 is done. Only perform Steps 2 and 3.
Control Center (ppctl) Operator Account
No container in the compose stack publishes port 9700 to the host. systemctl restart ppctl also doesn't apply to the stack running inside containers.
The content below is kept for environments that run the Control Center separately. For starting, stopping, and controlling the platform in container environments, see System Startup and Shutdown — the common verbs (up.sh · down.sh · restart.sh · status.sh).
There is one more password that is completely separate from the procedures above. The Control Center (ppctl) is a separate web interface running on port 9700 and uses its own login account. This account is neither the platform web console account nor an infrastructure service account from above.
The Control Center is the interface that starts, stops, and controls the entire platform with root privileges.
If you don't set the environment variables, it will log in with the same developer default account (username admin) across all installations. Be sure to change it at commissioning.
How to Change
It's determined by two environment variables. If values are provided, they are used instead of the defaults.
| Environment Variable | Purpose |
|---|---|
PP_CONTROL_USER | Control Center login username |
PP_CONTROL_PASSWORD | Control Center login password |
-
Set these two values in the environment that runs the Control Center (sidecar
/etc/kopens/plantpulse-platform.envor systemd unitppctl.service, inEnvironment=). -
Restart the Control Center.
systemctl restart ppctl -
Verify login at
https://[HOST]:9700with the new values.
This account has no in-UI change option. Modifying the environment variables and restarting is the only way to change it.
Don't Enable PP_CONTROL_NOAUTH in Production
If you set PP_CONTROL_NOAUTH=1 (or the JVM option -Dppctl.noauth=1), the Control Center's login is completely disabled. It's a switch for trusted-network demos, and the default is off.
Anyone who can reach port 9700 can control the entire platform without authentication. In production, do not enable it, and restrict 9700 access in the firewall to the operator network.
Precautions
- If the password contains a single quote (
'), it must be escaped by doubling it within SQL/CQL statements (a'b→'a''b'). - Store the change history and new passwords in a safe place.
/etc/kopens/platform-credentials.txtrecords the current values, but this file is plaintext, so you must maintain its permissions (0600). - The API keys for CEP · Data Gateway (
PP_CEP_API_KEY·PP_DATA_GATEWAY_API_KEY) work by having the server and the consumer each read the same value and compare them. Changing only one side will cause immediate 401 errors, so change both together. Both keys are automatic rotation targets. - The
/etc/kopens/rotation.journalleft by automatic rotation contains the old and new values encoded. It's as sensitive as the sidecar, so maintain permissions0600.
Related Documents
- Security Settings
- User Management — Web console login accounts (separate from this document)
- Environment Variable Reference
- Troubleshooting