Using & Overriding Secrets

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

We have successfully added the SENTRY_DSN secret value to both the dev and prod vaults.

Listing the Secrets

How can I prove that? By running:

php bin/console secrets:list

Because we're in the dev environment, this reads the dev vault. There's our one secret. To see its value, add --reveal:

php bin/console secrets:list --reveal

Behind-the-scenes, that used the dev "decrypt" key to decrypt the value: it's an empty string. Ignore this "local value" thing for a minute.

We can do the same thing for the prod vault by passing --env=prod:

php bin/console secrets:list --env=prod

Including adding --reveal to see the value.

php bin/console secrets:list --env=prod --reveal

Reading Secrets in your App

Ok: because we're in the dev environment, the dev secret - the empty string - is the one that should be used. Refresh the page, check the dump, and expand it a few times. It's still using the production value.

Go back into config/packages/sentry.yaml:

sentry:
dsn: '%env(SENTRY_DSN)%'

We're still using the syntax for reading environment variables. How can we tell it to read the SENTRY_DSN secret instead? Surprise! To tell Symfony to read a SENTRY_DSN secret, we use the exact same syntax.

Environment Variables vs Secrets

Let me explain: when Symfony sees the %env()% syntax, it first looks to see if an environment variable called SENTRY_DSN exists. If it does, it uses it. If there is not, it then looks for a secret in the vault called SENTRY_DSN. So reading environment variables and secrets uses the same syntax, but environment variables take priority.

This means one important thing: when you identify an environment variable that you want to convert into a secret, you need to remove it entirely as an environment variable. Set a value as an environment variable or a secret, but not both. Delete the SENTRY_DSN entry from .env and .env.local:

26 lines .env
... lines 1 - 22
###> sentry/sentry-symfony ###
SENTRY_DSN=
###

2 lines .env.local
SENTRY_DSN=https://7f45741877f3498eab0ae2bee6463d57@o372370.ingest.sentry.io/5186941

Now Symfony should be read from our dev vault. Refresh... expand the object and... yes! All the values are null! It works!

Let's try out production. Until now, to switch to the prod environment, I've been updating the .env file:

26 lines .env
... lines 1 - 15
###> symfony/framework-bundle ###
APP_ENV=dev
... lines 18 - 20
###
... lines 22 - 26

But now that we understand .env.local, let's add APP_ENV=prod there instead:

2 lines .env.local
APP_ENV=prod

Next, clear your cache:

php bin/console cache:clear

Then spin back to your browser and refresh. This time the dump is on top. If I expand it... yes! It's using the production values. Booya! That works because my project has the prod decrypt key. If that was not there, we would get an error.

Go ahead and take out the APP_ENV= line in .env.local to get back to the dev environment:

2 lines .env.local
APP_ENV=prod

And in QuestionController, let's cleanup: remove the dump(), the new Exception and the HubInterface argument:

... lines 1 - 12
class QuestionController extends AbstractController
{
... lines 15 - 42
public function show($slug, MarkdownHelper $markdownHelper)
{
if ($this->isDebug) {
$this->logger->info('We are in debug mode!');
}
$answers = [
'Make sure your cat is sitting `purrrfectly` still 🤣',
'Honestly, I like furry shoes better than MY cat',
'Maybe... try saying the spell backwards?',
];
... lines 54 - 62
}
}

After this... things are working again.

Overriding Secrets Locally

You are now ready to use Symfony's secrets system. But! The fact that environment variables take precedent over secrets is something that we can use to our advantage.

Find your terminal and run:

php bin/console secrets:list --reveal

In the dev environment, the SENTRY_DSN value is set to an empty string. Let's pretend that, while developing, I want to temporarily set SENTRY_DSN to a real value so I can test that integration.

We could use secrets:set to override the value... but that would update the secrets file... and then we would have to be super careful to avoid committing that change.

There's a better way. In .env.local, set SENTRY_DSN to the real value. Well, I'll put "FOO" here so it's obvious when this value is being used.

Now run that command again:

php bin/console secrets:list --reveal

The "Value" is still empty quotes, but now it has a "Local Value" set to the string we just used! The "Local Value" is the one that will be used. Why? Because our new environment variable overrides the secret: environment variables always win over secrets. This "Local Value" is a fancy way of saying that.

I'll take that value out of .env.local so that my secret is once again used.

Next: let's have some fun! We're going to install MakerBundle and start generating some code!

