[HN Gopher] SecretSpec: Declarative Secrets Management
___________________________________________________________________
SecretSpec: Declarative Secrets Management
Author : domenkozar
Score : 107 points
Date : 2025-07-21 15:54 UTC (7 hours ago)
(HTM) web link (devenv.sh)
(TXT) w3m dump (devenv.sh)
| imglorp wrote:
| It's nice to present a LastPass method, but really, my suggestion
| is stay far away from LastPass either as a user or an integrator.
| They've been breached at least seven times since 2011. The net
| will be better off with fewer integrations to it.
| domenkozar wrote:
| I hope that this will be one of the tools that allow that
| transition to happen those who'd like to migrate from LastPass
| :)
| lucideer wrote:
| lastpass provide a cli that as far as I've seen serves all
| migration needs so I haven't seen any need to ever touch the
| service with a ten foot pole otherwise.
| kevmo314 wrote:
| Isn't this "echo with more steps"? The CI/CD example [1] strikes
| me as not obviously better than doing cat
| > .env << EOF DATABASE_URL=${{
| secrets.TEST_DATABASE_URL }} STRIPE_API_KEY=${{
| secrets.STRIPE_TEST_KEY }} EOF
|
| which also addresses the trust and rotation problems. I suppose
| for dev secrets those are annoying, but even with secretspec you
| would have to rotate dev secrets when someone is offboarded.
|
| [1] https://devenv.sh/blog/2025/07/21/announcing-secretspec-
| decl...
| domenkozar wrote:
| The example is more of a way to show how to keep backwards
| compatibility and migration to secretspec.
|
| We hope that one day github actions would integrate secretspec
| more tightly, leaving aside using environment variables as a
| transport.
|
| That's going to be a long journey, one worth striving for.
| JadoJodo wrote:
| > Don't you feel some anxiety given we've normalized committing
| encrypted secrets to git repos?
|
| Maybe I haven't worked at enough places, but... when has this
| ever been allowed/encouraged/normalized?
| apopapo wrote:
| What's wrong with committing _encrypted_ secrets? That 's how I
| use `sops`.
| nodesocket wrote:
| I would venture to guess the main concern is accidental
| commit of decrypted secrets.
| thomasingalls wrote:
| If a key gets compromised, the encrypted secrets are
| compromised forever, since you can't be sure all the git
| clones everywhere can be updated with a new encryption key.
| Not to mention how fiddly it is to edit git history.
| NewJazz wrote:
| But you can and should be rotating those secrets on some
| schedule regardless, and if you find out a key has been
| compromised you can immediately rotate the secrets.
| dvtkrlbs wrote:
| I would assume if you are committing encrypted secrets
| you would make sure they are rotatable
| NewJazz wrote:
| That can happen regardless.
| JeffMcCune wrote:
| You can't revoke, rotate, or audit access to them.
| NewJazz wrote:
| You can rotate them, although admittedly it can be more or
| less complicated depending on how your tooling compares to
| that of the secrets management system you are comparing
| against.
|
| You can't necessarily revoke a secret just because it is in
| Hashicorp Vault or AWS Secrets Manager. Revocation is a
| function of the system that provisions and/or uses the
| secret for authentication, not the system that stores the
| secret. E.g. if you generate a certificate and store the
| private key with vault or sops, the revocation procedure is
| identical and has nothing to do with the secrets storage
| system.
|
| Auditing access can be done coarsely by auditing access to
| the encryption key. Admittedly, this is an area where a
| more sophisticated system offers benefits. Although it
| isn't exactly iron clad -- a service may access a secret
| then leak or transfer the secret without that being visible
| in the audit log.
| jasonthorsness wrote:
| Indeed, the only time I saw this was a decade ago for a
| temporary POC... not doing this is a good defense-in-depth
| practice even if the encryption is solid.
| JohnMakin wrote:
| You'd be surprised. In the past I was on a big project at
| company with multi-billion $ revenue. They got caught with
| their pants down on an audit once because people would not only
| commit credentials into internal repositories, they were
| usually not encrypted at all, among other deeper issues. It
| sparked a multi-year long project of incorporating a secrets
| management service into the 1000+ repositories and services the
| company used. Found a loooooot of dead bodies, tons of people
| got fired during the process. After that experience I imagine
| this practice is fairly common - people, even smart developers,
| don't always seem to be able to comprehend the blast radius of
| some of these things.
|
| One of my favorite incidents during this clean-up effort was,
| the security team + my team had discovered a lot of DB
| credentials were just sitting on developer's local machines and
| basically nowhere else that made any kind of sense, and they'd
| hand them around as needed via email or message. So, we made
| tickets everywhere we found instances of this to migrate to the
| secret management platform. One lead developer with a
| privileged DB credential wrote a ticket that was basically:
|
| "Migrate secret to secret management platform" and in the info
| section, wrote the plaintext value of the key, inadvertently
| giving anyone with Jira read access to a sensitive production
| database. Even when it was explained to him I could tell he
| didn't really understand fully why that was silly. Why did he
| have it in the first place is a natural followup question, but
| these situations don't happen in a vacuum, there's usually a
| lot of other dumb stuff happening to even allow such a
| situation to unfold.
| NewJazz wrote:
| Okay, but that sounds like a very different situation than a
| small shop where encrypted secrets are committed to one file
| per-repo, and keys and secrets are rotated regularly.
| JohnMakin wrote:
| Okay, in case it was missed, my salient point was that this
| behavior is very common and provided a ridiculous example
| as my evidence. I'm making no commentary on the practice
| itself (although I do think committing configs like secrets
| is really silly and anti-productive)
| anbotero wrote:
| Wait, why are there so many skeptics in this thread?
|
| I have setup AWS + SOPS in several projects now, and the
| developers do not have access to the secrets themselves nor the
| encryption key (which is stored in AWS). Only once did we ever
| require to rollback a secret and that happened at AWS level,
| not the code's. Also it happened within the key rotation
| period, so it was easy.
|
| For us it's easier to track changes (not the value, but when it
| changes), easier to associate it with incidents.
| dayjah wrote:
| It's not clear to me how the secrets are referenced in storage.
| Is the expectation that given `--provider onepassword` that one
| of the entries in 1p would be "BUCKET"?
|
| edit: it's not covered in the post, but it is on the launch and
| doc site: https://secretspec.dev/providers/onepassword/
| sofixa wrote:
| I'm not sure I like the concept.
|
| Realistically, why would your different environments have
| different ways of consuming secrets from different locations?
| Yes, you wouldn't use AWS Secrets Manager in your local testing,
| maybe... but giving each developer control and management of
| their own secrets, in their own locations, is just begging for
| trouble. How do you handle sharing of common secrets? How do you
| handle scenarios where some parts are shared (e.g. a shared api
| key for a dev third party API) but others aren't (local instance
| of test db)? How do you make sure that api key that everyone uses
| in dev is actually rotated from times to times, and nobody has
| stored it in clear text .env because once they had issues with
| OnePassword's service being down, and left it at that? How do you
| make sure that nobody is using an insecure secrets manager (e.g.
| LastPass)?
|
| It's just adding the risk of having the _impression_ that there
| is proper secrets management, but actually having a mess of
| everyone doing whatever they feel like with secrets, with no
| control over who has access to what, and what secret is used
| where and by whom and why. Which is kind of like a good ~70% of
| the point of secrets management.
|
| Centralised secrets management or bust, IMO. Ideally with a
| secrets scanner checking your code doesn't have a secret in clear
| text left by mistake/lazyness. Vault/OpenBao isn't _that_
| complicated to set up, but if really is, your platform probably
| has something already.
|
| Disclaimer: I work at HashiCorp, but opinions my own, I've been a
| part of the team implementing Vault at my past job for
| centralised secrets management and 100% believe it's the way
| things should be done to minimise the risk of mishandling
| secrets.
| domenkozar wrote:
| I'm not advocating that different locations of secrets IS
| something we want, but rather it IS the sad state of reality.
|
| By having a secrets specification we can start working towards
| a future that will consolidate these providers and allow teams
| to centralize it if needed, by having simple means of migrating
| from a mess into a central system.
| tgbugs wrote:
| For at least the "keep secrets out of version control" I
| implemented a python library (and racket library) that has served
| me well over the years for general configuration [0].
|
| One key issue is that splitting general config from secrets is
| practically extremely difficult because once the variables are
| accessible to a running code base most languages and code bases
| don't actually have a way differentiate between them internally.
|
| I skipped the hard part of trying to integrate transparently with
| actual encrypted secret stores. The architecture leaves open the
| ability to write a new backend, but I have found that for most
| things, even in production, the more important security
| boundaries (for my use cases) mean that putting plaintext secrets
| in a file on disk adds minuscule risk compared to the additional
| complexity of adding encryption and screwing something up in the
| implementation. The reason is that most of those secrets can be
| rotated quickly because there will be bigger things to worry
| about if they leak from a prod or even a dev system.
|
| The challenge with a standard for something like this is that the
| devil is always in the details, and I sort of trust the code I
| wrote because I wrote it. Even then I assume I screwed something
| up, which is part of why I don't shared it around (the others are
| because there are still some missing features and architecture
| cleanup, and I don't want people depending on something I don't
| fully trust).
|
| There is a reason I put a bunch of warnings at the top of the
| readme. Other people shouldn't trust it without extensive review.
|
| Glad to see work in the space trying to solve the problem,
| because a good solution will need lots of community buy-in to
| build quality and trust.
|
| 0. https://github.com/tgbugs/orthauth
| dvtkrlbs wrote:
| I really like this. I am using infisical but it does not handle
| the app side without vendor locking to their service. I love the
| additional secretspec_derive bit for the Rust example.
| aranelsurion wrote:
| Another alternative: https://github.com/tellerops/teller
|
| It's a standalone tool with YAML configuration, simple to use.
|
| Basically the way it works:
|
| - You create the secret in GCP/AWS/etc Secrets Manager service,
| and put the secret data there.
|
| - Refer to the secret by its name in Teller.
|
| - Whenever you run `$ teller run ...` it fetches the data from
| the remote service, and makes it available to your process.
| athorax wrote:
| Unfortunately, teller is largely an abandoned project at this
| point.
| 0xbadcafebee wrote:
| It's embarrassing to see a place like HN, which is supposed to be
| cutting-edge, continuing to use designs from 2005.
|
| *Configuration Values*
|
| Your laptop is not hosting your website (I presume), so _.env_ is
| not going to be enough to run your app somewhere other than your
| laptop.
|
| I get it. You only want to run your app locally, and _.env_ is
| convenient. But your production server probably isn 't going to
| load your _.env_ file directly, and it will probably need extra
| or different variables. This disconnect between "the _main_
| development environment variables " and "the _extra stuff_ in
| production " will lead to inconsistencies that you have not
| tested/developed against. That will lead to production bugs. So
| keeping track of those differences in a uniform way is pretty
| useful.
|
| How do you specify configuration for development and production
| without running into inconsistency bugs? By splitting up your
| app's configuration into "static" and "dynamic", and version-
| controlling everything.
|
| 1) "Static" configuration is things like environment variables,
| which do not change from run to run, and are not environment-
| specific. So for example, an API URL prefix like "/api/routes" is
| pretty static and probably not going to change. But an IP address
| definitely will change at some point, so this configuration isn't
| static. (To think about it another way: on your computer, some
| environment variables are simply stored in a text file and read
| into your shell; these are static)
|
| 2) "Dynamic" configuration are values that may change, like
| hostnames, IP addresses, port numbers, usernames, passwords, etc.
| Secrets are also "dynamic", because they should never be hard-
| coded into a file or code, and you will want to rotate secrets in
| the future. All dynamic configuration should be loaded during a
| deployment process (for example, creating an ECS task definition,
| or Kubernetes yaml file), or at runtime (an ECS task definition
| that sources environments from secrets, or a Kubernetes yaml that
| sources environments from secrets, or a function in your code
| that calls an API to look up a secret from Hashicorp Vault or
| similar). In particular for secrets, you want to load those every
| time your program starts, as close to the application's execution
| environment as possible. (To think about it another way: some
| environment variables on your computer require executing a
| program and getting its output to set the variable - like your
| $HOSTNAME, $USER, $SHELL, and other variables)
|
| 3) Both static and dynamic configuration should be version-
| controlled, and any change to these should trigger a new
| deployment. If a value changes, and you don't then immediately
| make a new deployment, that change could be harboring a lurking
| bug that you won't find out about until someone makes a
| deployment much later on, and trying to find the cause will be
| very difficult.
|
| *Infrastructure Patterns*
|
| _test_ , _stage_ , and _prod_ servers are like pets. You have
| individual relationships with them, change them in unique ways,
| until eventually they have their own individual personalities.
| They become silos that pick up peculiarities that will not be
| reflected in other environments, and will be hard to replicate or
| rebuild later.
|
| Instead, use ephemeral infrastructure (the "cattle" in "pets vs
| cattle"). There should be a "production" infrastructure, which is
| built with Infrastructure-as-Code, to create an immutable
| artifact that can simply be deleted and re-created automatically.
| That same code that builds production should build any other
| server, for example for testing or staging. When the testing or
| staging is done, the ephemeral copy should be shut down. They
| should all be rebuilt frequently to prevent infrastructure rot
| from setting in.
|
| This pattern does a lot of things, like making sure you have
| automation for disaster recovery, using automation to prevent
| inconsistencies, using automation to detect when your
| infrastructure-as-code has stopped working, saving money by
| turning off unneeded resources, and the ability to spin up a
| unique copy of your infrastructure with unique changes in order
| to test them in parallel to your other infrastructure/changes. It
| also makes it trivial to test upgrades, patch security holes, or
| destroy and recreate compromised infrastructure. And of course it
| saves you time in the long run, because you only expend effort to
| set it up once.
|
| *This Is Not About Scaling*
|
| I know the first thing everyone's going to complain about is
| something like "I'm not Facebook, I don't need all that!" or "It
| works fine for me!".
|
| There's a lot of things we do today that are better for us than
| what we did before, even though we don't have to. You brush your
| teeth and wash your hands, right? Well we didn't used to do those
| things. And you can still live your life without doing them! So
| why do them at all?
|
| Because we've learned about the downsides of _not_ doing them,
| and the benefits outweigh the downsides. Getting into the habit
| of doing things differently may be annoying or painful at first,
| but then they will become second nature, and you won 't even
| think about it.
___________________________________________________________________
(page generated 2025-07-21 23:00 UTC)