8000 RFC - envProcessor to decrypt secrets for simple applications · Issue #27351 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

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

Closed
jderusse opened this issue May 23, 2018 · 48 comments · Fixed by #33997
Closed

RFC - envProcessor to decrypt secrets for simple applications #27351

jderusse opened this issue May 23, 2018 · 48 comments · Fixed by #33997
Labels
DependencyInjection RFC RFC = Request For Comments (proposals about features that you want to be discussed)
Milestone

Comments

@jderusse
Copy link
Member

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

$ ./bin/console secret:encrypt "foo bar"
MIIBoQYJKoZIhvcNAQcDoIIBkjCCAY4CAQAxg...
# services.yaml
parameters:
  default_jwk: '%env(json:APP_JWK)%' # This variable contains the private key used to decrypt secrets
  # default_jwk: '%env(json:file:APP_JWK_FILE)%' # with APP_JWK_FILE=/opt/application/.jwk.json

# config/packages/prod/doctrine.yaml
parameters:
  env(DATABASE_PASSWORD): 'MIIBoQYJKoZIhvcNAQcDoIIBkjCCAY4CAQAxg...'
  database_password: '%env(decrypt:default_jwk:DATABASE_PASSWORD)%'
@stof
Copy link
Member
stof commented May 23, 2018

the main reason for the %env()% syntax is that these special parameters are resolved at runtime without needing a cache clearing (allowing to change them at runtime easily). If all the settings are committed in the repo anyway (requiring a deployment to change them), I'm not sure the env processor is the right way to handle this. use case.

@xabbuh xabbuh added DependencyInjection RFC RFC = Request For Comments (proposals about features that you want to be discussed) labels May 23, 2018
@nicolas-grekas
Copy link
Member

Following @stof's commend, does this have to be an env processor?
Can't we make a command that would be used when setting up the prod env, and that would decrypt+export the secrets (in a file) so that they could be consumed as real env vars using the "file:" processor?

@jderusse
Copy link
Member Author

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.

@jderusse
Copy link
Member Author

Thinking about the @nicolas-grekas comment.

When a developer want to add a new secrets:

$ ./bin/console secret:encrypt-string "my secret" --name bar 
$ ./bin/console secret:encrypt-file /path/to/file --name foo --bucket prod1

This command generate a new file (uses https://tools.ietf.org/html/rfc7516)

$ cat ./app/config/secrets/prod1/foo.jwe
{
    "header": "eyJhbGciOiAiUlNBLU9BRVAiLCAiZW5jIjogIkExMjhDQkMtSFMyNTYifQ",
    "cek": "SsLgP2bNKYDYGzHvLYY7rsVEBHSms6_jW...",
    "iv": "Awelp3ryBVpdFhRckQ-KKw",
    "ciphertext": "1MyZ-3nky1EFO4UgTB-9C2EHpYh1Z-ij0RbiuuMez70nIH7uqL9hlhskutO0oPjqdpmNc9glSmO9pheMH2DVag",
    "tag": "Xccck85XZMvG-fAJ6oDnAw"
}

In order to use it in production, developer have to decrypt secrets first

$ ./bin/console secret:decrypt --key /path/to/key --bucket prod1

This command dump the revealed secrets

$ cat ./var/cache/secrets.json
{
    "foo": "raw content of original file"
}

Application can use it

parameters:
  %env(APP_SECRETS)%: %kernel.cache_dir%/secrets.json
  bar: %env(key:foo:json:file:APP_SECRETS)%

How to configure several environment? for instance (preprod vs prod or prod-eu vs prod-us) ?
=> the encrypt and decrypt command expose a "--bucket" parameter (default value is "default").
Encrypted file will be stored in the "name of bucket" folder.

How to not break dev environment?
=> developpers could configure a dedicated "dist" bucket without encryption key which would generate plain text secrets files

Going further with semantic configuration

  secrets:
    default:
      key:
        public: %kernel.config_dir%/secrets/keys/{bucket_name}.jwk.json
        private: /opt/application/secret-key.jpsk.json
      encrypted_folder: %kernel.config_dir%/secrets/buckets/{bucket_name}/
    buckets:
      default:
        key: false
        encrypted_folder: %kernel.config_dir%/secrets/buckets/dist/
      prod1:
        key:
            public: %kernel.config_dir%/secrets/keys/prod1
            private: /opt/application/secret-key
        encrypted_folder: %kernel.config_dir%/secrets/buckets/prod1/
      prod2: ~

Comming to the conclusion that it could be done in a dedicated bundle...

@javiereguiluz
Copy link
Member

I'm sharing some resources about how other frameworks have solved this very same problem, so we can take ideas from them:

@javiereguiluz javiereguiluz added this to the next milestone May 24, 2018
@tvlooy
Copy link
Contributor
tvlooy commented May 24, 2018

@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?

@nicolas-grekas
Copy link
Member
nicolas-grekas commented May 25, 2018

If someone can read the file you lose your credentials

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 :)