Leave a comment!

  • 2020-07-28 Joao Andrade

    No, it wasn't cache. Cleared it alright

  • 2020-07-28 Diego Aguiar

    Hey Joao Andrade

    That's weird. Perhaps it was due to the cache? Who knows :)

  • 2020-07-28 Joao Andrade

    It started working, I'm not sure why, but the only thing I think changed was that I restarted the Symfony server.

  • 2020-07-28 Joao Andrade

    Hi, I'm getting:

    Environment variable not found: "SENTRY_DSN".

    But the secret is configured as below:


    $ bin/console secrets:list --reveal --env=dev

    // Use "%env(<name>)%" to reference a secret in a config file.

    ------------ ------- -------------
    Secret Value Local Value
    ------------ ------- -------------
    SENTRY_DSN ""
    ------------ ------- -------------

    And my sentry.yaml file show:


    sentry:
    dsn: "%env(SENTRY_DSN)%"

    I've delete the SENTRY_DSN lines from .env and .env.local

  • 2020-07-13 Marwâne Chahed

    Great! Thanks weaverryan

  • 2020-07-13 weaverryan

    Hey @Beam OP!

    I know this :). You have found a Symfony bug! https://symfonycasts.com/sc...

    It is fixed in Symfony 5.1.3, but that hasn't been released yet - I expect that to happen any day now - it's been 28 days since the last bug release.

    Cheers!

  • 2020-07-13 Marwâne Chahed

    I'm getting the following error when trying to reveal secrets in prod environment:

    PHP Fatal error: Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException: Circular reference detected for parameter "env(base64:default::SYMFONY_DECRYPTION_SECRET)" ("env(base64:default::SYMFONY_DECRYPTION_SECRET)" > "env(default::SYMFONY_DECRYPTION_SECRET)" > "env(SYMFONY_DECRYPTION_SECRET)" > "env(base64:default::SYMFONY_DECRYPTION_SECRET)"). in cauldron-overflow\vendor\symfony\dependency-injection\Container.php:389

    I used this command: php bin/console secrets:list --env=prod --reveal, Symfony version: 5.1.2

    Any ideas?

  • 2020-06-22 Victor Bocharsky

    Hey Michał,

    If you use Git - try to commit changes and run:

    $ git grep dsn

    This command will show you all the spots where you use dsn. Make sure that all dsn options are set properly in your project. Also, keep eyes closer where those values should come from, probably you need to set up an env var in your .env or .env.local files? Or probably specifically in .env.prod in your case?

    Cheers!

  • 2020-06-19 Michał Śnieżko

    I am getting the following error in logs when switching to prod:

    [2020-06-19T16:54:53.389249+00:00] request.CRITICAL: Uncaught PHP Exception Symfony\Component\OptionsResolver\Exception\InvalidOptionsException: "The option "dsn" with value "" is invalid." at C:\Users\Michael\code\code-symfony-fundamentals\start\vendor\symfony\options-resolver\OptionsResolver.php line 987 {"exception":"[object] (Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException(code: 0): The option \"dsn\" with value \"\u0016\" is invalid. at C:\\Users\\Michael\\code\\code-symfony-fundamentals\\start\\vendor\\symfony\\options-resolver\\OptionsResolver.php:987)"} []
  • 2020-05-25 Diego Aguiar

    Dang! what a sneaky error. I'm glad you figured it out. FYI what you put on the .env.<environment_name> will override any other value you already have

    Cheers!

  • 2020-05-25 Egor Ushakov

    OMG! Shame on me! I've just forgot to comment out env vars in .env file. I use an empty env vars in .env just not to forget to set them in .env.<env_name> file. Thus empty values in .env just override secrets. And only decrypting secrets with `secrets:decrypt-to-local` writes .env.<env_name>.local which in its turn override empty values in `.env`.

  • 2020-05-25 Egor Ushakov

    Event setting SYMFONY_DECRYPTION_SECRET environment variable gives no help...

  • 2020-05-25 Egor Ushakov

    The problem is that the secrets are NOT read for some reason. They are successfully displayed with `./bin/console secrets:list --env=demo --reveal` but do not work until decrypting into `.env.demo.local`. `debug:config framework secrets` confirms that secrets are enabled. Some googling showed that there might be a problem with sodium extension but I have one both on dev and demo machine/environments.

  • 2020-05-22 Egor Ushakov

    Hey! Doesn't secrets work for console commands? Something weird is happening with my project. When I ran ./bin/console doctrine:database:drop it returns

    Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.

    I set a breakpoint at exception line and found out that `$params` local variable does not contain data for my PostgreSQL connection: at least $params["driver"] = pdo_mysql . I faced with the issue after deploying my whole Docker Compose stack to remote machine through Docker Machine. I set all the demo env var through secrets intentionally. After deploying I decided to re-create database and started with doctrine:database:drop and got that error. Trying to execute doctrine:migrations:migrate shows $dsn == mysql:host=localhost;dbname=;charset=utf8mb4; Nonsense! To be honest I just lost debugging call stack to find out why the app takes default env values instead of stored in secrets. Any ideas?