Skip to main content

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.

Details are in the Data Lake section

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 like PP_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 VariablerotatorTarget Account
PP_PG_PASSWORDpostgresplantpulse
PP_TEMPORAL_PASSWORDpostgrestemporal
PP_HIVE_PASSWORDpostgreshive
PP_CASSANDRA_PASSWORDcassandra
PP_REDIS_PASSWORDvalkey
PP_MINIO_PASSWORDminio
PP_CEP_API_KEYapikey
PP_DATA_GATEWAY_API_KEYapikey
PP_DATALAKE_ADMIN_PASSWORDconsoleadmin (admin console login, added 2026-09-05)
PP_DATALAKE_ADMIN_API_KEYapikey— (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 registeredPP_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>
No aliases — keys are environment variable names as-is

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.

KeyComponentChanged By
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_PASSWORDCassandraCommand — ALTER ROLE (cqlsh)
PP_REDIS_PASSWORDValkeyFile — plantpulse-storage/cache/valkey/conf/valkey.conf
PP_MINIO_PASSWORDMinIOStartup env — MINIO_ROOT_PASSWORD
PP_MQ_PASSWORDKafka + HiveMQ (share 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 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)

Kafka and HiveMQ cannot be changed separately

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.

MQ paths disconnect during rotation

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.

MessageMeaningAction
probe=NEITHERCannot connect with either old or new credentialsAutomation has no basis for judgment. A person must check the state
apply 실패Server-side change failed, halt immediatelyFix the cause and re-run the same command
verify 실패Server-side changed but cannot connect with new valueMost dangerous. Journal is left at APPLIED — a person must verify
configure 실패Stop without restartingIntended if it comes up with old config, then all auth fails
Cannot be used if worker nodes exist

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 VariableTargetServer Change RequiredAuto Rotation (Planned)
PP_PG_PASSWORDPostgreSQL account plantpulseRequiredTarget
PP_TEMPORAL_PASSWORDPostgreSQL account temporal (Temporal backend)RequiredTarget
PP_HIVE_PASSWORDPostgreSQL account hive (Hive metastore) + Hive/Kyuubi access authRequiredTarget
PP_CASSANDRA_PASSWORDCassandra roleRequiredTarget
PP_REDIS_PASSWORDValkey requirepassNot required (config file)Target
PP_MINIO_PASSWORDMinIO root credentialNot required (injected at startup)Target
PP_MQ_PASSWORDKafka SASL · MQTT brokerNot required (config file)Target (MQ rotation)

PP_TEMPORAL_PASSWORD is not a Temporal account itself. It is the DB account that Temporal uses when connecting to the backend PostgreSQL.

PP_HIVE_PASSWORD is 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.yaml in credentials_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.

The config canonical source is /etc/kopens/plantpulse-platform.env alone

The 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/
FileStatus
/etc/kopens/plantpulse-platform.envCanonical — shared convention across platform · ai · studio (plantpulse-<product>.env)
/opt/kopens/plantpulse-platform.envUsed 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 to plantpulse-datalake-cli/config/templates in 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

SymptomCauseAction
All components fail auth after changeStep 1 only, step 3 not doneRun bin/restart.sh on the host
Old password returns after restartStep 2 (sidecar) skippedEdit /etc/kopens/plantpulse-platform.env and restart. Sidecar beats both bin/env.sh and shell export
Only Cassandra connection failsCredential cache (2 sec)Check again after 3–5 seconds
Hive/Kyuubi works but metastore errorPostgreSQL hive account not changedDo ALTER USER hive in step 1
ALTER ROLE succeeded but connection failsQuoting mistake — unintended value storedSee verification procedure in step 1 — success return does not guarantee value accuracy
Only specific paths failSome config files not updatedRe-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

This screen is not accessible in current container shipments

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.

Do not leave the default value

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 VariablePurpose
PP_CONTROL_USERControl Center login username
PP_CONTROL_PASSWORDControl Center login password
  1. Set the above two values in the environment where you launch the Control Center (environment file in plantpulse-startup or systemd unit ppctl.service in Environment=).

  2. Restart the Control Center.

    systemctl restart ppctl
  3. Verify that you can log into https://[HOST]:9700 with 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.txt records 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.journal left by auto-rotation has old and new values encoded and recorded. It has the same sensitivity as the sidecar, so maintain permissions 0600.