@tvlooy
Copy link
Contributor
tvlooy commented May 25, 2018

+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.

@jderusse
Copy link
Member Author
jderusse commented Jun 27, 2018

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...

@nayzo
Copy link
Contributor
nayzo commented Jun 28, 2018

@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 !!
Which to me is way overhead then a simple c:c command!

Or I'm missing something here ?

@keichinger
Copy link
Contributor

@nayzo for our 4.x projects we're copying the environment variables all over the place, which is madness compared to the old parameters.yaml approach:

  • We pass down environment variables to Symfony via Nginx's fastcgi_param APP_ENV "prod";
  • We explicitly load a revived config/parameters.yaml for console commands
  • We explicitly declare environment variables in our own custom deploy.sh script where you literally do a export APP_ENV="prod", which is only exported and and available during the script execution

We basically never touch /etc/environment or any of the ~/.bashrc, ~/.bash_profile scripts, unless it's something we configure globally (like private NPM or private Packagist related things) that affects all projects that run on the server.

I can't remember what exactly about .env wasn't working for us, but it was painful when we were evaluating the 4.0 betas, which is why we're still doing the above madness.

@nayzo
Copy link
Contributor
nayzo commented Jun 29, 2018

@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 APP_ENV (prod/test/dev) for instance. etc..
IMO, to manage multiple projects on the same server with env vars you have to use Docker or similar approach so that you can be sure no env vars collision could take place.
And this is an other downfall of the env vars.

@keichinger
Copy link
Contributor

That is odd to me since you can get some env vars collision between projects like database credentials or APP_ENV (prod/test/dev) for instance. etc..

Each project is running in its own PHP-FPM pool (e.g. projectA runs on fpm-port 9000 while projectB runs on 9001), so we have a perfect isolation between projects since Nginx and PHP-FPM won't leak them in any way.

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 :)

@cryptiklemur
Copy link
Contributor
cryptiklemur commented Mar 30, 2019

IMO secrets could benefit from their own "processor", like the env var processor. Being able to do %secret(foo:path/to/secret)% and %secret(foo:path/to/secret/map:indexOnSecret)% seems more appropriate than calling them "environment variables", which they arent necessarily.

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:

%secret(foo:PATH_ENV_VAR)% and %secret(foo:PATH_ENV_VAR:indexOnSecret)% respectively

@sstok
Copy link
Contributor
sstok commented Mar 30, 2019

I'm a bit late to this discussion, but I'm going to repeat something really important here.
Don't roll your own encryption!

PHP 7.2 comes with (Lib)sodium by default which is considered the most secure solution as to day.
Alternatively you can use Halite (MPLv 2.0 licensed) for even better security but given this is done at runtime for almost each request I'm not sure if this is to heavy and a lighter solution is acceptable.

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?

@jderusse
Copy link
Member Author

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.

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)

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?

@Tobion
Copy link
Contributor
Tobion commented Apr 5, 2019

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:

  • Developers just configure the encrypted secrets using the config like the following
secrets_encryption:
    encoding: base64
    encryption_key: '%env(SECRETS_ENCRYPTION_KEY)%'
    encrypted_secrets:
        database_password_prod: '<encrypted secret>'
        rabbit_mq_password_prod: '<encrypted secret>'
        redis_password_prod: '<encrypted secret>'

        database_password_test: '<encrypted secret>'
        rabbit_mq_password_test: '<encrypted secret>'
        redis_password_test: '<encrypted secret>'
  • The bundle then decrypts these secrets at runtime using the encryption_key (that MUST NOT be commited) and exposes those secrets as DI parameters that can be used like people are used to, e.g.
framework:
    cache:
        app: cache.adapter.redis
        default_redis_provider: 'redis://%redis_password_prod%@example.org'
  • There are also commands to encrypt/decrypt/list secrets and to generate a new encryption key to make to easy to work with the secrets.

@cryptiklemur
Copy link
Contributor
cryptiklemur commented Apr 5, 2019

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 php repo...)

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)

@Tobion
Copy link
Contributor
Tobion commented Apr 6, 2019

I want to work on this at the EUFOSSA Hackathon. You can find me at the main entrance at the big table.

@mpdude
Copy link
Contributor
mpdude commented Apr 6, 2019

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

@sstok
Copy link
Contributor
sstok commented Apr 6, 2019

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.

@nayzo
Copy link
Contributor
nayzo commented Apr 6, 2019

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:

  • Compatible Symfony version 2, 3 & 4
  • Url Data & parameters Encryption
  • Url Data & parameters Decryption
  • Data Encryption & Decryption
  • Access from Twig by ease
  • Flexible configuration
  • Compatible php version 5 & 7
  • Uses OpenSSL extension

