Seems odd to announce new features without calling out to the fact that libraries with the same name in other languages have had those same features for years. (for example, the dotenv gem in Ruby)
True, but the dotenv npm package has been around much longer than that support (11 years vs 9 months), and the .env support in Node doesn't yet support features like auto-inclusion of environment-specific files.
I don't get it. Dotenv is only good for local dev. Otherwise you should put your secrets in environment variables (the "env" in ".env"). That people put .env files in prod is a mistake itself, and the proposed fixes here seem to not really do much about that.
Just set those env vars in your IDE. Let your IDE or docker-compose or whatever read an .env file if you must. But don't do it directly from your application code, indeed you're one lazy dev away from putting an .env file on prod servers.
Using dotenv-like constructions is, in my eyes, an antipattern.
Please don't configure important things in the IDE. IDE configs are notoriously fickle and checking them into the repo usually just leads to a lot of problems in my experience, plus it will mean duplicated effort every time you need to do something from the CLI.
You can commit your secrets as an encrypted vault with this. Then decrypt it with a key where needed: locally, on CI, on prod, etc.
This is basically a simplified version of Hashicorp's Vault, GCP key vault etc. with some less granularity on user authentication.
It solves the issues around .env.example and is perfect for gitops. You have all your secrets for all your envs ready, while you only need to set a single env var (the private encrpytion key) on your specific hosting environment.
You could even use separate keys per env, eg.: to give access to a developer to staging only.
Mozilla's https://github.com/getsops/sops is another contender but with a more complicated (and perhaps more flexible) key management.
If something is compromissed you have a problem, whether with encrpyted key or something else.
If your env-data is compromissed you have to set new in all services and restart your app / container.
But the enc-env file could be shared in you team or published to server without any problem. In the past this was a problem, when publishing accidently with plain text passwords.
Dotenv is an okay hack, but the root problem is that the operating systems we run these applications in don't have a fully conceived environment variable system.
I think you have a fair point that dotenvx doesn't get the implementation right, but it does at least seem to recognize where the problem lies and is trying to fix it from that angle. You have to start somewhere. Almost never do we get solutions right the first time. It takes iteration and experimentation and perhaps this (and others like it) can pave the way towards a better solution.
That is the point, the OS / container-system must provide a secure system for storing and accessing the crypted keys.
In the best case your app sends (something like gRPC) to the OS-key-system, that adds decrypted keys and executes the function. So you app will never have direct access to the decrypted keys. Like fingerprint-system in smartphones.
Secrets don’t belong in environment variables either. Place them in a vault and grant specific processes/identities permission to read and decrypt them.
Env vars are prone to leaking and best practice moves the goal post further. Devs love to dump envs to log files, child processes inherit them, admins can very easily sniff them.
Secrets in env vars in production is not too secure either, ideally you’ll move to your app pulling secrets in-process from your infrastructure at boot-up or upon use. This also gives a nice advantage of not needing to rebuild the app or container or whatever to rotate a secret.
This just moves the problem to a different step. How are you going to manage access to said secrets, especially when your application lives off premises?
This is still “env vars”, easy to read from /proc/*/env too see the decrypted secrets from a different process. Versus in-process only secret fetch where you’d need to scan the memory pages of the app, which is a bit harder - especially if you keep the credentials in memory in a scrambled format so a simple scan on process memory for “secret_prefix_” doesn’t find them.
If an attacker can read other processes' envs you've pretty much lost as they're
1. Inside your process which means they can see the decrypted values.
2. Root which means they can get into your process to see the decrypted values.
I'm not sure if your average dev has a threat model that assumes in memory scrambling let alone leaked env vars. After all we're talking about the standard way to do it being populating a file with the decrypted secrets and just leaving it there. All the security is already kernel security.
I'm honestly not sure who dotenvx is aimed at.
- No one security conscious is going to be cool just making the cyphertext available publicly or even internally.
- Someone scrambling in-memory secrets isn't using dotenv to begin with, is using SecretsManager and the like, and probably doesn't want to change those to now go through the filesystem. You now get less auditing because all those secrets are bundled and you now only know "they accessed the decryption key."
- And someone using dotenv for secrets doesn't have a threat-model where this meaningfully improves security.
In adittion, if I'm not mistaken, child processes inherit the parent env vars, so if your application forks or use subcommands, you may be exposing the whole environment trove to 3rd party scripts, no root needed. Also, most vulnerabilities that enables execution of code will happily leak the env vars, no root access or "being inside the process" thingy (I know, code execution is technically "inside the process", but without requiring privileged levels)
I’m advocating people use something like SecretsManager, not this thing. In-memory only > env vars > secret files on disk.
I find env vars very precarious because harmless developer debug logging, actions like sshing into a container and typing `env` etc can easily expose them.
File on disk can be read by an attacker with via subdirectory path traversal bug
It’s much less likely for in process only secrets to be exposed by common mistakes/bugs
Yeah but it seems like the right step — deployment, not implementation. All my secrets are managed by GitLab AFAIR, but in general I feel way better integrating secrets into each service/container (I happen to use Dokku, so it’s as simple as something like `dokku add_secret APPNAME SECRET`) than having a central system.
I am far too clumsy to trust myself to push secrets in encrypted form, personally
I've always wondered this. Seems like a password to get more passwords is potentially actually less secure (as in practice people will reuse master keys and they might actually increase the surface area, or make it a persistent threat after the keys themselves rotate)
In AWS and other clouds, you application can use an infrastructure provider API to create a secure session to access infrastructure APIs with per-application-instance credentials that are automatically rotated and can be used only by that instance. These APIs are how the cloud provider themselves provides environment variable injection features, but if your application consumes these APIs directly you can avoid having decrypted secrets hanging out in environment variables as a middleman between your app and the cloud runtime.
Typically the application instance sessions are automatically rotated very frequently, AWS’s sessions are limited to 6 hours for example.
Dotenvx has a cloud hub from which the keys can be pulled. I imagine an eventual feature will be for the keys to expire, and you’ll have to re-authenticate with the cloud to get new credentials, just as you would with AWS.
Yeah, this is a really weird post for me. Before dotenv existed I used Foreman. It worked basically exactly how "dotenvx cross-platform" works (as a global command line). I switched to node-foreman because I largely switched to working on node projects and didn't want devs to need to have ruby installed. Then, at some point I switched to dotenv, and I don't even remember why.
I think for some reason people don't like such simple tools. I think it seems more reasonable to deploy a vault service running on kubernetes with jenkins. In some comments, they didn't even understand what the tool was for. Dear dude, you can travel between dimensions with this tool.
Encrypting secrets and committing them seems very convenient but I'm paranoid about these sorts of things. Can anyone tell me why this would be a bad idea?
One reason I can think of is that normally with secrets I actually don't keep any copies of them. I just set them in whatever secret manager my cloud environment uses and never touch them again unless I need to rotate them. Meaning there is no way to accidentally expose them other than by the secret vault being hacked or my environment being hacked.
With this approach if someone gets access to the encryption key all secrets are exposed.
I disagree. Using vaults isn’t that bad. And I’d also like developers to never actually know the secrets.
It’s a learning curve, but I think it’s best to just bite the bullet and use a vault rather than trusting developers to know and manage secrets properly.
Another benefit is that debugging secret changes is a lot easier. We've had a couple of cases where someone changes the secret in a vault and that causes problems and no one can tell what changed between two deploys
The biggest issue with storing secrets in version control with the code is that past secrets are never relevant after they have been rotated. This makes rollbacks risky. Consider:
1. Create secret v1
2. Code v1
3. Deploy
4. Secret v2 (rotation)
5. Code v2
6. Deploy
7. Oops, need to roll back to v1 (from step 2)
8. Outage, because the secrets in step 2 are not the secrets from step 4
Inserting deployments between steps 1 and 2 and between steps 4 and 5 still results in an outage when the deploy for code v2 needs to be rolled back to the older commit, not the commit before. Arguing that rollbacks means you only ever need to rollback to commit #n-1 is like arguing you should never retain more than one backup; there's always a chance that you need to rollback even further to fix things.
I've used git-crypt and sealed-secrets and the problem is always backing up the master key. sealed-secrets rotates it every so often so you need to go find it and copy it to 1password or whatever you use as your root of trust. (We used a calendar invite for this.)
git-crypt is easy, the master key doesn't rotate, so don't leak it. (Secret encryption key rotation is kind useless; it's nice that if you leak an old key newer secrets aren't leaked, but it depends on your underlying secret rotation policy as to whether or not that saves you any work. I have tended to do them in bulk in the past.)
On my last project we did disaster recovery exercises every 6 months, sometimes with the master key intentionally lost, and it wasn't that big of a deal. Restoring your infra-as-code involves creating the secret encryption service manually, though, which is kind of a pain, but not like days of downtime pain or anything. Of course, if the secrets encrypted your database or something like that, then losing the master key equals losing all your data. Hopefully your database backup has some solution for that problem.
It was added in a backwards compatible way, but the author decided to make a breaking change with this release.
The previous (IMHO superior) version was generating a .env.vault and a .env.keys from a .env file. Leaving the .env plain text and .env.vault encrypted.
Even without backwards compatibility, why not just a breaking change in the same project? Don't get it either.
I've never used it (knowingly) but if I did and wanted to use this new version/project even the CLI name change to append 'x' would be annoying (I'd probably alias /symlink it).
I don't think encryption is a good idea, and the reason is forming bad habits. Now developers have a very strong and non-ambiguous habit: never put .env files in version control (except may be for .example.env). However, with this, you'll get accustomed to commit .env in _some_ projects, so you'll easily slip and commit it in another project where the vars are not encrypted.
I too have been using sops for years, and agree -- dotenvx encryption seems very similar to sops.
I'd prefer an integration between dotevnx and sops where dotevnx handles the UX of public env and injection, while leveraging sops for secret management and retrieval. Additionally, being able to have multiple keys for different actors is important.
Having a single `.env.keys` file feels risky and error prone. dotenvx encourages adding your various env files, such as `.env.production`, to vcs, and you're one simple mistake away from committing your keyfile and having a bad day.
If sops is not to be integrated, dotenvx could take some inspiration where the main key is encrypted in the secrets file itself, and you can define multiple age key recipients, each of which can then decrypt the main key.
I've been using sops in production since at least 2017, plus it has excellent compatibility with containerized infra tools like helm and other infra tools like terraform (both technically using plugins, but helm secrets and the carlpetts terraform plugin have been around for ages and are widely used.
Can a kind soul point me some documentation on how to put .env in a vault correctly? Possibly open-source solutions?
If the vault is password protected, aren't you just adding one more indirection and nothing more? How is that helpful, since now I have to write the vault password in clear-text somewhere such that my application can read the env file from the vault?
A lot of modern cloud deployments read from a secret management system or vault at deployment time, and the secrets are made accessible to the application through various indirect methods so they cannot be accessed later on (i.e. if someone were to gain access to a running Kubernetes container).
At no point does the application have access to the vault itself, and access to read the vault is guarded by IAM role permissions.
Something else pulls from the vault and exposes it as a regular env var to your process. The .env file is a workaround to get this running locally! For open source, the simplest to explain is using a k8s secret mounted to a pod — pod identity allows access to the secret via rbac, which cannot be faked. Only that pod has access to that secret. But your process, running on the pod, sees it as an env var.
That doesn't cover what the GP was talking about with
> (i.e. if someone were to gain access to a running Kubernetes container)
right? Since those would still be secrets available in the env.
I get that if someone has access to read your envvars, its a foregone conclusion already (about how compromised you are).
However IIUC, the part of the point of doing things in memory with reading secrets (like with a Secrets Manager, is to eliminate having to keep secrets around as envvars/secret files in the runtime?
If they can snoop on env vars of a running pod, it can snoop on the process. A k8s secret could be a file in the pod or a env var in the process, but neither are a persistent file distributed to developers
During deployment, the pipeline connects to the Vault, dumps relevant secrets in a .env file that it pushes to production systems that the application then reads from.
A script fetches values from a database then sticks them in a file. This script runs before the your application starts so the file will be there waiting.
You were on the right path when you called a vault “one more layer of redirection.” A vault on its own won’t dramatically improve your security posture. Neither will a .env file.
Instead, they both beat the alternative (which is writing secrets and environmental config directly in the source code). And they’re both part of defending in depth.
Defense in depth is hard to explain to a five year old, so think of candy. You’re five and you’re obsessed with candy. If I don’t want you to eat pounds of it every single day, I’ll likely do different things to stop you.
1.) I’ll give you a reasonable amount of candy.
2.) I’ll explain that if you eat too much candy, you’ll face health consequences like tooth decay or childhood obesity.
3.) I’ll put the candy somewhere both out of sight and out of reach.
Software security is like that too. Instead of relying upon just one method, you’ll do a number of different things. On their own, few of them are really very useful. But when you combine them all together, you can end up with a reasonably secure system.
In the case of vaults, it’s just a slightly safer tradeoff with its own problems. We’ve already established that it’s bad to write secrets and environmental configuration info in source code. One way around that is to put secrets in a .env file but then distributing that file becomes the weak link. Maybe you Slack them around, or email them or maybe you write all the secrets on a whiteboard in your office? A vault has a lot of flaws, but it’s better than writing them on a whiteboard. In some threat models, it’s better than Slack or email.
Add your actual dotenv to .gitignore. Use bfg to make sure that any traces of your dotenv aren't in your commit history. Use detect-secrets client-side commit hook to confirm this and also monitor for secrets leakage. Use sops to encrypt your dotenv into some other file that is tracked by git. Sops can integrate with secrets management solutions (Vault, AWS KMS, etc.). Done.
This is mildly complicated, but the alternative is storing config in a configuration server somewhere, which comes with its own can of worms.
We’ve been pushing for committing encrypted secrets for many years now, and have written an open source spec and implementation in multiple languages: https://github.com/neosmart/securestore-rs
I got so excited, but it doesn't seem to support multiple keys and seems overly eager to encourage people to leave a valuable key lying around on disk.
So if a single dev machine is compromised, all of your prod secrets are exposed?
I wish this were closer to sops with support for gpg and or ssh keys. Because sops is a great idea locked in a questionable codebase.
Happy to discuss a proposal to add asymmetric key support to the project in the GitHub issue tracker. Although I'm not sure how the security changes with an asymmetric key, as either way the worst case scenario is the same?
Note that you don't have to leave the key "lying around" as you can secure it the same way you would an asymmetric key. And it certainly beats leaving the plaintext secrets themselves lying around in a .env file or similar.
EDIT:
I see you were saying "dev machine" exposes "prod secrets" but that's not the case. The protocol is designed so you would have secrets.json and secrets.prod.json, encrypted with different keys and (necessarily) managed separately but with the same tools and api. Dev machines being compromised compromises dev keys, not prod keys.
Read the last section in the README on GitHub for more on the dev/prod split.
Asymmetric keys mean you can you can have per-dev or per-team keys and allow one team to rotate them and resign them for all other consumers. I don't know how you'd do that with symmetric keys. This is an important feature of sops, imo.
It also means I can do things like seal them to a key that is stored in KeyVault and then allow the transparent retrieval of that key at runtime on Instances that have been given an identity with access.
This means that production secrets are sealed in place and only openable by effectively authenticated workloads.
And if you use sops-nix, this becomes a "setup once and never think about it ever again, ever" kind of operation.
You keep the key wherever you want to keep the key, just don't commit it and don't distribute it. Put it in on a YubiKey for your devs, upload it out-of-band securely to prod.
Whether it's a symmetric key or an asymmetric key, you have the same problem. Someone overriding your secrets is definitely not high on the list of concerns, and if they're committed to git then they can never be truly overwritten.
On my phone so can’t double test, but can’t you get this by adding “export” in front of every line in your env file and then source before running command?
I suppose if you don’t want it to stay after execution i believe you can:
> $(source .env; my command)
I’m sure there is a fairly straightforward way to encrypt and decrypt a local file
dotenv has features that include conditional selection and ordered merging of env files, which are configurable by dotenv's runtime and buildtime APIs.
At least merging can happen by sourcing .env files in order (last to be sourced has priority)
But I do agree that at some point you want a tool to orchestrate these things and guide your usage so you don’t have to reinvent the same lines of code all the item
Now take that a step further with Environment Modules and you can source them from any shell or scripting language: https://modules.readthedocs.io/ (without creating a subinterpreter)
With leaking secrets being such a big concern, it seems wise to require that secrets be encrypted to use dotenvx. That is, it will only work with encrypted secrets. As others have commented, this doesn't eliminate the risk entirely, but I think having a tool that doesn't support unencrypted secrets at all, although a bit less convenient, is a win.
The utility has a means of encrypting them with public key cryptography so that the plaintext is never in your development directory. GP thinks this should be made mandatory.
Part of my point is that if you care about security, you need to be thinking about these things. Forcing encryption then also forces you to think about how to provide the key. Of course, it's possible to do this in a very insecure way that defeats the purpose of encryption in the first place, but I think it moves things in the right direction.
No, I mean encrypted. I'm not sure what you mean by "obscured", but if you just mean obfuscated in some easily recoverable way without a key, then no. If you read the original post, it describes the mechanism that dotenvx already has in place for encryption.
I'm not sure what the "it" you're referring to is, but if something is not a secret, you could either encrypt it anyway or use an alternate mechanism to provide the value.
The whole idea of using environment variables for configuration information is good, but ultimately flawed, and we are way past the point where this should continue to be the status quo.
Environment variables are great for configuration because:
- you can inherit them from a previous application or application(s)
- you can override them in each environment you run your app in
- you can pass them on to other applications
- they are globals that can be loaded by libraries
- they're not hardcoded in the code, so easier to change things without rebuilding, easier to reuse in different ways/environments/configurations
- the OS has primitives for them
- they're simple
Environment variables are bad for configuration:
- because (by default) when set in application, they are passed on to all future applications/forks/execs
- they are often dumped as part of troubleshooting and aren't considered confidential
- they can often be viewed by external processes/users
- there are restrictions on key names and values and size depending on the platform
- typical "dotenv" solution doesn't necessarily handle things like multi-line strings, has no formal specification
- no types, schemas
What we actually need that environment variables are being used for:
- configuration information passed at execution time that can change per environment
- loading or passing secret values
- development environments
- production environments
So what would be a good alternative?
- an application library ("libconfig") that can load configuration of various types from various sources in various ways
- support for configuration types: key-value, file/blob, integer/float
- support for confidentiality (require specific function to unseal secret values; in programming languages the intent would be you can't just print a stringified version of the variable without an unseal function)
- support for schema (application defines schema, throws exception if value does not match)
- support allowing a configuration to be overloaded by different sources/hierarchies
- support passing a configuration on to other applications
- support tracing, verbose logging
- truly cross-platform and cross-language with one specification, behavior for all
How would it work?
- devs can create a .env file if they want
- devs load 'libconfig' into app, use it to load their configuration values during development. library can have default sources, and even set env vars or an object internally, so no code needs to be written to use it
- in production, same code causes libconfig to look at cloud-native and other sources for configuration
- when debugging, secret confidentiality is maintained, tracing communicates sources of configuration, what was loaded, from where, etc
Super early days but we're building something very similar to what you are describing over at https://dmno.dev
Granted our solution is more javascript/typescript focused - and the config schema will be defined using TypeScript and piggyback on npm for sharing plugins/extensions. But the config will be usable in any language (and generate types for multiple languages) with deeper integrations coming soon.
The pluggable nature of our architecture also means you can encrypt secrets in your repo if you want to, or sync with other backends. Shouldn't be too hard to keep everything away from env vars either if that's what some folks want.
Would love your input, and to hear what you think!
The feature list looks great, but it looks like it's trying to bite off too much at once.
- Supporting a particular repo type is an engineering smell (over-complicated/over-opinionated/tightly-integrated); this should be repo-design-agnostic
- The "dmno service" is also biting off too much, this should not be a concept inherent to the configuration library
- Schemas should be optional
- Data types are fine, but get complicated, especially when mapping between different data formats/containers/transports; it's probably better to start with only a couple types and grow them over time if needed
- Inter-service dependency management is also too complex for this solution
- Plugins are a good idea
- Three different package types? Complexity...
- The security features are great
I get that you're trying to sell a product, and so having a big kitchen sink makes it more attractive to buy, but it makes for more complicated solutions which then annoy users.
I _detest_ this kind of encryption. It's literally worse than useless. It makes life much harder during debugging, and it eventually leads to developers just storing the decryption keys locally.
For this kind of encryption to work, you need to supply the decryption key from some outside system (e.g. via env vars, AWS SSM, etc.). And if it can supply the key, then why not just use it for other important secrets directly?
it might be easier to just store and checkout a single decryption key that only devops people know, vs storing hundreds of secrets.
while developers can move around their .env file across systems without worrying that they left plaintext secrets somewhere.
also it allows adding new secrets without knowing decryption key - I think it is important for collaboration
also most importantly: plaintext decrypted secrets are never stored on disk, and only kept in memory. I think it is also an improvement towards the regular doting
> it might be easier to just store and checkout a single decryption key that only devops people know,
"Devops people know" means that the key must be some secret property. Or you need to use the key during the deployment artifact building pipeline, and then deploy the artifacts with clear-text secrets.
> vs storing hundreds of secrets.
Then serialize them to JSON or whatever.
> also it allows adding new secrets without knowing decryption key - I think it is important for collaboration
So basically, you want developers (who don't have access to prod) to add random properties that your peers can't see during the code review? Ok...
Sorry, there's just no way the encrypted secrets in git are a good idea for general-purpose software.
encrypted secrets are strictly an improvement over status quo of unencrypted secrets on disk - I dont understand why you make it seem like it is a bad idea?
like what is the alternative you propose? storing plaintext secrets on disk and hope that your runtime is secure and hardened enough and free from vulnerabilities??
as if directory traversal, path injection vulnerabilities, shell command injection, etc vulns that allow reading file from disk, don't exist?
> encrypted secrets are strictly an improvement over status quo of unencrypted secrets on disk
No. They are NOT. They are strictly worse than unencrypted data.
Unecrypted data is at least honest. Simply encrypting it and putting the key next to the data itself creates a dangerous illusion of security.
> like what is the alternative you propose? storing plaintext secrets on disk and hope that your runtime is secure and hardened enough and free from vulnerabilities??
Put secrets into your environment, don't store them on the disk.
If your code runs on AWS, then use AWS SSM or AWS Secrets Manager. If it's on Heroku, put secrets into the env vars. K8s has a secret manager. And so on.
It's also problematic from a secrets management perspective, because a big part of the perceived value of encryption is being able to check secrets into git. But because the encryption is tied to long-term keys, you have to design your security processes with the assumption that those keys will eventually be exposed and need to be "revoked" (ie: the secrets re-encrypted), and the "de-revocation" of those keys is hiding in your git history.
223 comments
[ 3.0 ms ] story [ 221 ms ] threadhttps://nodejs.org/en/blog/release/v20.6.0#built-in-env-file...
Using dotenv-like constructions is, in my eyes, an antipattern.
How is that different from a dotenv, other than location of where the k/v persists?
This is basically a simplified version of Hashicorp's Vault, GCP key vault etc. with some less granularity on user authentication.
It solves the issues around .env.example and is perfect for gitops. You have all your secrets for all your envs ready, while you only need to set a single env var (the private encrpytion key) on your specific hosting environment.
You could even use separate keys per env, eg.: to give access to a developer to staging only.
Mozilla's https://github.com/getsops/sops is another contender but with a more complicated (and perhaps more flexible) key management.
(author of rot)
If your env-data is compromissed you have to set new in all services and restart your app / container.
But the enc-env file could be shared in you team or published to server without any problem. In the past this was a problem, when publishing accidently with plain text passwords.
I think you have a fair point that dotenvx doesn't get the implementation right, but it does at least seem to recognize where the problem lies and is trying to fix it from that angle. You have to start somewhere. Almost never do we get solutions right the first time. It takes iteration and experimentation and perhaps this (and others like it) can pave the way towards a better solution.
In the best case your app sends (something like gRPC) to the OS-key-system, that adds decrypted keys and executes the function. So you app will never have direct access to the decrypted keys. Like fingerprint-system in smartphones.
It wouldn't surprise me if many VPS use .env files.
Env vars are prone to leaking and best practice moves the goal post further. Devs love to dump envs to log files, child processes inherit them, admins can very easily sniff them.
there are costs associated with adding additional layer in regards to maintenance of such layer.
easiest way to bring down your entire distributed infrastructure and cause large scale outage is when your vault is down...
`start: dotenvx run -f .env.local -f .env -- node index.js`
Instead of the -f flag, which now cannot be overriden, one could invoke it with
`DOTENV=.env.staging npm run start`
For example
DOTENV_PRIVATE_KEY_PRODUCTION
Would provide it with the information it needs to read .env.production
For example AWS gives you multiple ways of injecting secrets as env vars into your containers when they boot up (ECS + secrets manager, EKS, etc)
1. Inside your process which means they can see the decrypted values.
2. Root which means they can get into your process to see the decrypted values.
I'm not sure if your average dev has a threat model that assumes in memory scrambling let alone leaked env vars. After all we're talking about the standard way to do it being populating a file with the decrypted secrets and just leaving it there. All the security is already kernel security.
I'm honestly not sure who dotenvx is aimed at.
- No one security conscious is going to be cool just making the cyphertext available publicly or even internally.
- Someone scrambling in-memory secrets isn't using dotenv to begin with, is using SecretsManager and the like, and probably doesn't want to change those to now go through the filesystem. You now get less auditing because all those secrets are bundled and you now only know "they accessed the decryption key."
- And someone using dotenv for secrets doesn't have a threat-model where this meaningfully improves security.
I find env vars very precarious because harmless developer debug logging, actions like sshing into a container and typing `env` etc can easily expose them.
File on disk can be read by an attacker with via subdirectory path traversal bug
It’s much less likely for in process only secrets to be exposed by common mistakes/bugs
I am far too clumsy to trust myself to push secrets in encrypted form, personally
Typically the application instance sessions are automatically rotated very frequently, AWS’s sessions are limited to 6 hours for example.
The URL in `dotenvx` points to https://gitub.com/dotenvx/dotenvx (gitub without the h)
[0] https://github.com/ddollar/foreman
[0] https://theforeman.org
One reason I can think of is that normally with secrets I actually don't keep any copies of them. I just set them in whatever secret manager my cloud environment uses and never touch them again unless I need to rotate them. Meaning there is no way to accidentally expose them other than by the secret vault being hacked or my environment being hacked.
With this approach if someone gets access to the encryption key all secrets are exposed.
Typically, developers can’t change production secrets in vaults and need to follow some other protocols.
Encrypted secrets mean you deploy everything along side the secrets.
The developer experience is great, but the biggest issues I have faced while using Kubeseal were
1. Developers HAVE the secret in order to encrypt it. This can be not ideal as then they can use these secrets in production or leak them
2. The secret encryption key change causes the need to re encrypt everything.
3. People don’t understand the concept.
It’s a learning curve, but I think it’s best to just bite the bullet and use a vault rather than trusting developers to know and manage secrets properly.
git-crypt is easy, the master key doesn't rotate, so don't leak it. (Secret encryption key rotation is kind useless; it's nice that if you leak an old key newer secrets aren't leaked, but it depends on your underlying secret rotation policy as to whether or not that saves you any work. I have tended to do them in bulk in the past.)
On my last project we did disaster recovery exercises every 6 months, sometimes with the master key intentionally lost, and it wasn't that big of a deal. Restoring your infra-as-code involves creating the secret encryption service manually, though, which is kind of a pain, but not like days of downtime pain or anything. Of course, if the secrets encrypted your database or something like that, then losing the master key equals losing all your data. Hopefully your database backup has some solution for that problem.
It would only break in cases where people's values specifically started with "encrypted:"
The previous (IMHO superior) version was generating a .env.vault and a .env.keys from a .env file. Leaving the .env plain text and .env.vault encrypted.
I've never used it (knowingly) but if I did and wanted to use this new version/project even the CLI name change to append 'x' would be annoying (I'd probably alias /symlink it).
Sops also integrates easily with AWS and other existing key management solutions, so that you can use your existing IAM controls on keys.
I mentioned in another comment, but I've been using it over five years at two jobs and have found it to be great.
[0]: https://github.com/getsops/sops
I'd prefer an integration between dotevnx and sops where dotevnx handles the UX of public env and injection, while leveraging sops for secret management and retrieval. Additionally, being able to have multiple keys for different actors is important.
Having a single `.env.keys` file feels risky and error prone. dotenvx encourages adding your various env files, such as `.env.production`, to vcs, and you're one simple mistake away from committing your keyfile and having a bad day.
If sops is not to be integrated, dotenvx could take some inspiration where the main key is encrypted in the secrets file itself, and you can define multiple age key recipients, each of which can then decrypt the main key.
If the vault is password protected, aren't you just adding one more indirection and nothing more? How is that helpful, since now I have to write the vault password in clear-text somewhere such that my application can read the env file from the vault?
At no point does the application have access to the vault itself, and access to read the vault is guarded by IAM role permissions.
I hope someone can do me a ELI5.
> (i.e. if someone were to gain access to a running Kubernetes container)
right? Since those would still be secrets available in the env.
I get that if someone has access to read your envvars, its a foregone conclusion already (about how compromised you are).
However IIUC, the part of the point of doing things in memory with reading secrets (like with a Secrets Manager, is to eliminate having to keep secrets around as envvars/secret files in the runtime?
Instead, they both beat the alternative (which is writing secrets and environmental config directly in the source code). And they’re both part of defending in depth.
Defense in depth is hard to explain to a five year old, so think of candy. You’re five and you’re obsessed with candy. If I don’t want you to eat pounds of it every single day, I’ll likely do different things to stop you.
1.) I’ll give you a reasonable amount of candy.
2.) I’ll explain that if you eat too much candy, you’ll face health consequences like tooth decay or childhood obesity.
3.) I’ll put the candy somewhere both out of sight and out of reach.
Software security is like that too. Instead of relying upon just one method, you’ll do a number of different things. On their own, few of them are really very useful. But when you combine them all together, you can end up with a reasonably secure system.
In the case of vaults, it’s just a slightly safer tradeoff with its own problems. We’ve already established that it’s bad to write secrets and environmental configuration info in source code. One way around that is to put secrets in a .env file but then distributing that file becomes the weak link. Maybe you Slack them around, or email them or maybe you write all the secrets on a whiteboard in your office? A vault has a lot of flaws, but it’s better than writing them on a whiteboard. In some threat models, it’s better than Slack or email.
It adds depth but it’s far from perfect.
This is mildly complicated, but the alternative is storing config in a configuration server somewhere, which comes with its own can of worms.
So if a single dev machine is compromised, all of your prod secrets are exposed?
I wish this were closer to sops with support for gpg and or ssh keys. Because sops is a great idea locked in a questionable codebase.
Note that you don't have to leave the key "lying around" as you can secure it the same way you would an asymmetric key. And it certainly beats leaving the plaintext secrets themselves lying around in a .env file or similar.
EDIT:
I see you were saying "dev machine" exposes "prod secrets" but that's not the case. The protocol is designed so you would have secrets.json and secrets.prod.json, encrypted with different keys and (necessarily) managed separately but with the same tools and api. Dev machines being compromised compromises dev keys, not prod keys.
Read the last section in the README on GitHub for more on the dev/prod split.
It also means I can do things like seal them to a key that is stored in KeyVault and then allow the transparent retrieval of that key at runtime on Instances that have been given an identity with access.
This means that production secrets are sealed in place and only openable by effectively authenticated workloads.
And if you use sops-nix, this becomes a "setup once and never think about it ever again, ever" kind of operation.
I've used it at two jobs now over about 5 years and have had zero issues.
SecureStore was launched in 2017 (initial version was .NET only): https://neosmart.net/blog/securestore-a-net-secrets-manager/
And where do I keep the key? In a secret store?
Whether it's a symmetric key or an asymmetric key, you have the same problem. Someone overriding your secrets is definitely not high on the list of concerns, and if they're committed to git then they can never be truly overwritten.
I suppose if you don’t want it to stay after execution i believe you can:
I’m sure there is a fairly straightforward way to encrypt and decrypt a local fileBut I do agree that at some point you want a tool to orchestrate these things and guide your usage so you don’t have to reinvent the same lines of code all the item
But in reality, nobody will do that.
Environment variables are great for configuration because:
Environment variables are bad for configuration: What we actually need that environment variables are being used for: So what would be a good alternative? How would it work?Granted our solution is more javascript/typescript focused - and the config schema will be defined using TypeScript and piggyback on npm for sharing plugins/extensions. But the config will be usable in any language (and generate types for multiple languages) with deeper integrations coming soon.
The pluggable nature of our architecture also means you can encrypt secrets in your repo if you want to, or sync with other backends. Shouldn't be too hard to keep everything away from env vars either if that's what some folks want.
Would love your input, and to hear what you think!
And the attackers will be after this file not the .env anymore.
It looks great nonetheless, especially the cross-language feature.
In Rails, the entire file is encrypted unlike here where only the secrets are
For this kind of encryption to work, you need to supply the decryption key from some outside system (e.g. via env vars, AWS SSM, etc.). And if it can supply the key, then why not just use it for other important secrets directly?
while developers can move around their .env file across systems without worrying that they left plaintext secrets somewhere.
also it allows adding new secrets without knowing decryption key - I think it is important for collaboration
also most importantly: plaintext decrypted secrets are never stored on disk, and only kept in memory. I think it is also an improvement towards the regular doting
"Devops people know" means that the key must be some secret property. Or you need to use the key during the deployment artifact building pipeline, and then deploy the artifacts with clear-text secrets.
> vs storing hundreds of secrets.
Then serialize them to JSON or whatever.
> also it allows adding new secrets without knowing decryption key - I think it is important for collaboration
So basically, you want developers (who don't have access to prod) to add random properties that your peers can't see during the code review? Ok...
Sorry, there's just no way the encrypted secrets in git are a good idea for general-purpose software.
like what is the alternative you propose? storing plaintext secrets on disk and hope that your runtime is secure and hardened enough and free from vulnerabilities??
as if directory traversal, path injection vulnerabilities, shell command injection, etc vulns that allow reading file from disk, don't exist?
No. They are NOT. They are strictly worse than unencrypted data.
Unecrypted data is at least honest. Simply encrypting it and putting the key next to the data itself creates a dangerous illusion of security.
> like what is the alternative you propose? storing plaintext secrets on disk and hope that your runtime is secure and hardened enough and free from vulnerabilities??
Put secrets into your environment, don't store them on the disk.
If your code runs on AWS, then use AWS SSM or AWS Secrets Manager. If it's on Heroku, put secrets into the env vars. K8s has a secret manager. And so on.