Skip to main content

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.

Documented in the Data Lake section

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 like PP_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 VariablerotatorTarget Account
PP_PG_PASSWORDpostgresplantpulse
PP_TEMPORAL_PASSWORDpostgrestemporal
PP_HIVE_PASSWORDpostgreshive
PP_CASSANDRA_PASSWORDcassandra—
PP_REDIS_PASSWORDvalkey—
PP_S3_PASSWORDrustfs—
PP_MQ_PASSWORDmq— (Kafka · HiveMQ together — see the section below)
PP_CEP_API_KEYapikey—
PP_DATA_GATEWAY_API_KEYapikey—
PP_DATALAKE_ADMIN_PASSWORDconsoleadmin (Management console login, added 2026-09-05)
PP_DATALAKE_ADMIN_API_KEYapikey— (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>
No aliases — keys are the environment variable names themselves

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.

KeyComponentHow it is changed
PP_PG_PASSWORDPostgreSQLCommand — ALTER ROLE (psql)
PP_TEMPORAL_PASSWORDTemporal's backend PostgreSQL accountCommand — ALTER ROLE (psql)
PP_HIVE_PASSWORDHive metastore's PostgreSQL accountCommand — ALTER ROLE (psql)
PP_CASSANDRA_PASSWORDAster TSDBCommand — ALTER ROLE (cqlsh)
PP_REDIS_PASSWORDValkeyFile — plantpulse-storage/cache/valkey/conf/valkey.conf
PP_S3_PASSWORDRustFSStartup env — RUSTFS_SECRET_KEY
PP_MQ_PASSWORDKafka + HiveMQ (shares one value)File — kafka/config/jaas.conf + mqtt/conf/auth.properties
PP_CEP_API_KEYCEP API keyFile — plantpulse-cep/config/plantpulse-cep.properties
PP_DATA_GATEWAY_API_KEYData Gateway API keyFile — 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​

Kafka and HiveMQ cannot be changed separately

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.

MQ paths disconnect during rotation

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.

MessageMeaningAction
probe=NEITHERCannot connect with either the old or new credentialsNo basis for the automation to decide. A human must check the state
apply 실패Server-side change failed, stopped immediatelyFix the cause and re-run the same command
verify 실패Server side changed but cannot connect with the new valueMost dangerous. There's a APPLIED entry in the journal — a human must investigate
configure 실패Stopped without restartingIf it comes up with the old config, all authentication will fail, so this is intentional behavior
Not usable if worker nodes exist

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 VariableTargetServer-side Change RequiredAutomatic Rotation
PP_PG_PASSWORDPostgreSQL account plantpulseYesTarget
PP_TEMPORAL_PASSWORDPostgreSQL account temporal (Temporal backend)YesTarget
PP_HIVE_PASSWORDPostgreSQL account hive (Hive metastore) + Hive/Kyuubi connection authYesTarget
PP_CASSANDRA_PASSWORDAster TSDB roleYesTarget
PP_REDIS_PASSWORDValkey requirepassNo (configuration file)Target
PP_S3_PASSWORDRustFS root credentialsNo (injected at startup)Target
PP_MQ_PASSWORDKafka SASL · MQTT brokerNo (configuration file)Target (MQ Rotation)

PP_TEMPORAL_PASSWORD is not a Temporal account itself. It's the DB account Temporal uses to connect to its backend PostgreSQL.

PP_HIVE_PASSWORD uses 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.

The configuration source of truth is /etc/kopens/plantpulse-platform.env alone

The 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/
FileStatus
/etc/kopens/plantpulse-platform.envSource of truth — common convention (plantpulse-<product>.env) for platform · ai · studio
/opt/kopens/plantpulse-platform.envPath 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's plantpulse-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​

SymptomCauseAction
Auth failure on all components right after changeOnly Step 1 done, Step 3 not performedbin/restart.sh on the host
Reverts to old password after restartStep 2 (sidecar) omittedFix /etc/kopens/plantpulse-platform.env and restart. The sidecar overrides both bin/env.sh and shell exports
Only Aster TSDB connection failsCredentials cache (2 seconds)Re-check after 3–5 seconds
Hive/Kyuubi works but metastore errorsPostgreSQL hive account not changedPerform ALTER USER hive from Step 1
ALTER ROLE succeeded but connection failsQuotation error caused an unintended value to be storedRefer to the verification procedure in Step 1 — success return doesn't guarantee the value is correct
Only specific paths failSome configuration files not updatedRe-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​

Not accessible in current container builds

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.

Do not leave the default value

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 VariablePurpose
PP_CONTROL_USERControl Center login username
PP_CONTROL_PASSWORDControl Center login password
  1. Set these two values in the environment that runs the Control Center (sidecar /etc/kopens/plantpulse-platform.env or systemd unit ppctl.service, in Environment=).

  2. Restart the Control Center.

    systemctl restart ppctl
  3. Verify login at https://[HOST]:9700 with 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.txt records 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.journal left by automatic rotation contains the old and new values encoded. It's as sensitive as the sidecar, so maintain permissions 0600.