Skip to content

fix(redpanda): Use rpk --password-stdin to keep SASL password off argv#643

Merged
stefanko-ch merged 1 commit into
mainfrom
fix/redpanda-sasl-password-stdin
Jun 4, 2026
Merged

fix(redpanda): Use rpk --password-stdin to keep SASL password off argv#643
stefanko-ch merged 1 commit into
mainfrom
fix/redpanda-sasl-password-stdin

Conversation

@stefanko-ch

@stefanko-ch stefanko-ch commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

Switches the RedPanda SASL-user hook from rpk ... --password "$RPK_PASS" (password expanded into rpk's argv inside the container) to rpk ... --password-stdin (password flows end-to-end on stdin, never landing in argv).

Closes #463.

Why

The previous shape:

printf '%s' "$REDPANDA_PASSWORD" | docker exec -i redpanda \
  sh -c 'RPK_PASS=$(cat); rpk acl user create nexus-redpanda --password "$RPK_PASS" --mechanism SCRAM-SHA-256'

passed the password via stdin to the OUTER docker exec (good — host ps aux shows just the sh -c invocation), but the inner sh -c expanded $RPK_PASS and re-injected the password into rpk's argv inside the container. The file's own header comment already acknowledged this:

The inner CLI rpk acl user create --password "$PASS" still has the password in its argv inside the container (visible to other processes in the same container), but the OUTER host-level ps -ef shows just the benign sh -c invocation.

--password-stdin closes that last surface — password flows host-stdin → docker exec stdin → rpk stdin → rpk internal state, never landing in argv on any of:

Surface Before After
Host ps aux clean (printf + sh -c only) clean
Docker daemon ps aux clean clean
Container ps aux (rpk's argv) password visible for ~1s clean

History

An earlier attempt at this landed and was reverted in 7c3c530 (2026-04-15) because the then-bundled rpk version didn't recognise --password-stdin. The pinned redpanda image is now v24.3 and rpk v23+ supports the flag natively per Redpanda docs. If a future image downgrade re-introduces the limitation, the failure mode is a loud "unknown flag" error from rpk (not silent).

Test plan

  • All 10 redpanda-hook unit tests still pass — none pinned the old RPK_PASS=$(cat) shell shape, so the rendered-bash change is invisible to them. Idempotency contract and rotation path unchanged.
  • On a real spin-up: gh workflow run spin-up.yml succeeds; the services-configure phase reports RESULT hook=redpanda status=configured; SASL clients (Kestra, Flink-stack tutorials) authenticate against the broker as before.
  • On a re-run after Infisical password rotation: hook reports status=configured via the delete+recreate rotation path.

Rollback

Revert the commit. The single edit is constrained to two rpk acl user create invocations + the rationale comment block; no API/contract changes to the orchestrator or the rest of the services module.

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced security in RedPanda service credential handling by adopting a safer password transport mechanism that prevents credentials from appearing in command-line arguments or environment variables.

The previous SASL-user hook piped the password via stdin into a shell
var inside the container, then expanded it as `rpk ... --password
"$RPK_PASS"`. The shell expansion put the password back into argv at
the rpk level — visible to anyone with /proc access inside the redpanda
container. The header comment in this file already acknowledged this
("still has the password in its argv inside the container — different
threat model").

rpk's `--password-stdin` flag closes the last surface: the password
flows host stdin → docker exec stdin → rpk stdin → rpk's internal
state, never landing in any argv on any of the three process-list
surfaces (host, docker daemon, container).

The earlier attempt at this (commit 7c3c530, 2026-04-15) reverted
because the then-bundled rpk was older than v23 and didn't recognise
--password-stdin. The pinned redpanda image is now v24.3 where rpk
v24+ supports the flag natively. If a future image downgrade
re-introduces the limitation, the symptom is a clean "unknown flag"
error from rpk — failure mode is loud, not silent.

Existing 10 redpanda-hook unit tests still pass — none of them pinned
the previous `RPK_PASS=$(cat)` shell shape, so the rendered-bash
shape change is invisible to them. Idempotency contract and rotation
path unchanged.

Closes #463
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0042fa67-b3c6-4fe8-952d-6eae0b0e2cc0

📥 Commits

Reviewing files that changed from the base of the PR and between 7a57ef7 and 488a877.

📒 Files selected for processing (1)
  • src/nexus_deploy/services.py

📝 Walkthrough

Walkthrough

This PR addresses security issue #463 by eliminating shell variable expansion in RedPanda SASL user creation. The password is now piped directly to rpk via stdin using the --password-stdin flag, keeping credentials off process argument lists across the runner, nexus server, and container.

Changes

Safer password transport for RedPanda SASL user creation

Layer / File(s) Summary
Password credential handling via stdin
src/nexus_deploy/services.py
Docker-exec guidance comments now document the printf | docker exec -i <container> <cli> --password-stdin pattern. RedPanda-specific commentary explains that passwords reach the container via stdin and are consumed by rpk --password-stdin (requires minimum rpk version). Both rpk acl user create invocations—initial creation and post-delete rotation—replace sh -c 'RPK_PASS=$(cat); rpk ... --password "$RPK_PASS"' with rpk ... --password-stdin. Surrounding idempotency, rotation control flow, and verification logic remain unchanged.

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the main change: using rpk --password-stdin instead of shell variable expansion to keep SASL passwords off argv.
Linked Issues check ✅ Passed The PR implements option 1 from #463: uses rpk --password-stdin to eliminate password visibility in ps output across all three surfaces (runner, server, container).
Out of Scope Changes check ✅ Passed All changes are scoped to the render_redpanda_hook function and directly address the password transport mechanism specified in #463, with no unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/redpanda-sasl-password-stdin

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

coverage

Coverage report — nexus_deploy
FileStmtsMissCoverMissing
__init__.py50100% 
_remote.py150100% 
cli.py40100% 
compose_restart.py400100% 
compose_runner.py880100% 
config.py1400100% 
firewall.py2060100% 
gitea.py5875590%691–692, 697, 720–721, 733–734, 770–771, 783–784, 802–803, 828–829, 851–852, 863–864, 919–920, 928–929, 934, 940–941, 965–966, 999–1000, 1003, 1034–1035, 1076–1077, 1082–1083, 1123–1124, 1155–1156, 1179–1180, 1185–1186, 1285–1286, 1291–1292, 1766, 1770, 1791, 1819–1820, 1907
hetzner_capacity.py1260100% 
infisical.py2040100% 
kestra.py176398%223, 427, 768
orchestrator.py6187388%456, 616, 628, 798–799, 804–805, 837–839, 848, 853–855, 866, 903–904, 909–910, 930, 965–966, 971–972, 980, 1005–1006, 1014, 1036–1037, 1042–1043, 1095–1096, 1101–1102, 1335, 1338, 1408, 1414–1415, 1420–1421, 1455, 1562–1563, 1568–1569, 1618–1619, 1624–1625, 1684, 1699, 1756, 1761–1762, 1767–1768, 1775, 1781, 1956, 1963, 1975–1976, 1981–1982, 1988, 1994, 2067–2068, 2089–2090
pipeline.py2071393%165–166, 350, 388, 468, 485, 580–581, 626–627, 717–718, 765
r2_tokens.py113298%87, 150
s3_persistence.py199199%315
s3_restore.py1030100% 
secret_sync.py990100% 
seeder.py980100% 
service_env.py3973391%1142, 1144–1146, 1154–1155, 1484–1487, 1492–1498, 1527–1531, 1547–1551, 1573, 1575, 1597–1598, 1605, 1714
services.py319199%2028
setup.py1651392%238, 308–311, 319, 323–328, 344
ssh.py560100% 
stack_sync.py960100% 
tfvars.py440100% 
tofu.py860100% 
workspace_coords.py1010100% 
TOTAL429219495% 

@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@stefanko-ch stefanko-ch merged commit 6e20c48 into main Jun 4, 2026
9 checks passed
@stefanko-ch stefanko-ch deleted the fix/redpanda-sasl-password-stdin branch June 4, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security: Redpanda SASL password visible in process lists during deploy

1 participant