Secret References
Every flexFS component reads its secrets from a TOML credentials file. By default each value in that file is the secret — a token, a password, an object-store key. A secret reference lets a value instead name where the secret lives, so the credentials file can point at an environment variable, a file, or a secrets manager rather than hold the secret itself.
References are resolved when the field is read, at start-up. They are a way to source a secret from somewhere it is already managed; they are not a replacement for the file’s own protections, which still apply.
The rule
Section titled “The rule”A value is a reference only if it begins with one of the scheme words below followed by a colon — write it with no surrounding whitespace (token = "env:FOO", not " env:FOO "), since a value that does not begin with the scheme word is taken literally. Anything else is a literal. A token, a host:port, an https:// URL, a JSON identity, a PEM key — none of them begin with a scheme word, so every credentials file written before references existed reads exactly as it did.
| Scheme | Resolves to | Example |
|---|---|---|
env: | the value of an environment variable | env:FLEXFS_META_TOKEN |
file: | the contents of a file | file:/run/secrets/token |
exec: | the standard output of a command | exec:vault kv get -field=token secret/flexfs |
literal: | the rest of the value, verbatim | literal:env:not-a-reference |
# A metadata server credentials file sourcing every secret from elsewhereadminAddr = "admin.example.com:443"token = "env:FLEXFS_META_TOKEN"blockUser = "file:/run/secrets/block-user"blockPass = "exec:vault kv get -field=password secret/flexfs/block"Surrounding whitespace on a resolved value is trimmed for env:, file: and
exec: — a trailing newline, a CRLF, a stray space is a storage artifact, not
part of the secret. Only literal: keeps its bytes exactly.
env: — environment variable
Section titled “env: — environment variable”env:NAME reads environment variable NAME. A variable that is not set is an error the process reports at start; a variable that is set but empty (or only whitespace) is treated as an unset field.
This is the natural fit for systemd EnvironmentFile=, container orchestrator secrets injected as environment, and CI secret injection. Bear in mind that a process’s environment is visible to anything running as the same user and is inherited by child processes — prefer file: or exec: for the most sensitive values.
file: — file contents
Section titled “file: — file contents”file:/path reads the secret from a file. Surrounding whitespace is trimmed (a trailing newline from echo "$secret" > /path, a CRLF from a file written on Windows), while interior newlines — a PEM key’s own line breaks — are preserved.
The file is held to a lighter standard than the credentials file itself:
- A file writable by group or others is refused — anyone who can write it could substitute the secret.
- A file readable by group or others is warned about but accepted, because Kubernetes projects secret files mode
0644by default. Tighten it withdefaultMode: 0400where you can. - A directory, a named pipe, or any non-regular file is refused rather than read.
This scheme covers the two best on-host options:
- systemd
LoadCredential=places the secret on a tmpfs that never touches disk. Point the reference at the concrete path systemd exposes, e.g.file:/run/credentials/meta.flexfs.service/token. - Kubernetes / container secret mounts appear as files; reference them by their mount path, e.g.
file:/etc/flexfs-secrets/token.
exec: — a helper command
Section titled “exec: — a helper command”exec:command args… runs a program and uses its standard output as the secret. This is the vendor-neutral path to any secrets manager: HashiCorp Vault, AWS Secrets Manager, Google Secret Manager, Azure Key Vault, 1Password, pass, or a script of your own.
smtpPass = "exec:vault kv get -field=password secret/flexfs/smtp"The command is run without a shell. It is tokenized the way a shell splits a line — whitespace separates arguments and quotes group them — but nothing is expanded: there is no variable substitution, no command substitution, no globbing, and no pipe or redirection. exec:foo | bar runs a program literally named foo with the arguments | and bar; if you need a pipeline, wrap it in your own script and reference that script.
The helper is bounded by a 30-second timeout and killed if it overruns, so an unreachable backend fails the start rather than hanging it. It runs with the process’s environment and PATH; give the helper an absolute path for hardening.
literal: — the escape hatch
Section titled “literal: — the escape hatch”If a real secret genuinely begins with a scheme word — an object-store password that happens to start with exec:, say — prefix it with literal: to force it through verbatim:
blockPass = "literal:exec:this-is-my-actual-password"literal: strips exactly one prefix and never re-resolves what it uncovers.
Trust model
Section titled “Trust model”A reference moves a secret out of the 0600 credentials file and gives it the trust of wherever it now points: env: rests on the process environment, file: on that file’s own permissions, exec: on your PATH and the helper. The credentials file’s own rules are unchanged — it must still be 0600, and a value with no scheme prefix is still stored in it as plaintext. Use references to keep the sensitive fields (tokens, passwords, the volume secret) in a managed store while the credentials file holds only pointers and non-secret settings like the admin address.