<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Sam Jarrett]]></title><description><![CDATA[Musings of a web developer in recovery]]></description><link>https://www.samjarrett.com.au/</link><image><url>https://www.samjarrett.com.au/favicon.png</url><title>Sam Jarrett</title><link>https://www.samjarrett.com.au/</link></image><generator>Ghost 1.21</generator><lastBuildDate>Sun, 25 Feb 2018 06:33:14 GMT</lastBuildDate><atom:link href="https://www.samjarrett.com.au/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Symfony finally swiped right on envvars (the gifless edition)]]></title><description><![CDATA[<div class="kg-card-markdown"><h5 id="orwhydowekeepbakingsecretsintoourartifacts"><em>(Or, “why do we keep baking secrets into our artifacts?”)</em></h5>
<p>A paper on the talk I gave at the <a href="https://www.meetup.com/Melbourne-Symfony/">Melbourne Symfony Developers group</a> on September 26, 2017. Slides are available <a href="https://www.slideshare.net/SamJarrett4/symfony-finally-swiped-right-on-envvars">here</a>.</p>
<p><img src="https://www.samjarrett.com.au/content/images/2017/09/giphy-1.gif" alt="Back to the Future"></p>
<h2 id="symfonyisuniqueasawebframework">Symfony is unique as a web framework.</h2>
<h3 id="mostframeworksandapplicationsrereadconfigurationwitheachboot">Most frameworks and applications re-read configuration with each boot.</h3>
<ul>
<li>Booting in the</li></ul></div>]]></description><link>https://www.samjarrett.com.au/symfony-finally-swiped-right-on-envvars/</link><guid isPermaLink="false">59c760da3f20de00052758bb</guid><category><![CDATA[Symfony]]></category><category><![CDATA[Secrets]]></category><category><![CDATA[Environment Variables]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Development]]></category><dc:creator><![CDATA[Sam Marley-Jarrett]]></dc:creator><pubDate>Tue, 26 Sep 2017 08:38:00 GMT</pubDate><media:content url="https://www.samjarrett.com.au/content/images/2017/09/giphy.gif" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><h5 id="orwhydowekeepbakingsecretsintoourartifacts"><em>(Or, “why do we keep baking secrets into our artifacts?”)</em></h5>
<img src="https://www.samjarrett.com.au/content/images/2017/09/giphy.gif" alt="Symfony finally swiped right on envvars (the gifless edition)"><p>A paper on the talk I gave at the <a href="https://www.meetup.com/Melbourne-Symfony/">Melbourne Symfony Developers group</a> on September 26, 2017. Slides are available <a href="https://www.slideshare.net/SamJarrett4/symfony-finally-swiped-right-on-envvars">here</a>.</p>
<p><img src="https://www.samjarrett.com.au/content/images/2017/09/giphy-1.gif" alt="Symfony finally swiped right on envvars (the gifless edition)"></p>
<h2 id="symfonyisuniqueasawebframework">Symfony is unique as a web framework.</h2>
<h3 id="mostframeworksandapplicationsrereadconfigurationwitheachboot">Most frameworks and applications re-read configuration with each boot.</h3>
<ul>
<li>Booting in the the sense of refers to the app loading on each page load.</li>
<li>In comparison, Symfony demands that you compile the application entirely before you boot it.</li>
<li>Not just something that occurs in PHP. Ruby, Python, Node, etc all do this.<br>
Java also suffers from this same issue, but you notice it differently...</li>
</ul>
<h3 id="binelasticsearch"><code>$ bin/elasticsearch</code></h3>
<p>… wait 30 seconds<br>
<code>$ curl localhost:9200</code><br>
[timeout]<br>
… wait 30 more seconds…<br>
<code>$ curl localhost:9200</code><br>
[timeout]<br>
… wait 30 more seconds…</p>
<ul>
<li>This is familiar with any time you’ve ever booted a java app. Building the configuration tree and configuring the services is mostly why they take so long to start accepting traffic!</li>
</ul>
<h3 id="symfonycompilesthedicontainertophpincludingallconfigurationvalues">Symfony compiles the DI container to PHP, including all configuration values.</h3>
<ul>
<li>Which gives us a good speed improvement because the app doesn’t have to load all of it’s configuration and build the container at runtime!</li>
<li>This creates a big problem when we have complex build and deploy processes that need to operate at scale (how do you deploy to 20+ instances quickly? prebuild!) and still protect secret values</li>
<li>Modern deployment practices emphasise keeping the artifact static between environments, to be able to more easily replicate issues back in development or testing environments.</li>
<li>Container compilation means that we can’t guarantee that our artifact compiles correctly or that it is exactly the same in production. So, how do we solve this in Symfony?</li>
</ul>
<h2 id="butfirstletstalkaboutsecrets">But first… Let’s talk about secrets.</h2>
<h3 id="whatisasecret">What is a secret?</h3>
<ul>
<li>Secrets can be any form of data that you wouldn’t want to be widely spread around.</li>
<li>Database credentials</li>
<li>SMTP logins</li>
<li>SMS or EDM api keys</li>
<li>Payment gateway keys</li>
<li>AWS credentials (access keys, etc)</li>
</ul>
<h3 id="themodernapphassomany">The modern app has so many!</h3>
<ul>
<li>Some more obscure things include:</li>
<li>S3 bucket names</li>
<li>SSH or deploy keys</li>
<li>JWT keys or certificates</li>
<li>Internal hostnames (i.e. unprotected internal systems you’re hiding): redis, memcached, finance systems, etc<br>
Lead developer’s email address (for emailing emerging issues, I guess)</li>
<li>Pagerduty/OpsGenie alert topics/email address (don’t want these abused overnight waking up on call staff!)</li>
<li>Slack tokens</li>
<li>… <em>Service n</em>’s API key</li>
</ul>
<p>Summary: An application secret is anything you wouldn’t be comfortable putting directly in the HTML markup itself.</p>
<h2 id="hereswhywegetabadrepasphpdevelopers">Here’s why we get a bad rep as PHP developers</h2>
<p>Here are some ways I’ve seen secret management in production apps. Let’s take a look back through some ways PHP apps have managed secrets before the current generation.</p>
<h3 id="theknowitallpattern">The know it all “pattern”</h3>
<ul>
<li>
<p>Create a single class or factory class, and delegate responsibility for it to provide you with answers,</p>
</li>
<li>
<p>Ultimately embedding the secret for all environments in it -- e.g.</p>
<pre><code>const LIVE_DATABASE_STRING = 'mysql://root:root@localhost:3306/prod';
const TEST_DATABASE_STRING = 'mysql://root:root@localhost:3306/test';
</code></pre>
</li>
<li>
<p>Compromise here on either test or production means all of your secrets are gone.</p>
</li>
</ul>
<h3 id="thebuildittheyllcomepattern">The build it &amp; they’ll come “pattern”</h3>
<ul>
<li>
<p>Use ini, json, yaml, java.properties style or some other configuration format to specify all of your values</p>
</li>
<li>
<p>Create a php-class as a template full of placeholders that a template parser can quickly generate the final value for you -- e.g.</p>
<pre><code>const DATABASE_STRING = '{{ database_string }}';
</code></pre>
</li>
<li>
<p>Generate the PHP from the template before deploying (as part of a build or deploy process)</p>
</li>
<li>
<p>Include the final configuration (without the config file) as part of your deployment artifact.</p>
</li>
<li>
<p>???</p>
</li>
<li>
<p>Profit?</p>
</li>
</ul>
<blockquote>
<h3 id="mybiggestprofessionalfuckupprobably">My biggest professional fuck-up, probably?</h3>
<p>Dropping a production database because a test-site was misconfigured to use it by one of the above secret management methods <em>(you can guess)</em></p>
</blockquote>
<h3 id="theconfigymlisthenewconfigclasspattern">The config.yml is the new Config class …. “pattern”</h3>
<ul>
<li>Same management principal as the config class (“know it all”), but instead we add some Symfony flair!</li>
<li>Use <code>config_dev.yml</code> and <code>config_prod.yml</code> to store secrets for each environment</li>
<li>Largely seen in remnants of old Symfony 2.0 projects, from before the framework suggested a way of swapping values out yet (i.e. pre-<code>parameters.ini</code> &amp; <code>.gitignore</code> recommendations)</li>
</ul>
<h2 id="intotoday">Into today</h2>
<p>(As is common in many Symfony projects)</p>
<h3 id="thefabientoldmetopattern">The Fabien told me to “pattern”</h3>
<ul>
<li>Define your config keys in a file called parameters.yml.dist, setting sane (development?) values</li>
<li>Store actual configuration in S3, build servers, CI or wherever you build your app</li>
<li>At build-time, copy the file into app/config &amp; build the container (cache-warm / cache-clear)</li>
<li>Best of the bad approach</li>
<li>Because of Symfony’s cache compilation, it makes this insecure and difficult to update</li>
</ul>
<h3 id="whatswrongwiththisapproach">What’s wrong with this approach?</h3>
<ul>
<li>
<p>How secure is your CI?</p>
<ul>
<li>CI systems can be hacked.</li>
<li>Does yours have a security breach notification policy?</li>
</ul>
</li>
<li>
<p>Who has access to wherever the parameter values are stored?</p>
<ul>
<li>If it’s in S3 or on a server somewhere that the CI can access, can others?</li>
<li>Do you audit access to this?</li>
</ul>
</li>
<li>
<p>Rolling any keys mean rebuilding and redeploying the entire application</p>
<ul>
<li>In the midst of a security incident, this may allow an unacceptable delay to roll a pivotal secret, or mean that your application “goes down” or has degraded functionality until the build/deploy completes</li>
<li>If the key is something critical (e.g. database), you can’t do a rolling deploy - it has to be all or nothing.</li>
</ul>
</li>
<li>
<p>Secrets are compiled into the container class!<br>
Try it yourself:</p>
<pre><code>$ grep “database.host” var/cache/prod/*.php
</code></pre>
<p>Pro-tip: If you ever lose them, you can look at <code>var/cache/prod/appProdContainer.php</code> in any previous deploy for your DB creds!</p>
</li>
<li>
<p>Creating new deployments is hard!</p>
<ul>
<li>Want to add a pre-prod?</li>
<li>Or test a new feature on prod-like infra?</li>
<li>New deployments now involve: updating your CI &amp; build process, building a new artifact, updating your deployment process &amp; tools</li>
</ul>
</li>
<li>
<p>Production artifact differs to test</p>
<ul>
<li>How sure are you that the issue doesn’t exist solely on production?</li>
<li>Can developers 'drag' the exact artifact down to their machine to replicate?</li>
</ul>
</li>
</ul>
<h2 id="oksowhatstheanswer">OK, so what’s the answer?</h2>
<h3 id="introducingenvironmentvariablesenvvarsthegood">Introducing environment variables (“envvars”) - the good</h3>
<ul>
<li>Easy to set up in your app</li>
<li>Functions mostly just like another parameter</li>
<li>Not compiled into the container (although defaults are)</li>
<li>Like other parameters, still allows default values to be set</li>
<li>Updating values is easy! Just update wherever the value is stored and reload PHP</li>
</ul>
<h3 id="introducingenvironmentvariablesenvvarsthenotsogood">Introducing environment variables (“envvars”) - the not so good</h3>
<ul>
<li>Can’t (currently) access the value outside of a service, e.g. in a controller, so implementing may mean refactoring the application</li>
<li>Can be difficult to use environment variables consistently if you run your application outside of Docker</li>
<li>Some libraries don’t <em>quite</em> support envvars yet</li>
</ul>
<h2 id="portinganexistingapplicationiseasy">Porting an existing application is easy</h2>
<ul>
<li>Drop any periods (“<code>.</code>”) in parameter names and replace with underscores<br>
e.g. <code>database.host</code> becomes <code>database_host</code>, or better yet, <code>DATABASE_HOST</code> (although Symfony doesn't care)</li>
<li>Wrap the parameter name with “<code>env()</code>”<br>
e.g. <code>database.host</code> becomes <code>env(DATABASE_HOST)</code></li>
<li>Wherever the variable is used (<code>services.yml</code>, <code>config.yml</code>, etc), replace the key the same way</li>
<li>If any don’t support environment variables, you can sometimes replace with what Symfony calls a <em>dynamic</em> parameter
<ul>
<li>Dynamic parameters are mostly used whenever you have a parameter that refers to another parameter</li>
<li>The container delays calculating the value until as late as possible, so the full value only gets cached when it’s first used</li>
<li>To do it, update your parameters as above, but also define a parameter like so:<br>
<code>old.parameter.name: %env(PARAMETER_NAME)%</code></li>
<li>Can be used if you must access the parameter in a controller or other non-service space</li>
<li>but probably suggests you need to refactor</li>
<li>Does not work if a library needs the value at container compilation time (e.g. if a container extension needs it to decide what class to use based on a value)</li>
</ul>
</li>
</ul>
<h3 id="whileyoureporting">While you’re porting</h3>
<ul>
<li>In Symfony we think of application environments (dev, test and prod only, usually), and try to keep agnostic to deployment environments (e.g. test/staging server is still running the production application environment)</li>
<li>But it’s valuable to consider what values actually change between deployment environments</li>
<li>Consider using application-deployment environments - i.e. the combination of what mode the application runs in <em>and</em> what deployment you're running in</li>
<li>Anything that doesn’t change between app or deployment environments that isn't sensitive/secret should go into <code>config.yml</code> as a <code>parameters:</code> block (see <a href="https://github.com/symfony/symfony-standard/blob/3.3/app/config/config.yml#L6-L9">Symfony standard edition recommendation</a>)</li>
<li>Anything that changes between application environments but not deployemnts (that isn't sensitive/secret) should be stored in <code>config_dev.yml</code> or <code>config_prod.yml</code> as a <code>parameters:</code> block</li>
<li>Anything that changes between deployments or is secretive is a good candidate to live in the environment</li>
</ul>
<h3 id="anygotchas">Any gotchas?</h3>
<ul>
<li>You need to defer making decisions on anything that changes per-deployment until later. -- e.g. any decisions made in a container extension</li>
<li>Dynamic parameters don’t solve this (<code>%param% =&gt; %env(PARAM)%</code>)</li>
<li>If it’s your bundle/extension, you can try using another mechanism, such as factory services to work around this</li>
</ul>
<h3 id="thedotenvcomponentinsymfony33">The Dotenv component in Symfony 3.3</h3>
<ul>
<li>Solves the biggest challenge using env vars: development environments where setting envvars is traditionally difficult</li>
<li>Symfony flex uses it out of the box!</li>
<li>It’s a small class that is added in your <code>app</code>/<code>app_dev.php</code> and <code>console</code> scripts</li>
<li>Provides env-var like support in places you can’t easily set variables (development!)</li>
<li>In Flex, it serves to replace the parameters.yml paradigm</li>
<li>Relatively easy to back-port into the full-stack framework</li>
</ul>
<h2 id="or">Or...</h2>
<h3 id="youcanusedockerindevelopment">You can use Docker in development!</h3>
<p>Docker is now quite stable on Mac and Windows!</p>
<ul>
<li>Mac: <a href="https://www.docker.com/docker-mac">https://www.docker.com/docker-mac</a></li>
<li>Windows: <a href="https://www.docker.com/docker-windows">https://www.docker.com/docker-windows</a></li>
</ul>
<h2 id="itsnotallaboutsecurity">It’s not (all) about security</h2>
<p>I've talked a bit about how this is really all about security, but there's one big gotcha:</p>
<p><img src="https://www.samjarrett.com.au/content/images/2017/09/Image-1.png" alt="Symfony finally swiped right on envvars (the gifless edition)"></p>
<p><img src="https://www.samjarrett.com.au/content/images/2017/09/Image-1-1.png" alt="Symfony finally swiped right on envvars (the gifless edition)"></p>
<h3 id="environmentvariablesarenotperfect">Environment variables are not perfect</h3>
<ul>
<li>Doesn’t solve security out of the box for anyone with access to your servers</li>
<li>Can be accessed outside your application - e.g. any <code>shell_exec()</code> calls you make get the full environment</li>
<li>Anything run as the Apache, PHP or www-data user, or someone sudo-ed to it can unintentionally expose to another program</li>
<li>Any insecure libraries in your application can access the values, too</li>
<li>Can unintentionally end up in logs or the Symfony profiler, so be careful with what you log your app!</li>
<li>Your plain text values are likely to be stored somewhere, too (some ideas on this below)</li>
</ul>
<h3 id="howdoimakethisbetter">How do I make this better?</h3>
<ul>
<li>Current PR: <a href="https://github.com/symfony/symfony/pull/23901">github/symfony/symfony#23901</a> (due Symfony 3.4) adds features to support docker/kubernetes secrets out of the box &amp; make this extendable in your app!</li>
<li>Once it’s available to us, here’s some ideas on how you can keep your raw values safe...</li>
<li>If you use AWS for your application, possibly:
<ul>
<li>KMS-encrypted value provided as an environment variable and a provider that decrypts at run-time</li>
<li>Using SSM Parameter Store to fetch values at run-time</li>
<li>You can use both of these services without running locally on AWS, but integration with short-lived machine tokens so you don't need to embed an access key &amp; secret in the machine somewhere makes it much more appealing</li>
</ul>
</li>
<li>Not on AWS? <a href="https://cloud.google.com/kms/docs/secret-management">Google Cloud KMS</a> for their equivalent to KMS might offer a similar idea</li>
<li>Or you can use other open-source tools and write custom providers to fetch &amp; decrypt (hopefully the community helps here!) - existing secret management tools like:
<ul>
<li><a href="https://www.vaultproject.io/">Hashicorp Vault</a></li>
<li><a href="https://github.com/pinterest/knox">Pinterest Knox</a></li>
<li><a href="https://lyft.github.io/confidant/">Lyft Confidant</a></li>
</ul>
</li>
<li>In the meantime, before we can use 3.4: if you’re hosting on AWS, use <a href="https://aws.amazon.com/kms/">KMS-encrypted</a> environment variables or fetch from SSM Param. Store at container boot (open-source tools exist to help with this!)</li>
</ul>
<h3 id="furtherreading">Further reading</h3>
<ul>
<li>
<p>Were any of the ideas on using the environment a surprise? PHP developers have shied away from it until recently, while other languages have embraced them entirely for config. Check out the <a href="https://12factor.net/">12 factor app</a> for more info.</p>
</li>
<li>
<p><a href="https://segment.com/blog/the-right-way-to-manage-secrets/">Segment have a great post on managing secrets with AWS SSM parameter store on their blog</a></p>
</li>
<li>
<p>Pre-3.4, if you use KMS-encrypted envvars (encrypted strings): REA’s <a href="https://github.com/realestate-com-au/shush/">Shush</a> library is a tiny go library to help decrypt your secrets transparently<br>
Usage is simple - change your execution like so:</p>
<pre><code>$ shush exec -- php-fpm
</code></pre>
</li>
<li>
<p>More pre-3.4: If you use SSM parameter store (which provides central management of secrets): Segment’s <a href="https://github.com/segmentio/chamber">Chamber</a> can help in a very similar way.</p>
</li>
<li>
<p>Taking inspiration from <a href="https://kubernetes.io/docs/concepts/configuration/secret/">kubernetes</a>, Docker swarm mode now has <a href="https://docs.docker.com/engine/swarm/secrets/">secrets built-in</a>.</p>
</li>
<li>
<p>Not using Docker? It can be <a href="https://ypereirareis.github.io/blog/2015/12/13/symfony-nginx-environment-variables-php-fpm/">quite complicated to set</a> env across multiple users + daemons. <a href="https://httpd.apache.org/docs/2.4/env.html">Apache</a>, and <a href="https://wiki.archlinux.org/index.php/environment_variables#Globally">CLI users</a> share a syntax, but of course <a href="https://medium.com/@tomahock/passing-system-environment-variables-to-php-fpm-when-using-nginx-a70045370fad">PHP-FPM</a> is different (eye roll).</p>
</li>
<li>
<p>For the adventurous: I've got an example of porting a Symfony application to Docker on <a href="https://github.com/samjarrett/Symfony-docker-example">Github</a> that talks through the changes you would make, commit by commit</p>
</li>
</ul>
</div>]]></content:encoded></item></channel></rss>