Skip to content

aliAljaffer/s2t

Repository files navigation

s2t - secret2text

s2t — secret to text

CI

A small CLI that decodes Kubernetes Secrets into readable key/value pairs. Reads a raw manifest (YAML or JSON), a custom key: value blob, or fetches a live secret via kubectl, base64-decodes every value, and prints the result in a few different shapes.

kubectl is only required for live-fetching a resource by name. Decoding a file (-f) or piped stdin doesn't need it installed at all.

Install

Requires Go 1.26+.

git clone https://github.com/aljaffer/s2t.git
cd s2t
make install

make install builds the binary and copies it to ~/.local/bin/s2t. Make sure that directory is on your PATH.

Usage

s2t -h
Examples:
  s2t -f secret.yaml                              decode a saved manifest file
  cat secret.json | s2t                           decode piped stdin (format auto-detected)
  s2t db-creds -n prod                            fetch and decode a live secret via kubectl
  s2t -f secret.yaml --only username,password     only print specific keys
  s2t -f secret.yaml -o env                       print as KEY=value pairs
  s2t db-creds -n prod -o yaml                    re-encode a live secret as a patch-ready manifest
  s2t -f app.yaml -k configmap                    decode a ConfigMap manifest instead of a Secret
  s2t cm/app-config -n prod                       fetch a ConfigMap live; kind/name in one argument
  s2t cm app-config -n prod                       fetch a ConfigMap live; kind and name as separate arguments
  s2t diff a.yaml b.yaml                          compare two secrets' decoded contents key by key

The resource name is a plain positional argument, just like kubectl get secret NAME -n NAMESPACE — no --name needed (though --name still works, if you prefer being explicit).

Example outputs:

$ s2t -n ar # Auto-completion using default kubeconfig (-n is --namespace's shorthand, matching kubectl)
argocd     arms-prom
$ s2t argocd-initial-admin-secret -n argocd # given the namespace and secret, print plain
password: CLG31TzP3S31XX5j
$ s2t argocd-initial-admin-secret -n argocd -oenv # print as .env file
password=CLG31TzP3S31XX5j
$ s2t argocd-initial-admin-secret -n argocd -oyaml # print as YAML string data
stringData:
    password: CLG31TzP3S31XX5j
$ s2t argocd-initial-admin-secret -n argocd -ojsonc # print as compact json (single line)
{"stringData":{"password":"CLG31TzP3S31XX5j"}}

Flags

Flag Description Default
[name] or [kind] [name] Positional: name of the resource to fetch live via kubectl — a plain name (uses --kind), kind/name (e.g. secret/my-secret, cm/my-cm), or kind name as two separate arguments -
-f, --file Path to a file containing secret data stdin
-t, --format Input format: yaml, json, kv, or sealed-secret (sealed-secret must be requested explicitly), if empty, use any which auto-detects any
-k, --kind Resource kind: secret or configmap secret
--name Same as the positional [name] argument; useful if you'd rather be explicit or the name collides with a subcommand (e.g. diff) -
-n, --namespace Kubernetes namespace (used when fetching a resource by name) defaults to the kubeconfig's current context if omitted
--kubeconfig Path to the kubeconfig file to use ~/.kube/config
--only Comma-separated list of keys to print -
-o, --output Output format: empty (plain), env, json, jsonc, or yaml (json/jsonc/yaml produce a patch-ready manifest; jsonc is compact) empty
--mask Replace every value with a fixed-length placeholder; cannot be combined with -o json/jsonc/yaml false
-h, --help Print usage false

Real Use Cases

Patch a live secret from an edited local file — -o jsonc produces a compact, single-line stringData payload that's directly usable as a kubectl patch argument (the API server base64-encodes it server-side):

kubectl patch secret db-creds -n prod --type merge -p "$(s2t -f secret.yaml -o jsonc)"

Export a live secret as a .env file for local development:

s2t db-creds -n staging -o env > .env

Diff two secret files' decoded values, key by key, with s2t diff (see Diffing secrets below):

s2t diff staging.yaml prod.yaml

Comparing two live secrets still works via the shell trick (s2t diff is file-only for now):

diff <(s2t db-creds -n staging) <(s2t db-creds -n prod)

Grab a single value for scripting, combining --only with -o env:

PASSWORD=$(s2t -f secret.yaml --only password -o env | cut -d= -f2-)

Sealed Secrets

s2t -f sealed.yaml -t sealed-secret decrypts a Sealed Secrets manifest client-side (spec.encryptedData), given the sealed-secrets controller's private key. Set the S2T_SEALED_SECRETS_KEY_FILE env var to a path to that key's PEM file:

export S2T_SEALED_SECRETS_KEY_FILE=~/.config/s2t/sealed-secrets-key.pem
s2t -f sealed.yaml -t sealed-secret

The key is read from a file path, not the env var's value directly, so the key material never touches argv or the environment itself. This isn't auto-detected by -t any — it must be requested explicitly, since it depends on external key material that no other format needs.

ConfigMaps

-k configmap decodes a ConfigMap instead of a Secret. ConfigMaps store data as plain text (not base64) plus an optional binaryData for actual binary content — the opposite of a Secret's always-base64 data — so s2t handles both fields correctly and merges them into one output, whether they're used separately or together in the same manifest:

s2t -f app-config.yaml -k configmap
s2t app-config -n prod -k configmap                # live fetch
s2t -f app-config.yaml -k configmap -o jsonc       # patch payload wraps as {"data": ...}, not stringData

-k configmap only combines with --format json, yaml, or anykv and sealed-secret have no defined ConfigMap shape and are rejected with a clear error.

The kind can also be given positionally instead of via -k, matching kubectl's TYPE/NAME and TYPE NAME forms:

s2t secret/db-creds -n prod
s2t cm/app-config -n prod           # cm is a short alias for configmap
s2t cm app-config -n prod           # kind and name as two separate arguments

If a positional kind is given and -k is also passed with a different kind, s2t rejects the conflict rather than silently picking one.

Masking values

--mask replaces every value with a fixed-length placeholder (********, same length regardless of the real value's length, so it doesn't leak how long the secret actually is) — for sharing output in a screen-share, chat message, or CI log without exposing the real value:

s2t -f secret.yaml --mask

--mask cannot be combined with -o json, jsonc, or yaml: those produce a kubectl patch-ready payload, and a masked value would silently overwrite the real secret with the literal string "********" if applied. s2t refuses this combination outright rather than risk it.

Diffing secrets

s2t diff <fileA> <fileB> compares two manifests' decoded contents key by key — added, removed, and changed keys, not a raw line diff:

s2t diff staging.yaml prod.yaml
- deprecated-key
+ new-feature-flag
~ password

Values are hidden by default (diff output is more likely to end up pasted into a ticket or chat than a single decode). Pass --show-values to see the actual old/new values for changed keys:

s2t diff --show-values staging.yaml prod.yaml
- deprecated-key: old-value
+ new-feature-flag: enabled
~ password: staging-pass -> prod-pass

s2t diff shares --format/-t and --kind/-k with the root command (both files are assumed to be the same format/kind), and is file-only for now — see the shell-trick example above for comparing two live secrets.

Development

make build   # go vet + go test + build to bin/
make test    # go test ./...
make vet     # go vet ./...
make clean   # remove bin/

See CONTRIBUTING.md for details on contributing.

License

MIT

About

secret2text: Easily view b64 encoded values as plaintext and re-export them as patchable snippets to be used with 'kubectl patch'

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors