Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Configuring Symfony - from localhost to High Availability

Configuring Symfony - from localhost to High Availability

All apps need configuration: the database host, background color of your client's theme, API token to some service, number of items per page on the blog, etc. Where should all this configuration live? Operating systems, PHP and Symfony provide many options: environment variables, .env files, plain PHP constants, dependency injection parameters, key-value databases, "secrets" vaults, etc. You have many choices! And with great choices comes great responsibility... and complexity!

In this talk, we'll review all the ways to store configuration, which is best for each "type" of configuration and the benefits and drawbacks of each. We'll also talk about Symfony 4.4's new "secrets vault" as one of the ways to store sensitive settings. By the end, you'll be ready to configure anything from localhost up to a cluster of high resiliency microservices.

Nicolas Grekas

November 21, 2019
Tweet

More Decks by Nicolas Grekas

Other Decks in Programming

Transcript

  1. Configuring Symfony
    from localhost to High Availability
    #SymfonyCon
    @nicolasgrekas

    View Slide

  2. @nicolasgrekas
    @nicolasgrekas
    https://symfony.com/the-fast-track

    View Slide

  3. View Slide

  4. @nicolasgrekas
    Configuring a Symfony app
    •DI parameters and PHP constants
    •Environment variables
    •Secrets
    •(High Availability)

    View Slide

  5. What is a Symfony
    Application?
    @nicolasgrekas

    View Slide

  6. A Symfony app
    Uses a compiled
    container
    @nicolasgrekas

    View Slide

  7. View Slide

  8. @nicolasgrekas
    config/services.yaml
    # Put parameters here that don't need to change on each machine where the app is deployed
    # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
    parameters:
    locale: 'en'
    # This parameter defines the codes of the locales (languages) enabled in the application
    app_locales: en|fr|de|es|cs|nl|ru|uk|ro|pt_BR|pl|it|ja|id|ca|sl|hr|zh_CN|bg|tr|lt
    app.notifications.email_sender: [email protected]

    View Slide

  9. @nicolasgrekas
    /**
    * Gets the private 'App\Command\ListUsersCommand' shared autowired service.
    *
    * @return \App\Command\ListUsersCommand
    */
    protected function getListUsersCommandService()
    {
    $this->privates['App\\Command\\ListUsersCommand'] = $instance = new \App\Command\ListU
    ($this->services['swiftmailer.mailer.default'] ?? $this->getSwiftmailer_Mailer_Def
    '[email protected]',
    ($this->privates['App\\Repository\\UserRepository'] ?? $this->getUserRepositorySer
    );
    $instance->setName('app:list-users');
    return $instance;
    }
    var/cache/…/srcApp...Container.php

    View Slide

  10. @nicolasgrekas
    var/cache/…/srcApp...Container.php
    /**
    * Gets the private 'App\EventSubscriber\CommentNotificationSubscriber' shared autowired s
    *
    * @return \App\EventSubscriber\CommentNotificationSubscriber
    */
    protected function getCommentNotificationSubscriberService()
    {
    return $this->privates['App\\EventSubscriber\\CommentNotificationSubscriber'] = new \A
    ($this->services['swiftmailer.mailer.default'] ?? $this->getSwiftmailer_Mailer_Def
    ($this->services['router'] ?? $this->getRouterService()),
    ($this->services['translator'] ?? $this->getTranslatorService()),
    '[email protected]'
    );
    }

    View Slide

  11. @nicolasgrekas
    Parameters are static!
    •Updates require recompilation!
    •Available at built time
    •Cluster-wide settings
    Fit configuring client requirements!

    View Slide

  12. @nicolasgrekas
    Parameter examples
    •Locale(s)
    •Facebook app id
    •Google Analytics tracker id
    •framework.cache.prefix_seed
    Anything commitable (this excludes secrets!)

    View Slide

  13. Best Practice
    Define business-
    related settings in
    config/services.yaml

    View Slide

  14. Best Practice
    Define rarely
    changing settings in
    PHP constants

    View Slide

  15. View Slide

  16. A Symfony app
    Can be configured
    dynamically via env vars
    @nicolasgrekas

    View Slide

  17. @nicolasgrekas
    config/packages/mailer.yaml
    framework:
    mailer:
    dsn: '%env(MAILER_DSN)%'

    View Slide

  18. @nicolasgrekas
    config/packages/doctrine.yaml
    doctrine:
    dbal:
    url: '%env(resolve:DATABASE_URL)%'

    View Slide

  19. @nicolasgrekas
    var/cache/…/srcApp...Container.php
    /**
    * Gets the public 'doctrine.dbal.default_connection' shared service.
    *
    * @return \Doctrine\DBAL\Connection
    */
    protected function getDoctrine_Dbal_DefaultConnectionService()
    {
    // ...
    return $this->services['doctrine.dbal.default_connection'] =
    (new \Doctrine\Bundle\DoctrineBundle\ConnectionFactory([]))
    ->createConnection([
    'driver' => 'pdo_sqlite',
    'charset' => 'utf8mb4',
    'url' => $this->getEnv('resolve:DATABASE_URL'),
    'host' => 'localhost',
    'port' => NULL,
    'user' => 'root',
    'password' => NULL,
    'driverOptions' => [],
    'serverVersion' => '3.15',

    View Slide

  20. @nicolasgrekas
    vendor/dependency-injection/…
    if (isset($_ENV[$name])) {
    $env = $_ENV[$name];
    } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
    $env = $_SERVER[$name];
    } elseif (false === ($env = getenv($name)) || null === $env) {
    // ...
    Check httpoxy.org
    getenv() is not thread safe
    variable_order=EGPCS
    Access to fastcgi_params, env vars, etc.

    View Slide

  21. @nicolasgrekas
    Environment Variables

    View Slide

  22. @nicolasgrekas

    View Slide

  23. @nicolasgrekas
    $ SYMFONY='<3' php -S localhost:8000

    View Slide

  24. @nicolasgrekas
    Anything coming from the runtime environment
    •OS-level env vars
    •SAPI-level env vars – e.g. FastCGI parameters
    •.env files
    •File contents
    •FUSE mount points
    •Key/value services – Consul / DNS

    View Slide

  25. @nicolasgrekas
    Reconfiguring the runtime environment?
    •Per-process, immutable: restart
    •Per-vhost/URL: reload the server
    •Committed: redeploy
    •Files : reupload
    •Virtual filesystem: live updates
    •Network service: live updates

    View Slide

  26. @nicolasgrekas
    Environment variables
    •Don’t require recompiling the app
    •Can be updated live
    •Shouldn’t be used at built time
    •Can vary by deployment target
    Fit configuring hosting requirements!

    View Slide

  27. Best Practice
    Define infrastructure-
    related settings
    in env vars

    View Slide

  28. But how should we
    configure env vars?!
    @nicolasgrekas

    View Slide

  29. @nicolasgrekas
    Delegated to devs: for mostly static parts
    •Commit defaults in .env
    •rsync .env.local
    composer dump-env prod
    (creates .env.local.php)

    View Slide

  30. @nicolasgrekas
    Delegated to ops: enables auto-scaling
    •Ansile, chef, Dockerfile, etc.
    •Server vhosts
    •The web-UI of your hoster
    •Don’t forget about the CLI!
    symfony var:set FOO=bar

    View Slide

  31. @nicolasgrekas
    Delegated to bots: unlocks high availability!
    •File rsync
    •Virtual filesystems
    •Key/value stores
    •Consul <3 (Consul Template too)
    Next: health checks, routing layer hot-
    reconfiguration with istio/envoy/traeffic/etc.

    View Slide

  32. Best Practice
    Document
    all env vars
    in .env

    View Slide

  33. @nicolasgrekas
    class ConsulEnvVarLoader implements EnvVarLoaderInterface
    {
    // ...
    public function loadEnvVars(): array
    {
    // $this->consulUrl = 'http://127.0.0.1:8500/v1/kv/website-config';
    $consulVars = $this->httpClient
    ->request('GET', $this->consulUrl)
    ->toArray()[0]['value'];
    return json_decode(base64_decode($consulVars), true);
    }
    }

    View Slide

  34. View Slide

  35. Best Practice
    Increase complexity
    only when needed!

    View Slide

  36. What about secrets?
    @nicolasgrekas

    View Slide

  37. @nicolasgrekas

    View Slide

  38. @nicolasgrekas
    $ SYMFONY='<3' php -S localhost:8000

    View Slide

  39. Fact: .env.local.php
    is more secure than
    real env vars
    @nicolasgrekas

    View Slide

  40. @nicolasgrekas
    Security threats – entropy
    •Remote access
    •Insecure deployment network
    •Shared hosting
    •Bad document root / php.ini
    •Employees leaving
    •Technical responsibilities in the wrong hands

    View Slide

  41. @nicolasgrekas
    Security threats mitigation
    •Rotate secrets
    •Store and deploy secrets encrypted
    •Use .env.local.php
    •Use asymmetric cryptography
    •Make things convenient

    View Slide

  42. @nicolasgrekas
    Secrets management tools
    •Pet .env.local file deployed via SSH
    •bin/console secrets:*
    •(Consul|Docker|Kubernetes) Vault

    View Slide

  43. @nicolasgrekas

    View Slide

  44. @nicolasgrekas

    View Slide

  45. @nicolasgrekas
    $ cat config/secrets/dev/dev.GITHUB_API_TOKEN.aebb6a.php
    return "d\x9B\x5D2\xABV\x0B\xE6\xB1\xC2\x25\xCB\x2B\xDAkA\xE4\xC7\x9Cu\x

    View Slide

  46. @nicolasgrekas

    View Slide

  47. @nicolasgrekas
    class SodiumVault implements EnvVarLoaderInterface
    public function loadEnvVars(): array
    {
    return $this->list(true);
    }

    View Slide

  48. @nicolasgrekas
    Configure secrets
    framework:
    secrets:
    vault_directory: '%kernel.project_dir%/config/secrets/%kernel.environment%'
    local_dotenv_file: '%kernel.project_dir%/.env.local'
    decryption_env_var: 'base64:default::SYMFONY_DECRYPTION_SECRET'
    export SYMFONY_DECRYPTION_SECRET=$(php -r 'echo base64_encode( \
    require "config/secrets/prod/prod.decrypt.private.php" \
    );')

    View Slide

  49. @nicolasgrekas
    Deploy secrets
    •Same workflow as code – git blame <3
    •But the decryption key
    •Share the keys with the appropriate persons
    •Rotation is yours to build
    •Decrypt live – or at build time
    bin/console secrets:decrypt-to-local
    (populates .env.local)

    View Slide

  50. Takeaways?
    @nicolasgrekas

    View Slide

  51. @nicolasgrekas
    Key takeaways
    •Use parameters for business requirements
    •Use env vars for infra-related configuration
    •Use .env files first, increase complexity later
    •Use bin/console secrets:*
    •Use Consul/etc. when the need is here
    You’re on the path to high-availability!

    View Slide

  52. Merci Thank you Gracias ﺍﺮﻜﺷ
    多謝 Ευχαριστώ ありがとう
    감사합 니다 Спасибо!

    View Slide

  53. View Slide