Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Symfony finally swiped right on envvars

217 views

Published on

A presentation I gave on September 26 at the Melbourne Symfony developers group on using Environment Variables (envvars) in Symfony and managing secrets in your PHP applications.

For more information on these subjects, check out the supporting piece I wrote: https://samjarrett.com.au/swipe-right

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Symfony finally swiped right on envvars

  1. 1. Or,“why do we keep baking secrets into our artifacts?” Symfony finally swiped right on envvars
  2. 2. Or,“why do we keep baking secrets into our artifacts?” Symfony finally swiped right on envvars just a collection of cool gifs
  3. 3. Hi, I’m Sam ● Long time listener, first time caller. ● Co-organiser of this group. ● Using Symfony since 2.0 beta. ● Day job - ops @ REA. ● Docker & build automation fanboy. ● Have committed terrible atrocities with VCS and build systems in the past.
  4. 4. Symfony is not your grandma’s framework unique as a web framework
  5. 5. Most frameworks re-read config at boot. (Every. page. load.)
  6. 6. $ bin/elasticsearch ...waits 30 seconds... $ curl localhost:9200 [timeout] ...waits 30 more seconds... $ curl localhost:9200 [timeout]
  7. 7. Symfony compiles the DI container to PHP, including all config values.
  8. 8. Let’s talk about secrets, baby.
  9. 9. What is a secret?
  10. 10. ● Database credentials ● SMTP creds ● Payment gateway keys ● AWS credentials ● SMS tokens ● EDM service API keys Beyond 11 herbs and spices
  11. 11. The modern app has so many.. ● S3 bucket names ● SSH or deploy keys ● JWT keys or certificates ● Service n’s API key ● Other internal system hostnames But really, an application secret is anything you wouldn’t put directly in the HTML markup itself.
  12. 12. Here’s why we get a bad rep as php developers[1][2] … 1: I’ve seen all of these examples used 2: you probably have, too
  13. 13. <?php class Config { const LIVE_DATABASE_STRING = 'mysql://root:root@localhost:3306/pro const TEST_DATABASE_STRING = 'mysql://root:root@localhost:3306/tes public static function getDatabaseString() { // @TODO add magic to determine environment return self::LIVE_DATABASE_STRING; } // ...etc The know it all “pattern”
  14. 14. The build it & they’ll come “pattern” 1. put your prod & test values in a config file 2. run “make config” or some other magic build script 3. generate PHP from template: <?php class Config { const DATABASE_STRING = '{{ database_string }}'; public static function getDatabaseString() { return self::DATABASE_STRING;
  15. 15. dropping a prod database on a misconfigured test site. - my biggest professional fuck up. probably?
  16. 16. config_dev.yml: doctrine: dbal: url: 'mysql://root:root@localhost:3306/dev' The config.yml is the new Config class…. “pattern” config_prod.yml: doctrine: dbal: url: 'mysql://root:fk!ash#@localhost:3306/pr
  17. 17. Into today*... * as is common in many Symfony projects
  18. 18. Note: This is the best of the bad, but Symfony makes it insecure & hard to update. 1. define your config keys in a file called parameters.yml.dist, set sane defaults 2. store the actual configuration on S3 / build servers / CI / somewhere else 3. at build-time, copy the file in & build the container (cache-warm) The Fabien told me to “pattern”
  19. 19. OK. What’s wrong with this? How secure is your CI? Who has access to wherever the parameter values are stored?
  20. 20. OK. What’s wrong with this? Rolling the keys means re-deploying the application.
  21. 21. OK. What’s wrong with this? Secrets are written into the compiled Container class file. Try it yourself: $ grep “database.host” var/cache/prod/*.php
  22. 22. OK. What’s wrong with this? Creating new deployments is hard.
  23. 23. OK. What’s wrong with this? Production artifact differs to test.
  24. 24. OK, so what’s the answer?
  25. 25. Introducing environment variables ✅ Easy to set up in the app ✅ Functions just like another parameter ✅ Not compiled into the container ✅ Like other parameters, allows default values to be set ✅ Updating values is as simple as updating wherever it’s stored & reload PHP
  26. 26. Introducing environment variables ❌ Can’t access outside services - i.e. controllers etc - may require app refactoring ❌ Can be difficult to implement if you run your app outside of Docker ❌ Some libraries don’t quite support envvars yet (there’s a workaround though!)
  27. 27. Porting an existing app is easy.
  28. 28. parameters: env(DATABASE_URL): mysql://root:root@db:3306/symfony?charset= env(MAILER_HOST): smtp.sparkpostmail.com env(MAILER_PORT): 587 env(MAILER_USER): SMTP_Injection env(MAILER_PASS): env(SECRET): ThisTokenIsNotSoSecretChangeIt short_link_host: %env(SHORT_LINK_HOST)% env(SHORT_LINK_HOST): fallback.local Default values, get overridden if provided in the env How I dealt with libraries that don’t support env vars or values needed in controllers Note the structure around the name
  29. 29. doctrine: dbal: url: '%env(DATABASE_URL)%' orm: auto_generate_proxy_classes: '%kernel.debug%' naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: true swiftmailer: transport: "smtp" host: "%env(MAILER_HOST)%" port: "%env(MAILER_PORT)%" username: "%env(MAILER_USER)%" password: "%env(MAILER_PASS)%" spool: { type: "memory" } How they’re used - in configuration YAML files...
  30. 30. services: amazon_s3: class: AwsS3S3Client arguments: - credentials: key: "%env(AWS_ACCESS_KEY_ID)%" secret: "%env(AWS_ACCESS_SECRET_KEY)%" region: "%env(AWS_REGION)%" version: "2006-03-01" How they’re used - in service configuration files...
  31. 31. While you’re porting ● Consider what values actually change between deployment environments ● Anything that doesn’t change, should go into config.yml, config_dev.yml or config_prod.yml (a la current standard edition practice). ● Anything that changes is a good candidate to live in the environment.
  32. 32. Any gotchas? ● Need to defer making decisions on anything that changes per-deployment until later. i.e. any decisions made in a container extension. ● Dynamic parameters don’t solve this %param% => %env(PARAM)% ● If it’s your bundle/extension, you can use factory services to work around this.
  33. 33. The Dotenv component in 3.3 ● Symfony flex uses it out of the box! ● A small class that is added in your app/app_dev.php and console scripts ● Provides env-var like support in places you can’t easily set variables (dev!) ● In Flex, it replaces the parameters.yml paradigm ● Relatively easy to back-port into the full-stack framework
  34. 34. Or…
  35. 35. You can use docker in development!
  36. 36. It’s not (all) about security.
  37. 37. Environment vars are not perfect ● Does not solve security for anyone with access to the machine. ● Can still be accessed from outside your app - i.e. anything in shell_exec() calls. ● The PHP user or someone on CLI can expose to another program. ● Any insecure libraries in your app can access it, too. ● Can unintentionally turn up in logs, the Symfony profiler, etc. ● The plain-text values are probably still stored somewhere.
  38. 38. How do I make it better? ● Current PR: github/symfony/symfony#23901 (due 3.4) adds features to support docker/kubernetes secrets & make it extendable. ● Once it’s available to us, it might be like this… ○ on AWS? KMS-encrypted envvars or SSM Param. Store. ○ Not AWS? G-Cloud KMS, or other open-source, e.g. Hashicorp Vault, Pinterest Knox, Lyft Confidant. ● In the meantime: do you host on AWS? Use KMS-encrypted environment variables or fetch from SSM Param. Store at container boot.
  39. 39. ● Any of these principles a surprise? Read about 12-factor apps ● Segment have a great write-up on managing secrets on AWS on their blog. ● KMS-ed envvars (enc. strings): REA’s Shush is a tiny go library to help. ● SSM param. store (central management): Segment’s Chamber can help. ● Taking inspo from k8s, Docker swarm mode now has secrets built-in. ● Not using Docker? Can be quite complicated to set env across multiple users + daemons. Apache, and CLI users share a syntax, PHP-FPM is different. ● Example of porting a Symfony app to Docker - for the adventurous. Further reading & tools
  40. 40. thank you. stalky stalk: @sammyjarrett or linkedin.com/in/samjarrett more detail and these slides published at samjarrett.com.au/swipe-right

×