Skip to content

feat(cli): add --raw and --base64 flags for clean SSH key import/export#33

Merged
f3rdy merged 1 commit intomasterfrom
feature/raw-base64-export
Mar 14, 2026
Merged

feat(cli): add --raw and --base64 flags for clean SSH key import/export#33
f3rdy merged 1 commit intomasterfrom
feature/raw-base64-export

Conversation

@f3rdy
Copy link
Copy Markdown
Contributor

@f3rdy f3rdy commented Mar 14, 2026

Summary

SSH private keys and certificates stored in Ansible Vault suffer from whitespace corruption during YAML multiline formatting. This PR adds clean import/export modes to prevent these issues.

  • get --raw: Outputs value without Type: headers or field labels, strips trailing whitespace per line, ensures single trailing newline. Ideal for vaultctl get key --field privateKey --raw > key.pem
  • get --base64: Outputs value as a single base64-encoded line, suitable for environments that cannot handle multiline values
  • set --base64: Accepts an inline base64-encoded value, decodes before storing
  • set --base64-file: Reads base64 from a file or stdin (-), decodes before storing
  • set --file: Now applies whitespace cleanup (trailing space removal) on import
  • clean_multiline_value(): New helper that strips trailing whitespace per line and ensures exactly one trailing newline

Problem

When SSH keys are stored in YAML via ansible-vault, the multiline formatting introduces trailing spaces on lines. Extracting these keys with vaultctl get ... --json | jq -r produces keys that SSH rejects. There was no way to get a clean, whitespace-safe export or to import base64-encoded values.

Changed Files

File Change
src/vaultctl/cli.py Added --raw and --base64 flags to get, --base64 and --base64-file options to set, mutual exclusivity validation, _output_raw() and _output_base64_encoded() helpers
src/vaultctl/yaml_util.py Added clean_multiline_value() helper
tests/test_cli.py 17 new integration tests covering all new flags and edge cases
tests/test_yaml_util.py 7 unit tests for clean_multiline_value()
tests/conftest.py Added ssh_key fixture entry with trailing whitespace for testing

Design Decisions

  1. clean_multiline_value in yaml_util.py — It is a value formatting utility closely related to YAML handling, keeping it here avoids a new module for one function
  2. Mutual exclusivity of --json, --raw, --base64 — Validated at runtime with a clear error message rather than Click's cls=MutuallyExclusiveOption to keep it simple
  3. --file now cleans whitespace on import — Prevents storing corrupted values at the source. This is a minor behavioral change but strictly an improvement
  4. --base64-file - for stdin — Follows Unix convention, enables piping: cat key.pem | base64 | vaultctl set key --base64-file -

Test Plan

  • get --raw on plain strings outputs clean value
  • get --raw on multiline values strips trailing whitespace
  • get --raw --field extracts single field without headers
  • get --raw on structured entries outputs YAML without Type: header
  • get --base64 produces valid single-line base64
  • get --base64 on multiline values cleans before encoding
  • get --base64 --field works on individual fields
  • --json, --raw, --base64 are mutually exclusive
  • set --base64 decodes and stores correctly
  • set --base64 rejects invalid input
  • set --base64-file reads from file
  • set --base64-file - reads from stdin
  • set --file cleans trailing whitespace
  • Multiple input sources rejected
  • clean_multiline_value unit tests (7 cases)
  • All 319 tests pass, coverage 88%

Add output modes to `get` command for clean secret extraction:
- --raw: strips trailing whitespace per line, no headers/formatting
- --base64: outputs value as single base64-encoded line

Add input modes to `set` command for encoded import:
- --base64: accepts inline base64-encoded value
- --base64-file: reads base64 from file or stdin (-)

Add clean_multiline_value() helper that strips trailing whitespace
per line and ensures exactly one trailing newline. Applied on --raw
export and --file import to prevent YAML multiline formatting artifacts.
@f3rdy f3rdy force-pushed the feature/raw-base64-export branch from 0adb17a to 8c601a8 Compare March 14, 2026 14:40
@f3rdy f3rdy merged commit 10a7ed8 into master Mar 14, 2026
2 checks passed
@f3rdy f3rdy deleted the feature/raw-base64-export branch March 14, 2026 14:49
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.

1 participant