fnox 1.46.0 adds fnox push, which copies secrets into CI and hosting secret stores such as GitHub Actions, Vercel and Fly. Proxy rules can now inject short-lived lease credentials, [files] entries can be cached locally with fnox sync, and allow_private entries can be limited to one port. This release also fixes a macOS sandbox escape through open.
Added
fnox pushand push targets (experimental) (#1079) by @jdx. CI secrets and hosting environment variables accept a value but never return it, so fnox can't read them as providers. Instead, a secret can now list the targets it should be sent to.fnox pushgets each value through the secret's own provider and writes it to every listed target, using the fnox key as the name.[targets.gh-prod] type = "github-actions" repo = "acme/api" environment = "production" [targets.vercel] type = "vercel" environments = ["production", "preview"] sensitive = true [secrets] STRIPE_KEY = { provider = "op", value = "Stripe/secret key", push = ["gh-prod", "vercel"] }
fnox push --dry-run # preview the writes fnox push STRIPE_KEY -t gh-prod fnox -P production push # use the production profile's targets
- Supported target types:
github-actions(viagh),gitlab-ci(viaglab),vercel,cloudflare-workers(viawrangler),fly,forgejo-actions(Forgejo/Gitea/Codeberg REST API, token fromFORGEJO_TOKEN/GITEA_TOKEN), andhcp-terraform(token fromTF_TOKEN_<host>,TFE_TOKEN, orterraform login). - CLI-based targets receive the value on stdin, not as an argument, and use whatever login that CLI already has.
fnox pushasks for confirmation unless you pass--force.--non-interactiveonly works together with--force. fnox checks every target name and target config before it resolves or writes anything. If one write fails, the remaining writes still run, and the command exits non-zero with a list of the failures.- A profile can define its own targets or override a top-level target that has the same name.
- Pushing keeps a value out of terminals, clipboards and chat transcripts. It doesn't hide the value from anyone who can change code that runs with the secret.
- Supported target types:
- Lease credentials in proxy rules (#1078) by @jdx. A
[[proxy.rules]]entry can now setleaseinstead ofsecret. The proxy then mints a short-lived credential from that lease (for examplegitlab,github-app,vaultorcloudflare) and injects it only into matching requests. The command sees a placeholder and never sees the minted credential or the parent credential it was minted from.[leases.forge] type = "gitlab" # url, project, scopes, duration ... [[proxy.rules]] lease = "forge" domain = "forge.example.com" header = "private-token" methods = ["POST"]
- Each rule sets exactly one of
secretandlease.envdefaults to the variable the lease produces. Leases that produce several variables, andcommandleases, needcredential = "NAME"to pick one.aws-stsleases are rejected. - The proxy mints a lease the first time a matching request needs it, and concurrent requests share that lease. A new lease is minted shortly before the old one expires. Old values stay redacted from responses.
- When the command exits, fnox revokes every lease it minted, if the backend supports revocation. Minted leases are recorded in the lease ledger, so
fnox lease listandfnox lease revokecan clean them up if fnox was killed. - If the parent credential is missing at startup,
fnox proxy runwon't start. If minting fails, that request is refused, and a later request tries again.
- Each rule sets exactly one of
- Sync cache for
[files](#1081) by @jdx.fnox syncnow caches[files]entries as well as secrets. A file stored in a slow remote provider such as 1Password can then be read from a local encrypted copy byfnox get --file,fnox files writeandfnox edit --file. The sync target must be an encryption provider defined in the global config, and the cache is always written to the global config.fnox set --file,fnox edit --fileandfnox reencryptdrop the old cache. Runfnox syncagain whenever the file changes at its source.fnox sync -p sync-age # or: fnox sync ~/.kube/config - Port-specific
allow_privateentries (#1088) by @jdx. Underfnox proxy run --sandbox, you can now give the command access to one local service without opening every port on that address. Entries take the formsip:port,[ipv6]:port, or a CIDR block followed by a port. A bare IP or CIDR still covers every port. Port0, hostnames and malformed entries are rejected when the config loads.[proxy] allow_private = ["127.0.0.1:4790", "[::1]:4790"]
Changed
- Signal handling in
fnox proxy run(#1078) by @jdx. Outside the sandbox,fnox proxy runno longer exits as soon as it gets a signal. It passesSIGTERMandSIGHUPon to the command. It also passes onSIGINT, unless fnox is in the terminal's foreground process group, because Ctrl-C has already reached the command in that case. fnox then waits for the command to exit, so it can shut down the proxy and revoke leases.
Fixed
- macOS sandbox escape through
open(#1090) by @jdx. A command run underfnox proxy run --sandboxon macOS could useopento launch an app through Launch Services. That app ran outside the sandbox, where it could read hidden paths and write outside the working directory (GHSA-38m4-h5j8-8fq6). The sandbox now blocks these launches. Linux is not affected.
Full Changelog: v1.45.0...v1.46.0
💚 Sponsor fnox
fnox is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If fnox handles secrets or config for you or your team, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep fnox secure, free, and independent.