@sstok
Copy link
Contributor
sstok commented Apr 6, 2019

@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!

@Tobion
Copy link
Contributor
Tobion commented Apr 6, 2019

Let's start by defining our goals:

  • Keep secrets in a file committed to the repository
  • Those secrets must be encrypted
  • The injection of the decrypted secrets must be at runtime (not at container compilation time). This allows people to compile/warmup the cache in an untrusted environment like CI server that will not expose the decrypted secrets in anyway. And only later when starting the app, have the decrypted secrets available.
  • Secret storages/providers should be configurable per secret so that we can implement different backends like Vault, AWS. But the primary goal for now is store secrets encrypted locally. Having configurable strategies allows to migrate to a different backend without changing the process.
  • There should commands to add/decrypt/list secrets.

@nayzo
Copy link
Contributor
nayzo commented Apr 6, 2019

@sstok No the bundle is for encryption & decryption any sort of data including ENV vars 😆
But sure i will take your suggestions into consideration to make it better 👍

@mpdude
Copy link
Contributor
mpdude commented Apr 6, 2019

Here's what @jderusse, @Tobion and me agreed upon after some discussion at FOSSHackathon:

  1. We'd like to be open for different or future "Secrets Provider" (or "backend"? or "storage"?) that can be added or plugged in at a later time. Each one has a name and its individual configuration, depending on the particular implementation.

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
  1. We'd like to use an env_var_processor for now to resolve the secrets. That means syntax will be something like %env(secrets:provider_name:path)%, where provider_name refers to one of the configured providers above, and path is something to identify/qualify the secret value requested. The path semantics are probably going to be provider-specific.

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 path approach work out for all providers? Are we syntactically limited within %env(...)%?

  1. Command to list, add, remove or change secrets

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).

@Tobion
Copy link
Contributor
Tobion commented Apr 6, 2019

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
With rails 6 they introduced environment specific credentials.

@javiereguiluz
Copy link
Member

It would be great if you could show a full realistic example of the workflow you propose to work with secrets. For example:

  1. How can a developer uses this feature to encrypt the database password? Please, show the actual command to execute, etc.
  2. How's the deployment process when using secrets? Please, explain things in detail.
  3. How's the common need of updating the secrets value from time to time?

Thanks!

@cryptiklemur
Copy link
Contributor
cryptiklemur commented Apr 6, 2019

Imo, this should separate logic between a bundle and a component too.

@jderusse
Copy link
Member Author
jderusse commented Apr 6, 2019

How can a developer uses this feature to encrypt the database password? Please, show the actual command to execute, etc.

Theire are 2 way to handle it:

  • Encrypt the DATABASE_URL
  • Or encrypt only the DATABASE_PASSWORD and use env(resolve:PLAIN_TEXT_DATABASE_URL)
$ compose req symfony/secrets
=> install composant
=> generate public/private keys
=> update to .gitignore to add the private key path. 

$ ./bin/console secrets:generate SECRET_DATABASE_PASSWORD this_is_secret
$ ./bin/console secrets:generate SECRET_REDIS_URL redis://user:this_is_secret@server.com

This command will generate a file in ./config/secrets/DATABASE_PASSWORD.bin

Then user user should update his config file:

framework:
  cache:
    default_redis: '%env(secret:SECRET_REDIS_URL)%'
doctrine:
  dbal:
    url: '%env(resolve:DATABASE_URL)%'
parameters:
  env(DATABASE_URL): mysql://admin:%env(secret:SECRET_DATABASE_PASSWORD)%@server.com

How's the deployment process when using secrets? Please, explain things in detail.

first of all: The material to decrypt secrets should be available on every servers runing the application.
It's up to the developper/ops to upload that private on those server.

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) ./bin/console secret:warmup to warm the secret's cache

How's the common need of updating the secrets value from time to time?

When calling the method ./bin/console secrets:generate SECRET_DATABASE_PASSWORD a_new_secret a new .bin file is generated.
Users have to commit it and deploy it.

@mpdude
Copy link
Contributor
mpdude commented Apr 6, 2019

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

@mpdude
Copy link
Contributor
mpdude commented Apr 6, 2019

@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 😉🍻.)

@javiereguiluz
Copy link
Member

@jderusse fantastic explanation! Thanks.

I only have a minor question about this phrase:

That's all. Symfony would be able decrypt secrets, at runtime, when required (on the fly), and use cache to optimize computation.

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?

@jderusse
Copy link
Member Author
jderusse commented Apr 7, 2019

yes, that's what we mean.

