Skip to content

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.

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.

SchemeResolves toExample
env:the value of an environment variableenv:FLEXFS_META_TOKEN
file:the contents of a filefile:/run/secrets/token
exec:the standard output of a commandexec:vault kv get -field=token secret/flexfs
literal:the rest of the value, verbatimliteral:env:not-a-reference
# A metadata server credentials file sourcing every secret from elsewhere
adminAddr = "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: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:/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 0644 by default. Tighten it with defaultMode: 0400 where 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: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.

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.

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.