github jdx/fnox v1.46.0
v1.46.0: Push secrets to CI and hosting platforms with fnox push

2 hours ago

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 push and 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 push gets 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 (via gh), gitlab-ci (via glab), vercel, cloudflare-workers (via wrangler), fly, forgejo-actions (Forgejo/Gitea/Codeberg REST API, token from FORGEJO_TOKEN/GITEA_TOKEN), and hcp-terraform (token from TF_TOKEN_<host>, TFE_TOKEN, or terraform login).
    • CLI-based targets receive the value on stdin, not as an argument, and use whatever login that CLI already has.
    • fnox push asks for confirmation unless you pass --force. --non-interactive only 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.
  • Lease credentials in proxy rules (#1078) by @jdx. A [[proxy.rules]] entry can now set lease instead of secret. The proxy then mints a short-lived credential from that lease (for example gitlab, github-app, vault or cloudflare) 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 secret and lease. env defaults to the variable the lease produces. Leases that produce several variables, and command leases, need credential = "NAME" to pick one. aws-sts leases 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 list and fnox lease revoke can clean them up if fnox was killed.
    • If the parent credential is missing at startup, fnox proxy run won't start. If minting fails, that request is refused, and a later request tries again.
  • Sync cache for [files] (#1081) by @jdx. fnox sync now 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 by fnox get --file, fnox files write and fnox 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 --file and fnox reencrypt drop the old cache. Run fnox sync again whenever the file changes at its source.
    fnox sync -p sync-age                 # or: fnox sync ~/.kube/config
  • Port-specific allow_private entries (#1088) by @jdx. Under fnox proxy run --sandbox, you can now give the command access to one local service without opening every port on that address. Entries take the forms ip:port, [ipv6]:port, or a CIDR block followed by a port. A bare IP or CIDR still covers every port. Port 0, 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 run no longer exits as soon as it gets a signal. It passes SIGTERM and SIGHUP on to the command. It also passes on SIGINT, 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 under fnox proxy run --sandbox on macOS could use open to 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.

Don't miss a new fnox release

NewReleases is sending notifications on new releases.