If attackers are able to read the cache folder, indeed, they are able to read the clear value.
BUT if they can do that, they can also able to read the master.key and be able to decrypt the files by their own, so what can we do?
To enforce security (but low the performances) users may inject an arrayAdapter.

Do you think about a specific security issue.

@Wirone
Copy link
Contributor
Wirone commented Apr 8, 2019

@Tobion In your comment is there one encryption key for all secrets, regardles if they are for test or prod? It's not acceptable IMO. In our company developers don't have access to production, all deploys are done with admins team (dev teams have to prepare installers, deploy instructions etc.), so developers don't / can't know production secrets or keys required to decrypt them.

At first I didn't get what's the point of encrypting secrets and store them in repo instead of using %env()%. But after reading discussion I see the pros of it, when you deploy decryption key only once and all further deploys/releases use it to decrypt any data stored in the app's config. Yeah, keeping ENV vars in sync with code can be tough (but it's possible).

Some of our dev teams keep Ansible playbooks for generating config from commited ansible-vault files in CD process, passing decryption password from Gitlab CI as protected variable (so only managers with permissions to protected branches can trigger build). This RFC would cover this case I think (all required secrets are ready in repo), but without Ansible, am I right?

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.

@dkarlovi
Copy link
Contributor
dkarlovi commented Apr 8, 2019

Yes, the use case where the dev team never gets access to the prod secrets to commit them should be taken into account.

@nicolas-grekas
Copy link
Member

It is already: configuration is Symfony-env dependent - and so are env vars.

@dkarlovi
Copy link
Contributor
dkarlovi commented Apr 8, 2019

@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.

@nicolas-grekas
Copy link
Member

Oh, sure. We discussed about defaulting to storing secrets in config/secrets/%kernel.environment%/, for the reason that has been highlighted.

@Wirone
Copy link
Contributor
Wirone commented Apr 9, 2019

@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 prod).

Right now we work on dev, but when we want to locally test how application behaves on production (different error handling, Sentry) we change APP_ENV to prod. If real production secrets were encrypted and stored in the app, then it would require additional hacks from developers to run app in prod environment (override commited file with different one, with known decryption key).

Or am I missing something?

@nicolas-grekas
Copy link
Member

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.

@jderusse
Copy link
Member Author
jderusse commented Apr 9, 2019

@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)%'

@jderusse
Copy link
Member Author
jderusse commented Apr 9, 2019

on other solution would be to configure the location of the keys and secrets per a new env variable instead of kernel.environement

# 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 SERVER:

config/secrets/
├── encryption_developper-team.key
├── encryption_production.key
├── developper-team
│   └── FOO.bin
└── production
    └── FOO.bin

the encryption_developper-team.key could be commited (or shared to developpers / ci), and the encryption_production.key should be manually deployed on targeted server.

The drawback is when creating a new "secret" you have to create 2 secrets: one per "SERVER"

$ SERVER=developper-team ./bin/console secret:add FOO not-really-a-secret
$ SERVER=production ./bin/console secret:add FOO strong-secret

@dkarlovi
Copy link
Contributor
dkarlovi commented Apr 9, 2019

@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

@Wirone
Copy link
Contributor
Wirone commented Apr 9, 2019

IMO new core parameter, like kernel.deployment_target, would be better than kernel.environment because it would allow to use the same configuration values (actually: secret read with special syntax) regardless of APP_ENV. Developers would use own deployment target (known decryption key, the same values for dev and prod environment) while for other instances like development, stage or production this deployment target would be different, reading different secrets (using decryption keys from CI/CD process or manual deploy instructions, not known by developers).

It would be more consistent, DX would be better in my opinion.

@cryptiklemur
Copy link
Contributor

i assume any of the possible edge-cases could be solved by updating a kernel.secrets_dir parameter, to have them stored wherever you would like

@Tobion
Copy link
Contributor
Tobion commented May 28, 2019

Link to secret management overview: https://gist.github.com/maxvt/bb49a6c7243163b8120625fc8ae3f3cd

@bpolaszek
Copy link
Contributor
bpolaszek commented Jun 27, 2019

Let's start by defining our goals:

  • Keep secrets in a file committed to the repository
  • Those secrets must be encrypted
  • The injection of the decrypted secrets must be at runtime (not at container compilation time). This allows people to compile/warmup the cache in an untrusted environment like CI server that will not expose the decrypted secrets in anyway. And only later when starting the app, have the decrypted secrets available.
  • Secret storages/providers should be configurable per secret so that we can implement different backends like Vault, AWS. But the primary goal for now is store secrets encrypted locally. Having configurable strategies allows to migrate to a different backend without changing the process.
  • There should commands to add/decrypt/list secrets.

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:
https://github.com/bpolaszek/shh

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DependencyInjection RFC RFC = Request For Comments (proposals about features that you want to be discussed)
Projects
None yet
0