-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
RFC - envProcessor to decrypt secrets for simple applications #27351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
the main reason for the |
Following @stof's commend, does this have to be an env processor? |
In fact, half of the secret is committed, to be revealed, the private key is required. This key should exists only at runtime. The encrypted secret could also be available only at runtime (if it's committed in a Dockerfile for instance) Using env preprocessor is also simpler for developers as documentation and recipes used that syntax. |
Thinking about the @nicolas-grekas comment. When a developer want to add a new secrets:
This command generate a new file (uses https://tools.ietf.org/html/rfc7516)
In order to use it in production, developer have to decrypt secrets first
This command dump the revealed secrets
Application can use it
How to configure several environment? for instance (preprod vs prod or prod-eu vs prod-us) ? How to not break dev environment? Going further with semantic configuration
Comming to the conclusion that it could be done in a dedicated bundle... |
I'm sharing some resources about how other frameworks have solved this very same problem, so we can take ideas from them:
|
@nicolas-grekas encrypt+export to a file seems bad because then you have a file with plain text credentials on your filesystem again. If someone can read the file you lose your credentials. It's better to keep the encrypted values on your filesystem at most and only use decrypted values in the runtime. I think credentials should not be in the environment because if someone leaves a phpinfo, there will be plaintext credentials and/or decryption key in there. So why use the environment anyway and not just the runtime? |
if someone can read your files, they can get the private key and decrypt the data on their own, so that this makes no difference, isn't it? the extra file makes it a bit more difficult to deal with secrets, as it requires one extra step to work. Looking at other frameworks, it looks like they decode the secrets at runtime, so maybe this is not an issue after all and we should get rid of the file for better DX? @jderusse I'm not sure about the "bucket" thing. Can't we deal with this using just standard parameters to vary the path of the stored secrets by region or whatever? You must have a parameter already that gives e.g. the region, isn't it? One less concept to grasp would be better :) |
+1 for no extra files. Decode secrets at runtime seems like what we need to do About the decryption key. How do you think we should protect the key? From CLI, the file itself should be available obviously. But, from FPM I would take the same approach like how webservers do it with SSL keys. It would make sense if the file path is defined in FPM with an ini setting, and only accessible by the parent process. The parent should load the key and make the key contents available in the runtime (not the environment vars) but the file itself is not available from FPM. If someone has file access from FPM, he can download your encrypted secrets but not your decryption key. I am in the process of making this a solution so your opinion has great value to me. |
The more I think about it, the more I love the simplicity of # service.yaml
framework:
secrets:
rsa_provider:
type: RSA
key:
public: %kernel.config_dir%/secrets/keys/public.jwk.json
private: /opt/application/secret-key.jpsk.json #could be defined in an env variable to
encrypted_folder: %kernel.config_dir%/secrets/
my_vault:
type: vault
endpoint: %env(VAULT_DSN)%
token: %env(VAULT_TOKEN)%
third-party-bundle:
aws-secret:
prefix: /prod/baz
parameters:
%env(SECRET_KEY)%: /production/database/bar
bar: %env(secret:my_vault:SECRET_KEY)% This add an opportunity to let third party registering their own secret mechanism... |
@jderusse Good idea! but it could be better without env vars. Actually, I still don't get how do you find deploying using environment variables is straight forward... Since in the best case scenario when adding/deleting or editing an env variable you have to update the /etc/environment or .bashrc file or any equivalent + sourcing the file to pick up the changes AND editing the webserver config + reload/restart !! Or I'm missing something here ? |
@nayzo for our 4.x projects we're copying the environment variables all over the place, which is madness compared to the old
We basically never touch I can't remember what exactly about |
@keichinger if I get this correctly you have multiple projects on the same server! That is odd to me since you can get some env vars collision between projects like database credentials or |
Each project is running in its own PHP-FPM pool (e.g. So far we've had a good run with this approach. We're kinda eyeing to upgrade our infrastructure to a Kubernetes cluster but for that we'd need some additional research and prototyping. Since we're an agency it needs to work for all of our projects in a seemless and reliable way while making sure our hosting prices won't go nuts, otherwise customers will want to host it elsewhere, which makes it harder to unify the deployment processes across hundreds of projects :) |
IMO secrets could benefit from their own "processor", like the env var processor. Being able to do They could also benefit from being conditionally cachable, depending on configuration. It looks like it would be a lot of work to add a different kind of processor though. Edit: I have been convinced that the path should be in an env var Replace the above with:
|
I'm a bit late to this discussion, but I'm going to repeat something really important here. PHP 7.2 comes with (Lib)sodium by default which is considered the most secure solution as to day. Given it's not easily possible to change the ENV (by an attacker) there is no need for authenticated encryption (as you can "trust" the value) (although I would recommend the usage of a IV in case the key is re-used) and focusing on the protection of leaking the encrypted value. But... I'm not expert on this 😅 @paragonie-scott can share you expertise here? |
This make me think that we should split this feature in severla PR (one by adapter) in order to ease the review and focus on such thing.
I'm not 100% agree on this. One of the use case is being able to commit secret. Because the encrypt value is not "reviewable/understandable" by the merger, should we presume that the commiter is trustable? |
I pushed the solution I created to https://github.com/Tobion/encrypted-secrets-bundle that we could use as a discussion base for the hackathon. How it works:
|
For the adapter side of this conversation, I did start work on a library (before I knew there was an interest in the Symfony community): https://github.com/secretary/php And a matching Symfony bundle: https://github.com/secretary/symfony (readme for this isn't up to date. I just copied it from the The purpose behind the library is to provide an abstract interface for working with Vault/Secret Stores (like HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, etc) |
I want to work on this at the EUFOSSA Hackathon. You can find me at the main entrance at the big table. |
Just joining this on FOSSHackathon. Not read all of it yet - just for the record, this is how Puppet manages this: https://github.com/voxpupuli/hiera-eyaml |
Coming back to my previous comment, having authenticated (signed) encrypted is always needed, because when the encrypted value is corrupted it would return garbage that cannot be used safely. |
For encryption & decryption purposes, I recommend: The NzoUrlEncryptorBundle is a Symfony Bundle used to Encrypt and Decrypt data and variables in the Web application or passed through the URL to provide more security to the project. Also it prevent users from reading and modifying sensitive data sent through the URL. Features include:
|
@nayzo Thanks for sharing, but this issue is about ENV variables not a URI's 😄 Secondly https://github.com/NAYZO/NzoUrlEncryptorBundle/blob/master/UrlEncryptor/UrlEncryptor.php#L86 NEVER! (Wording chosen carefully) use trim to remove the padding of an decrypted value! Always use a proper padding algorithm, which is best be done by crypto expert, these are extremely easy to get wrong and even harder to get right. If you use an older PHP version use Defuse otherwise Sodium or Halite 👍 Don't roll your own encryption! |
Let's start by defining our goals:
|
@sstok No the bundle is for encryption & decryption any sort of data including ENV vars 😆 |
Here's what @jderusse, @Tobion and me agreed upon after some discussion at FOSSHackathon:
Example: framework:
secrets:
files_backend: # the name used to refer to this provider
type: RSA # The type of the (imaginary) provider. This one would use public/private keys to encrypt secrets, keeping them in different files in a directory
# The following options are all specific to the particular provider
key:
public: %kernel.config_dir%/secrets/keys/public.jwk.json
private: /opt/application/secret-key.jpsk.json
encrypted_folder: %kernel.config_dir%/secrets/
vault_backend:
type: vault # maybe Hashicorp's Vault...?
#... whatever configuration settings that might need
Example: parameters:
mysql.password: %env(secrets:files_backend:mysql)% # Might give the decrypted contents of the `mysql` file in the folder configured above
some.other: %env(secrets:vault:secret/hello.foo)% # However you would refer to the "foo" key in the "secret/hello" secret Unknowns: Will this
We do not yet agree on how much commands and/or tooling we need to provide around listing, adding and managing secrets. The implementations will be highly provider-specific, and users might already have provider-specific tools in place (like the CLI for Vault, for example). |
Here is some good explanation of what rails does and how it changed over time: https://blog.capsens.eu/rails-5-2-imperfect-credentials-4ef03934aea4 |
It would be great if you could show a full realistic example of the workflow you propose to work with secrets. For example:
Thanks! |
Imo, this should separate logic between a bundle and a component too. |
Theire are 2 way to handle it:
This command will generate a file in Then user user should update his config file:
first of all: The material to decrypt secrets should be available on every servers runing the application. That's all. Symfony would be able decrypt secrets, at runtime, when required (on the fly), and use cache to optmize computation. Optionaly users may use a command (on the server)
When calling the method |
Here's a write-up of what I took away from today's discussion at the #EUFOSSA #FOSSHackathons https://www.webfactory.de/blog/storing-secrets-for-symfony-applications |
@javiereguiluz maybe this 👆🏻answers some of the questions. (That's how I would address the issue with what I know today, not speaking for @jderusse 😉🍻.) |
@jderusse fantastic explanation! Thanks. I only have a minor question about this phrase:
Are you saying that Symfony can store the unencrypted value in the cache (usually, a file in the filesystem)? Would that be a potential security problem? |
yes, that's what we mean. If attackers are able to read the cache folder, indeed, they are able to read the clear value. Do you think about a specific security issue. |
@Tobion In your comment is there one encryption key for all secrets, regardles if they are for At first I didn't get what's the point of encrypting secrets and store them in repo instead of using Some of our dev teams keep Ansible playbooks for generating config from commited Still, it must be elastic enough to read encrypted secrets based on environment (or maybe on some env variable?) and decrypt them using configured-per-instance key. And in my opinion it should support only local files because using Vault or other external providers misses the whole point of keeping secrets in sync between configuration and code in the same repo (you never know if secret is available in configured provider). And this "keeping in sync" thing is the only(?) advantage of this RFC, because in general it does not improve security since attacker who has access to the machine has everything to decrypt those values anyway. |
Yes, the use case where the dev team never gets access to the prod secrets to commit them should be taken into account. |
It is already: configuration is Symfony-env dependent - and so are env vars. |
@nicolas-grekas I was referring to the workflow where you "commit encrypted secrets to the repo" part, don't know how crucial that is to the overall current design direction. |
Oh, sure. We discussed about defaulting to storing secrets in |
@nicolas-grekas should it be Symfony-env dependent? In our case, where developers don't have access to production and production config it would mean that they won't be able to test application in production mode (because they won't have password/token to decrypt production config). And it's not good since these Symfony-envs are different (for example Sentry integration is for Right now we work on Or am I missing something? |
It's configuration depend: remove the envelope from the oath above and done. Fits all styles. By default it should be env-dependent I think yes. |
@Wirone using intermediate parameters would allow you to configure different strategy per environment / host. commited in project # packages/foo.yaml
service:
foo:
arguments: ['my_parameter']
parameters:
my_parameter: 'default plain text'
# or my_parameter: '%env(DEFAULT_PARAMETER)' only on production servers # packages/prod/foo.yaml
parameters:
my_parameter: '%env(secret:MY_PARAMETER)%' |
on other solution would be to configure the location of the keys and secrets per a new env variable instead of # packages/framework.yaml
framework:
secrets:
encrypted_secrets_dir: '%kernel.project_dir%/config/secrets/%env(SERVER)%/'
encryption_key: '%kernel.project_dir%/config/secrets/encryption_%env(SERVER)%.key'
parameters:
env(SERVER): developper-team and using the same service configuration for every environements # packages/foo.yaml
service:
foo:
arguments: ['%env(secret:FOO)%'] Then generating secrets for each
the The drawback is when creating a new "secret" you have to create 2 secrets: one per "SERVER"
|
@jderusse wouldn't it also be possible to configure the secret per environment? So you have something like # packages/framework.yaml
framework:
secrets:
my-secret: ~
services:
Foo\Bar:
$bla: %env(secret:my-secret)%
# packages/prod/framework.yaml
framework:
secrets:
my-secret:
storage: azure
# packages/dev/framework.yaml
framework:
secrets:
my-secret:
storage: inline
value: not-a-great-secret |
IMO new core parameter, like It would be more consistent, DX would be better in my opinion. |
i assume any of the possible edge-cases could be solved by updating a |
Link to secret management overview: https://gist.github.com/maxvt/bb49a6c7243163b8120625fc8ae3f3cd |
Hello there, Following @Tobion's comment and @mpdude's very interesting blog post, I published a sample implementation package as a proof-of-concept. My knowledge about storing secrets is quite poor (never used vaults or similar stuff) but it's a pretty fascinating topic, so I decided to work on this to improve my skills. If you want to have a look: Just one question for @Tobion: is a command to list secrets really relevant? Edit: just discovered the existence of #31101 - sorry for polluting the topic. |
Description
For simple architecture (without Vault or Amazon KMS, ...) the list of secrets have to be written down somewhere.
Could be file/env variables on the host server or in config files of deployment tool like ansible/puppet/docker/kubernetes...
The application end up with 2 repository to synchronize. In the worse case, the secrets are pushed along side the code (and sadly, many time in the parameters.yml itself)
The purpose of this RFC is to expose a simple way for developers to safely commits secrets and simplifying deployments.
Based on asymmetric encryption, a public key could be committed in the project which allow developers to generate new encrypted secrets.
Deployments, "just" have to deploy the private key
Nothing to synchronize anymore, adding/updating a secret does not requires to register this value of that secret in a dedicated tool.
Example
The text was updated successfully, but these errors were encountered: