<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <title>Blog entries tagged composer :: mwop.net</title>
    <description>Blog entries tagged composer :: mwop.net</description>
    <pubDate>Thu, 17 Dec 2015 09:00:00 -0600</pubDate>
    <generator>Laminas_Feed_Writer 2 (https://getlaminas.org)</generator>
    <link>https://mwop.net/blog/tag/composer</link>
    <atom:link rel="self" type="application/rss+xml" href="https://mwop.net/blog/tag/composer/rss.xml"/>
    <item>
      <title>Secure PHAR Automation</title>
      <pubDate>Thu, 17 Dec 2015 09:00:00 -0600</pubDate>
      <link>https://mwop.net/blog/2015-12-14-secure-phar-automation.html</link>
      <guid>https://mwop.net/blog/2015-12-14-secure-phar-automation.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>For a variety of reasons, I've been working on a utility that is best
distributed via <a href="http://php.net/phar">PHAR</a> file. As has been <a href="https://archive.is/hDwaA">noted by
others</a> (archive.is link, due to lack of availability
of original site), PHAR distribution, while useful, is not without security
concerns, and I decided to investigate how to securely create, distribute, and
update PHAR utilities as part of this exercise.</p>
<p>This is an account of my journey, as well as concrete steps you can take to
secure your own PHAR downloads.</p>


<h2>The Roadmap</h2>
<p>The steps outlined by Pádraic Brady in the afore-linked post were essentially:</p>
<ul>
<li>Distribute the PHAR over TLS-secured HTTPS.</li>
<li>Sign your PHAR with a private key.</li>
<li>Manage self updates securely (i.e., the updates must be over TLS, and updated
PHAR files should be signed using the same private key).</li>
</ul>
<p>As such, I figured the plan should be:</p>
<ul>
<li>Use <a href="https://pages.github.com">GitHub Pages</a> for distribution. This gives me
essentially free hosting, and free TLS.</li>
<li>Create an OpenSSL key, use it to sign the package, and provide the public key
for download.</li>
<li>Have functionality built-in to the PHAR for updating and rolling back.</li>
<li>Automate creation of the PHAR, as well as pushing it and the version
information to the site.</li>
</ul>
<p>Seems simple enough, right?</p>
<p>It would have been, had I been able to find examples of each of the steps. In
the end, I spent an afternoon testing different strategies, and finally came up
with what follows.</p>
<h2>Create an OpenSSL Key</h2>
<p>The first step is to create an OpenSSL private key. This will be used to sign
the packages.</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ openssl genrsa -des3 -out phar-private.pem 4096
</code></pre>
<p>The above will prompt you for a passphrase, which is used to encrypt the key.</p>
<p>For purposes of automation, however, you may not (in fact, will not, as you'll
see later) be able to enter the passphrase. As such, you'll need to strip it.</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ cp phar-private.pem phar-private.pem.passphrase-protected
$ openssl rsa -<span class="hljs-keyword">in</span> phar-private.pem -out phar-private-nopassphrase.pem
$ cp phar-private-nopassphrase.pem phar-private.pem
</code></pre>
<p>The second step will prompt you for the passphrase. The resultant key will have
it stripped. You can keep the passphrase-protected version if you wish; however,
it's functionally equivalent to the new key. If you keep it, place it somewhere
safe.</p>
<p>From here, create a <code>.travis/</code> subdirectory in your project, put the private key in
it, and then add that file to your project <code>.gitignore</code>:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ mkdir .travis
$ mv phar-private.pem .travis/
$ <span class="hljs-built_in">echo</span> <span class="hljs-string">".travis/phar-private.pem"</span> &gt;&gt; .gitignore
</code></pre>
<p>We're telling git to ignore the key, as we don't want to push it unencrypted to
our repository!</p>
<h2>Use Box to create the PHAR</h2>
<p>Now that we have a key, we can think about creating our PHAR file.</p>
<p>While PHP provides a <a href="http://php.net/phar">ton of functionality around PHARs</a>,
the problem is that the manual is not terribly detailed, and this particular
section has often gone out-of-date. On top of that, to build even a relatively
simple PHAR that has an executable stub takes a ton of knowledge.</p>
<p>So, let others do the work for you. Specifically, the <a href="https://box-project.github.io/box2/">Box Project</a>.
The team behind the Box Project has done the hard work for you; all you need
to do is create a configuration file that details what files are used, what
compression to use, what signing mechanism to use (if any) and where the key is
located, etc.</p>
<p>The link above details retrieving box, but the basics are:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ curl -LSs https://box-project.github.io/box2/installer.php | php
</code></pre>
<p>which will leave an executable <code>box.phar</code> in the working directory. I usually
put this in my <code>$PATH</code> and alias <code>box</code> to it.</p>
<p>Box, like many other PHP utilities, allows you to create either a configuration
file, or a &quot;dist&quot; configuration file. I like to do the latter, as it then lets
me copy it locally to provide modifications/customizations. Here's a basic
<code>box.json.dist</code> file containing the options for creating an OpenSSL-signed,
gzipped, executable package:</p>
<pre><code class="language-javascript hljs javascript" data-lang="javascript">{
  <span class="hljs-string">"algorithm"</span>: <span class="hljs-string">"OPENSSL"</span>,
  <span class="hljs-string">"chmod"</span>: <span class="hljs-string">"0755"</span>,
  <span class="hljs-string">"compression"</span>: <span class="hljs-string">"GZ"</span>,
  <span class="hljs-string">"directories"</span>: [
    <span class="hljs-string">"src"</span>
  ],
  <span class="hljs-string">"files"</span>: [
    <span class="hljs-string">"LICENSE.md"</span>
  ],
  <span class="hljs-string">"finder"</span>: [
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"*.php"</span>,
      <span class="hljs-string">"exclude"</span>: [
        <span class="hljs-string">"tests"</span>,
        <span class="hljs-string">"test"</span>
      ],
      <span class="hljs-string">"in"</span>: <span class="hljs-string">"vendor"</span>
    }
  ],
  <span class="hljs-string">"git-version"</span>: <span class="hljs-string">"package_version"</span>,
  <span class="hljs-string">"intercept"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-string">"key"</span>: <span class="hljs-string">".travis/phar-private.pem"</span>,
  <span class="hljs-string">"main"</span>: <span class="hljs-string">"bin/command.php"</span>,
  <span class="hljs-string">"output"</span>: <span class="hljs-string">"command.phar"</span>,
  <span class="hljs-string">"stub"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<p>Some notes on the various options:</p>
<ul>
<li>&quot;algorithm&quot; and &quot;key&quot; go hand-in-hand. The algorithm indicates what package
signing algorithm to use, and the &quot;key&quot; is the path on the filesystem to the
key; relative paths are relative to the <code>box.json.dist</code> file.</li>
<li>&quot;compression&quot; indicates the compression algorithm to use; in this case, I used
gzip.</li>
<li>&quot;git-version&quot; is a string to look for in files; technically, it looks for
<code>@package_version@</code>, and not just the string. When discovered, it replaces
that string with the sha1 of the most recent commit.</li>
<li>&quot;directories&quot; is used to specify directories where all files should be
included; &quot;files&quot; is used to specify individual files to include. Usually I
use &quot;files&quot; for anything not in &quot;directories&quot;, like the LICENSE file.</li>
<li>&quot;finder&quot; can be used in a similar fashion to &quot;directories&quot; and &quot;files&quot;, but
gives you the ability to provide filtering rules; it uses the <a href="http://symfony.com/doc/current/components/finder.html">Symfony Finder
component</a>, which means
that the rules you build will follow that component's configuration syntax. In
the example above, I'm telling it I want to include the <code>vendor/</code> directory,
but to only include PHP files, and to exclude files in the directories &quot;test&quot;
or &quot;tests&quot;. Doing so greatly reduces the size of the generated PHAR.</li>
<li>&quot;main&quot; is the name of the file containing the script to execute when the PHAR
is invoked. Obviously, the name will vary based on your application.</li>
<li>&quot;stub&quot;, when <code>true</code>, indicates thatthe default stub based on the &quot;main&quot; script
should be used; you can specify another stub file as well, if desired. I've
used Box several times, and found that this needs to be boolean <code>true</code> when
I'm having it encapsulate a command-line script.</li>
<li>&quot;output&quot; is the name of the file to generate.</li>
<li>&quot;chmod&quot; indicates the file mode mask to set on the generated file.</li>
</ul>
<p>Obviously, configure this according to your needs, and add or remove
configuration as befits your project. The <a href="https://github.com/box-project/box2/blob/2.0/res/schema.json">schema
file</a> is your
friend when determining what should be included; much of the information is also
available via <code>box.phar help build</code>.</p>
<p>Once you have created the file, you can attempt a build:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ box build -vv
</code></pre>
<p>(<code>-vv</code> indicates &quot;verbosity&quot;; I do this so I can see errors if they occur.)</p>
<blockquote>
<h3>Make the build leaner!</h3>
<p>The command may take a while. In fact, if you have a lot of dependencies, it
<em>will</em> take a while. I found it was best to strip any development-only
dependencies prior to running a build:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ composer install --no-dev
</code></pre>
<p>I find this results in a far faster build, with a much smaller file size.</p>
</blockquote>
<p>When the build is successful, you'll end up with two files:</p>
<ul>
<li><code>command.phar</code></li>
<li><code>command.phar.pubkey</code></li>
</ul>
<p>The name will be based on what you specified for &quot;output&quot; in the configuration.
The second file is the OpenSSL public key derived from your private key. Users
will need to have <em>both</em> files available, as PHP's PHAR functionality will
verify the archive against the public key on every invocation. This is what
ensures a secure distribution!</p>
<p>Now that you have the files, add them to your <code>.gitignore</code>:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ <span class="hljs-built_in">echo</span> <span class="hljs-string">"command.phar"</span> &gt;&gt; .gitignore
$ <span class="hljs-built_in">echo</span> <span class="hljs-string">"command.phar.pubkey"</span> &gt;&gt; .gitignore
</code></pre>
<p>We don't want the PHAR in the master branch; this is the branch used to create
the PHAR itself. By excluding it, we can do some automation later.</p>
<h2>Generate a version file</h2>
<p>Now that we have the PHAR, we need to provide a way of indicating what <em>version</em>
we have. The simplest way to do that is to take its sha1sum and write it to a
file:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ sha1sum command.phar &gt; command.phar.version
</code></pre>
<p>We'll use this later to automate self-updates. Just like the PHAR file itself
and its public key, we'll exclude it from the branch:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ <span class="hljs-built_in">echo</span> <span class="hljs-string">"command.phar.version"</span> &gt;&gt; .gitignore
</code></pre>
<h2>Create the gh-pages branch</h2>
<p>At this stage, we have a PHAR file that is signed with a private OpenSSL key, a
public OpenSSL key for verifying the signature, and a version file we can use
later for triggering updates. It's time to publish those.</p>
<p>As noted above, we want to publish to a TLS-enabled site. GitHub provides that
infrastructure for us via GitHub Pages. These are available via any public
repository that publishes a <code>gh-pages</code> branch. The obvious conclusion is: let's
add that branch to our current repository!</p>
<p>That said, the new branch really shouldn't have the build tools and code.</p>
<p>First, let's make sure you've committed everything you need on the master
branch:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ git add .gitignore box.json.dist
$ git commit -m <span class="hljs-string">'Build tools for PHAR file'</span>
</code></pre>
<p>Now we can create what's called an &quot;orphan&quot; branch — a branch with no
history and no parents:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ git checkout --orphan gh-pages
$ git rm -rf .
</code></pre>
<p>We perform the <code>git rm -rf .</code> command in order to remove any files previously
committed. One nice side effect is that untracked files — such as our
generated PHAR, public key, and version file — are untouched by this
operation!</p>
<p>So, let's add them:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ git add composer.phar*
</code></pre>
<p>It would be good to create a landing page as well, with links for downloading
the PHAR file and the public key (don't worry about the version file for now).
Create an <code>index.html</code> file in the root of the project, and add that as well, as
well as any CSS or JavaScript files you need for it. I personally used
<a href="https://getbootstrap.com">Bootstrap</a> from a CDN for this, which gives a
reasonable default look for the package.</p>
<p>Once all your files are added, execute:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ git commit -m <span class="hljs-string">'Initial gh-pages files'</span>
</code></pre>
<p>and then push to your GitHub repository.</p>
<p>Within a minute or so, you should be able to browse to <code>https://&lt;your username or org&gt;.github.io/&lt;your repo&gt;/</code>.</p>
<h2>Write self-update/rollback commands</h2>
<p>Now that we have a process for creating a PHAR, how will users update or
rollback?</p>
<p>Fortunately, Pádraic has a solution for that as well: <a href="https://github.com/padraic/phar-updater">PHAR
Updater</a>. This handy library provides
functionality for replacing the PHAR, and has built-in support to verify the
signature of the replacement with the public key. For it to work, the PHAR,
public key, and version file must all be accessible over TLS/SSL.</p>
<p>First, add the utility to your package:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ composer require padraic/phar-updater
</code></pre>
<p>Regardless of how you write your console commands in PHP, your self-update
command will execute something like the following:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">use</span> <span class="hljs-title">Humbug</span>\<span class="hljs-title">SelfUpdate</span>\<span class="hljs-title">Updater</span>;

$updater = <span class="hljs-keyword">new</span> Updater();
$updater-&gt;getStrategy()-&gt;setPharUrl($urlToGithubPagesPharFile);
$updater-&gt;getStrategy()-&gt;setVersionUrl($urlToGithubPagesVersionFile);
<span class="hljs-keyword">try</span> {
    $result = $updater-&gt;update();
    <span class="hljs-keyword">if</span> (! $result) {
        <span class="hljs-comment">// No update needed!</span>
        <span class="hljs-keyword">exit</span>(<span class="hljs-number">0</span>);
    }
    $new = $updater-&gt;getNewVersion();
    $old = $updater-&gt;getOldVersion();
    printf(<span class="hljs-string">'Updated from %s to %s'</span>, $old, $new);
    <span class="hljs-keyword">exit</span>(<span class="hljs-number">0</span>);
} <span class="hljs-keyword">catch</span> (\<span class="hljs-keyword">Exception</span> $e) {
    <span class="hljs-comment">// Report an error!</span>
    <span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}
</code></pre>
<p>It's really that simple! The defaults assume an OpenSSL-signed PHAR file, and
that the public key is present locally in a file named <code>&lt;name of phar&gt;.pubkey</code>.
If the version is different, it updates; if not, it doesn't. Exceptions
typically occur for things like:</p>
<ul>
<li>File permissions.</li>
<li>Inability to reach the remote PHAR or version files.</li>
<li>Inability to validate the downloaded PHAR.</li>
<li>Inability to perform TLS negotation.</li>
</ul>
<p>Regarding this latter, padraic/phar-updater includes
padraic/humbug_get_contents, which is supposed to iron out TLS issues on PHP
versions &lt; 5.6. I found in practice, however, that when performing the update,
if I didn't use a PHP 5.6 version, it consistently failed, indicating TLS
negotiation issues. Supposedly you can fix these by downloading
<a href="http://curl.haxx.se/ca/cacert.pem">http://curl.haxx.se/ca/cacert.pem</a> and setting
<code>openssl.cafile=/path/to/cacert.pem</code> in your <code>php.ini</code> file.</p>
<p>So, self-update is taken care of; what about rollback?</p>
<p>When a self-update is performed using padraic/phar-updater, it writes the
original PHAR to <code>command-old.phar</code> in the same directory as the original PHAR
file. If that file is available, you can write a rollback routine like the
following:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">use</span> <span class="hljs-title">Humbug</span>\<span class="hljs-title">SelfUpdate</span>\<span class="hljs-title">Updater</span>;

$updater = <span class="hljs-keyword">new</span> Updater();
<span class="hljs-keyword">try</span> {
    $result = $updater-&gt;rollback();
    <span class="hljs-keyword">if</span> (! $result) {
        <span class="hljs-comment">// report failure!</span>
        <span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">exit</span>(<span class="hljs-number">0</span>);
} <span class="hljs-keyword">catch</span> (\<span class="hljs-keyword">Exception</span> $e) {
    <span class="hljs-comment">// Report an error!</span>
    <span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}
</code></pre>
<p>Again, quite easy!</p>
<p>Checkout your project's <code>master</code> branch, add the above commands, and re-build
your PHAR…</p>
<p>Wait, shouldn't we automate that last step? It'd be really nice if we could push
to master, and have the new PHAR show up automatically on our gh-pages branch!</p>
<h2>Enable Travis-CI for the repository</h2>
<p>How do we automate? Via continuous integration and deployment, of course!</p>
<p>For this step, I'm choosing <a href="https://travis-ci.org">Travis-CI</a>. It's free for
open source projects, but also has a paid, private tier if you need it. Its
dockerized builds trigger typically within seconds of pushing your code, and the
environment is built and your tests run in often under 30 seconds. It's a great
choice for this.</p>
<p>As a CI service, it provides a number of stages that trigger, often
conditionally. We're going to use one of these, <code>after_success</code>, to build the
PHAR, update the version file, and push them to our gh-pages branch.</p>
<p>First, though, we need to enable Travis-CI for our repository. You will need to
do the following:</p>
<ul>
<li>Register for a Travis-CI account if you haven't already. The homepage will
guide you there.</li>
<li>Once you have an account and have logged in, go to your profile page (clicking
your icon in the top right takes you there).</li>
<li>Find your repository in the list, and toggle the switch to enable it. (You may
need to sync your repositories if your project is new within the last day;
there's typically a &quot;Sync&quot; button next to where your name appears above the
repository list.)</li>
</ul>
<p>From here, you should add a <code>.travis.yml</code> file to your project, if you haven't
already. Here's a template:</p>
<pre><code class="language-yaml hljs yaml" data-lang="yaml"><span class="hljs-attr">sudo:</span> <span class="hljs-literal">false</span>
<span class="hljs-attr">language:</span> <span class="hljs-string">php</span>

<span class="hljs-attr">cache:</span>
  <span class="hljs-attr">directories:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">$HOME/.composer/cache</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">vendor</span>

<span class="hljs-attr">matrix:</span>
  <span class="hljs-attr">fast_finish:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">include:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">php:</span> <span class="hljs-number">5.5</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">php:</span> <span class="hljs-number">5.6</span>
    <span class="hljs-attr">env:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">EXECUTE_DEPLOYMENT=true</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">php:</span> <span class="hljs-number">7</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">php:</span> <span class="hljs-string">hhvm</span>
  <span class="hljs-attr">allow_failures:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">php:</span> <span class="hljs-string">hhvm</span>

<span class="hljs-attr">before_install:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">phpenv</span> <span class="hljs-string">config-rm</span> <span class="hljs-string">xdebug.ini</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">composer</span> <span class="hljs-string">self-update</span>

<span class="hljs-attr">install:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">travis_retry</span> <span class="hljs-string">composer</span> <span class="hljs-string">install</span> <span class="hljs-string">--no-interaction</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">composer</span> <span class="hljs-string">info</span> <span class="hljs-string">-i</span>

<span class="hljs-attr">script:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">./vendor/bin/phpunit</span> <span class="hljs-comment"># If you have tests</span>

<span class="hljs-attr">notifications:</span>
  <span class="hljs-attr">email:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>Commit and push that file, and you should see your first build appear on
Travis-CI.</p>
<h2>Create an SSH deploy key</h2>
<p>If we want Travis-CI to push to our gh-pages branch, we'll need to provide it
with a deployment key.</p>
<p>First, create a new SSH key:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ ssh-keygen -t rsa -b 4096 -C <span class="hljs-string">"&lt;your email address&gt;"</span>
</code></pre>
<p>This will prompt you for where you want to put the new key files, and what they
should be named; I use descriptive names in these cases, such as the repository
name, and the selected encryption type: &quot;component_installer_rsa&quot; . Usually
<code>$HOME/.ssh/</code> is a good location to store them.</p>
<p>Next, we'll provide the public key to GitHub.  Open the public key generated
(usually <code>&lt;key name&gt;.pub</code> in a visual editor with clipboard support, and copy
the entire file. Then go to <code>https://github.com/&lt;your username or org&gt;/&lt;your repo&gt;/settings/keys</code>. On that page, click the button <code>Add deploy key</code>, give your
key a name (I used <code>&lt;repo name&gt; for Travis-CI</code>), and paste in the key where
indicated. Finally, click the box enabling write permissions; we want to be able
to push commits with this key! Confirm and save it.</p>
<p>Finally, we need to copy the <em>private</em> key into the project. Don't worry; we're
not going to commit it yet; in fact, we're going to tell git to omit it:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ cp <span class="hljs-variable">$HOME</span>/.ssh/&lt;repo&gt;_rsa .travis/build-key.pem
$ <span class="hljs-built_in">echo</span> <span class="hljs-string">".travis/build-key.pem"</span> &gt;&gt; .gitignore
</code></pre>
<p>At this point, we now have two files in <code>.travis/</code>, neither of which git will
commit to the repository: <code>phar-private.pem</code> and <code>build-key.pem</code>. And, somehow,
Travis-CI needs to get access to them.</p>
<h2>Archive and encrypt the secrets</h2>
<p>Travis-CI provides a number of facilities for encrypting secrets that you wish
to utilize during the build process. In our case, we need to provide <a href="https://docs.travis-ci.com/user/encrypting-files">encrypted
files</a>.</p>
<p>Interestingly, due to some issues with OpenSSL and the way the support is
implemented in Travis-CI, you can <a href="https://github.com/travis-ci/travis.rb/issues/239">only encrypt a <em>single</em>
file</a>. Thus, if you
have multiple files, you need <a href="https://docs.travis-ci.com/user/encrypting-files#Encrypting-multiple-files">create an archive of them and encrypt
that</a>.</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ <span class="hljs-built_in">cd</span> .travis
$ tar cvf secrets.tar *.pem
$ <span class="hljs-built_in">cd</span> ..
</code></pre>
<p>This will create the file <code>.travis/secrets.tar</code>.</p>
<p>Now, we need to encrypt the file. To do this, you will need to install the
<code>travis</code> gem:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ gem install travis
</code></pre>
<p>and then login:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ travis login
</code></pre>
<p>Once you've done that, you can encrypt the <code>secrets.tar</code> file:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ travis encrypt-file .travis/secrets.tar .travis/secrets.tar.enc --add
</code></pre>
<p>This will create a new file, <code>.travis/secrets.tar.enc</code>, and add an entry to your
<code>.travis.yml</code>'s <code>before_install</code> section that will decrypt the file; this means
that your code and scripts on Travis-CI can then rely on <code>.travis/secrets.tar</code>
being available.</p>
<blockquote>
<h3>Note for the Type-A personalities out there</h3>
<p>When you use the <code>--add</code> flag and <code>travis</code> rewrites your <code>.travis.yml</code> file,
it strips out any whitespace you've added.</p>
</blockquote>
<p>We'll add the <code>.travis/secrets.tar.enc</code> file to the repository, and omit
<code>.travis/secrets.tar</code>:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ git add .travis/secrets.tar.enc
$ <span class="hljs-built_in">echo</span> <span class="hljs-string">".travis/secrets.tar"</span> &gt;&gt; .gitignore
$ git add .gitignore
</code></pre>
<p>When a build is triggered on Travis-CI now, it will decrypt this file before any
of our build processes are triggered, allowing us access to those secrets!</p>
<h2>Write a deployment script</h2>
<p>Now that we have our secrets securely available on Travis-CI, we can figure out
what deployment might look like:</p>
<ul>
<li>We'll want to remove development-only dependencies.</li>
<li>We'll want to extract the secrets from the tarball.</li>
<li>We'll want to start the SSH agent with our deployment key.</li>
<li>We'll want to setup our Git identity. (In my experiments, I discovered that
GitHub rejected pushes from valid deployment keys that did not include a
full name and email.)</li>
<li>We'll need to add a git remote using the SSH-enabled repository path.</li>
<li>We'll want to fetch the Box Project PHAR file.</li>
<li>We'll want to create the PHAR using Box.</li>
<li>We'll need to generate a new version file from the re-generated PHAR.</li>
<li>We'll need to check out the gh-pages branch, and add the PHAR and version
file.</li>
<li>We'll need to push the changes to GitHub.</li>
</ul>
<p>I do all but the first step in a script, which I put in <code>bin/deploy.sh</code>:</p>
<pre><code class="language-bash hljs bash" data-lang="bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># Unpack secrets; -C ensures they unpack *in* the .travis directory</span>
tar xvf .travis/secrets.tar -C .travis

<span class="hljs-comment"># Setup SSH agent:</span>
<span class="hljs-built_in">eval</span> <span class="hljs-string">"<span class="hljs-variable">$(ssh-agent -s)</span>"</span> <span class="hljs-comment">#start the ssh agent</span>
chmod 600 .travis/build-key.pem
ssh-add .travis/build-key.pem

<span class="hljs-comment"># Setup git defaults:</span>
git config --global user.email <span class="hljs-string">"&lt;your email here&gt;"</span>
git config --global user.name <span class="hljs-string">"&lt;your name here&gt;"</span>

<span class="hljs-comment"># Add SSH-based remote to GitHub repo:</span>
git remote add deploy git@github.com:weierophinney/component-installer.git
git fetch deploy

<span class="hljs-comment"># Get box and build PHAR</span>
wget https://box-project.github.io/box2/manifest.json
BOX_URL=$(php bin/parse-manifest.php manifest.json)
rm manifest.json
wget -O box.phar <span class="hljs-variable">${BOX_URL}</span>
chmod 755 box.phar
./box.phar build -vv
<span class="hljs-comment"># Without the following step, we cannot checkout the gh-pages branch due to</span>
<span class="hljs-comment"># file conflicts:</span>
mv component-installer.phar component-installer.phar.tmp

<span class="hljs-comment"># Checkout gh-pages and add PHAR file and version:</span>
git checkout -b gh-pages deploy/gh-pages
mv component-installer.phar.tmp component-installer.phar
sha1sum component-installer.phar &gt; component-installer.phar.version
git add component-installer.phar component-installer.phar.version

<span class="hljs-comment"># Commit and push:</span>
git commit -m <span class="hljs-string">'Rebuilt phar'</span>
git push deploy gh-pages:gh-pages
</code></pre>
<p>You'll note that this script makes reference to <code>bin/parse-manifest.php</code>; this
is a PHP script that parses the version manifest file for Box to find the
download URL of the latest <code>box.phar</code>. It looks like this:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-meta">&lt;?php</span>
chdir(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/../'</span>);
$fallbackUrl = <span class="hljs-string">'https://github.com/box-project/box2/releases/download/2.6.0/box-2.6.0.phar'</span>;

<span class="hljs-keyword">if</span> (! <span class="hljs-keyword">isset</span>($argv[<span class="hljs-number">1</span>]) || ! is_file($argv[<span class="hljs-number">1</span>])) {
    <span class="hljs-keyword">return</span> $fallbackUrl;
}

$manifestJson = file_get_contents($argv[<span class="hljs-number">1</span>]);
$files = json_decode($manifestJson, <span class="hljs-keyword">true</span>);

<span class="hljs-keyword">if</span> (! is_array($files)) {
    <span class="hljs-keyword">echo</span> $fallbackUrl;
    <span class="hljs-keyword">exit</span>(<span class="hljs-number">0</span>);
}

<span class="hljs-keyword">foreach</span> ($files <span class="hljs-keyword">as</span> $file) {
    <span class="hljs-keyword">if</span> (! is_array($file) || ! <span class="hljs-keyword">isset</span>($file[<span class="hljs-string">'version'</span>])) {
        <span class="hljs-keyword">continue</span>;
    }

    <span class="hljs-keyword">if</span> (version_compare($file[<span class="hljs-string">'version'</span>], <span class="hljs-string">'2.6.0'</span>, <span class="hljs-string">'&gt;='</span>)) {
        <span class="hljs-keyword">echo</span> $file[<span class="hljs-string">'url'</span>];
        <span class="hljs-keyword">exit</span>(<span class="hljs-number">0</span>);
    }
}

<span class="hljs-keyword">echo</span> $fallbackUrl;
<span class="hljs-keyword">exit</span>(<span class="hljs-number">0</span>);
</code></pre>
<p>Essentially, it attempts to parse the manifest, and, on any failure, uses a
known-good URI. You could, of course, just use the known-good URI always.</p>
<blockquote>
<h3>Why not use the Box installer?</h3>
<p>I'm trying to demonstrate a secure toolchain. As <a href="https://archive.is/hDwaA">Pádraic outlines in his
post</a>, installer scripts can introduce remote code
execution vulnerabilities, particularly if directly piped to the PHP
executable. In this particular case, the Box <code>installer.php</code> is using an
insecure URI for downloading the <code>manifest.json</code> (I've proposed <a href="https://github.com/box-project/box2/pull/122">a
patch</a> for this). As such, I'm
downloading the manifest over SSL, manually parsing it, and then downloading
the PHAR file from the parsed results.</p>
</blockquote>
<p>Make the deployment script executable, add both scripts to your repository, and
commit!</p>
<h2>Add the script to travis</h2>
<p>Now we need to tell Travis-CI to execute this script. We want it to run:</p>
<ul>
<li>Only for one build environment. (No need to push multiple times for the same
commit!)</li>
<li>Only if the build is successful.</li>
<li>Only for builds on the master branch.</li>
<li>Only if the build is not for a pull request.</li>
</ul>
<p>If you used the <code>.travis.yml</code> file I provided earlier as a template, you likely
noted the section where I define the env variable <code>$EXECUTE_DEPLOYMENT</code>. This is
what enforces the first point (only run for one build environment).</p>
<p>For the second point, we're going to define an <code>after_success</code> section in the
configuration; this ensures it does not trigger if our other tasks fail (such as
unit tests, CS checks, etc.).</p>
<p>For the third and fourth points, Travis-CI provides some environment variables
to help us:</p>
<ul>
<li><code>$TRAVIS_BRANCH</code> indicates the branch. However, in the case of a pull request,
this will be the base branch against which the pull request was made. As such,
we also need:</li>
<li><code>$TRAVIS_PULL_REQUEST</code>. The value of this is the pull request ID when present;
otherwise, it's the string &quot;false&quot;.</li>
</ul>
<p>Putting it all together results in the following additions to the <code>.travis.yml</code>
file:</p>
<pre><code class="language-yaml hljs yaml" data-lang="yaml"><span class="hljs-attr">after_success:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">if</span> <span class="hljs-string">[[</span> <span class="hljs-string">$EXECUTE_DEPLOYMENT</span> <span class="hljs-string">==</span> <span class="hljs-string">'true'</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">$TRAVIS_BRANCH</span> <span class="hljs-string">==</span> <span class="hljs-string">'master'</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">$TRAVIS_PULL_REQUEST</span> <span class="hljs-string">==</span> <span class="hljs-string">'false'</span> <span class="hljs-string">]];</span> <span class="hljs-string">then</span> <span class="hljs-string">composer</span> <span class="hljs-string">install</span> <span class="hljs-string">--no-dev</span> <span class="hljs-string">;</span> <span class="hljs-string">fi</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">if</span> <span class="hljs-string">[[</span> <span class="hljs-string">$EXECUTE_DEPLOYMENT</span> <span class="hljs-string">==</span> <span class="hljs-string">'true'</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">$TRAVIS_BRANCH</span> <span class="hljs-string">==</span> <span class="hljs-string">'master'</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">$TRAVIS_PULL_REQUEST</span> <span class="hljs-string">==</span> <span class="hljs-string">'false'</span> <span class="hljs-string">]];</span> <span class="hljs-string">then</span> <span class="hljs-string">./bin/deploy.sh</span> <span class="hljs-string">;</span> <span class="hljs-string">fi</span>
</code></pre>
<p>Each line only executes if we are on the designated environment (we chose 5.6
for this example), on the &quot;master&quot; branch, for non-pull-request pushes. The
first line removes the development dependencies from the tree, and the second
executes our deployment script.</p>
<blockquote>
<h3>Note on after_success vs deploy</h3>
<p>Travis-CI has another event, <code>deploy</code>, which is often touted as the
appropriate place to perform, well, deployments, which is essentially what
we're doing in the above.</p>
<p>What I found, however, is that the workflow didn't work well when you have
cached or encrypted files.</p>
<p>The deploy event, when triggered, stashes changes, does a clean checkout, and
then tries to restore from the stash. What I observed was that my cached
composer files (the <code>composer.lock</code> file and <code>vendor/</code> directory) created
conflicts when applying the stash, which caused my deployment script to never
trigger. Another time I observed that the decrypted version of my secrets
disappeared with the new checkout.</p>
<p>If any readers have any feedback on this, I'd love to hear it!</p>
</blockquote>
<h2>Push and watch it work</h2>
<p>Hopefully, if you've been following along this far, you'll see that on your
next push with a successful build, your gh-pages branch will get a new commit,
with an updated PHAR and version file!</p>
<p>The workflow for consumers will then be:</p>
<ul>
<li>Go to your gh-pages site and download the PHAR file and public key.</li>
<li>Periodically execute <code>&lt;name of phar file&gt;.phar self-update</code> to update their
installation (assuming you named the self-update command &quot;self-update&quot;).</li>
<li>If desired, they can later rollback to a previous version using <code>&lt;name of phar file&gt;.phar rolback</code> (assuming you named the rollback command &quot;rollback&quot;).</li>
</ul>
<p>All of this can be done securely, because you've setup a secure workflow:</p>
<ul>
<li>The PHAR file, public key, and version file are all secured via TLS.</li>
<li>The PHAR file is signed using an OpenSSL private key, and can be verified
using its public complement.</li>
</ul>
<p>In your own workflow, you're only pushing <em>encrypted</em> secrets to the repository,
and the keys for those are known only to you and Travis-CI. (In fact, you only
&quot;know&quot; them through the <code>travis</code> gem!) If your deployment key is compromised,
you can revoke it from GitHub. If you feel the signing key has been compromised,
you can create a new one, and notify your users that they need to re-download
the PHAR and public key.</p>
<h2>Still under research</h2>
<p>While the above workflow is tested and works, I have one item I'm still unhappy
with: I'd really like it if the deployment could be delayed until I know <em>all</em>
environments have completed successfully. If anybody could assist me with that,
I'd love to hear from you!</p>
<h2>Updates</h2>
<p>Below is a list of updates made to this post since the time of writing:</p>
<ul>
<li>2015-12-15: Changed references to <code>composer update</code> to read <code>composer install</code>, per a comment from Christophe Coevoet.</li>
<li>2015-12-15: Changed OpenSSL key generation example to use 4096 bits instead of
2048, per a comment from sf_tristanb.</li>
<li>2015-12-17: Updated the <code>bin/deploy.sh</code> script to download the <code>box.phar</code>
securely, instead of using their installer script.</li>
</ul>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/2015-12-14-secure-phar-automation.html">Secure PHAR Automation</a> was originally
    published <time class="dt-published" datetime="2015-12-14T11:45:00-06:00">14 December 2015</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>OpenShift, ZF2, and Composer</title>
      <pubDate>Thu, 01 Nov 2012 15:25:00 -0500</pubDate>
      <link>https://mwop.net/blog/2012-11-01-openshift-zf2-composer.html</link>
      <guid>https://mwop.net/blog/2012-11-01-openshift-zf2-composer.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>I was recently shopping around for inexpensive cloud hosting; I want to try out
a couple of ideas that may or may not have much traffic, but which aren't
suited for my VPS setup (the excellent <a href="http://servergrove.com/">ServerGrove</a>);
additionally, I'm unsure how long I will maintain these projects. My budget for
this is quite small as a result; I'm already paying for hosting, and am quite
happy with it, so this is really for experimental stuff.</p>
<p>I considered Amazon, Orchestra.io, and a few others, but was concerned about
the idea of a ~$50/month cost for something I'm uncertain about.</p>
<p>When I asked in <a href="irc://irc.freenode.net/zftalk.dev">#zftalk.dev</a>, someone
suggested <a href="http://openshift.redhat.com/">OpenShift</a> as an idea, and
coincidentally, the very next day
<a href="http://www.zend.com/en/company/news/press/379_red-hat-expands-openshift-ecosystem-with-zend-partnership-to-offer-professional-grade-environment-for-php-developers">Zend announced a partnership with RedHat surrounding OpenShift</a>.
The stars were in alignment.</p>
<p>In the past month, in the few spare moments I've had (which included an
excellent OpenShift hackathon at ZendCon), I've created a quick application
that I've deployed and tested in OpenShift. These are my findings.</p>


<h2>ZF2</h2>
<p>I didn't really have to do anything different to have
<a href="http://framework.zend.com/">zf2</a> work; the standard <code>.htaccess</code> provided in
the skeleton application worked flawlessly the first time (I've worked with
some cloud environments where this is not the case).</p>
<p>The only frustration I had was the default directory structure OpenShift foists
upon us:</p>
<pre><code class="language-powershell hljs powershell" data-lang="powershell"><span class="hljs-keyword">data</span>/
libs/
misc/
php/
</code></pre>
<p>This is not terrible, by any stretch. However, it's attempting to dictate the
application structure, which I'm not terribly happy with — particularly as my
structure may vary based on the framework I'm using (or not!), and because I
may already have a project written that I simply want to deploy.</p>
<p>In particular, the <code>php</code> directory is galling — it's simply the document root.
Most frameworks I've used or seen call the equivalent directory <code>public</code>, or
<code>web</code>, or <code>html</code> — but never <code>php</code> (in large part because the only PHP file
under the document root in most frameworks is the <code>index.php</code> that acts as the
front controller). It would be nice if this were configurable.</p>
<p>This conflicts a bit with how a ZF2 app is structured. I ended up doing the
following:</p>
<ul>
<li>Removed <code>php</code> and symlinked my <code>public</code> directory to it.</li>
<li>Removed <code>libs</code> and symlinked my <code>vendor</code> directory to it.</li>
<li>Removed <code>misc</code> as I had no need to it.</li>
</ul>
<p>Nothing too big, thankfully — but problematic from the perspective of, &quot;I've
already developed this app, but now I have to make changes for it to work on a
specific cloud vendor.&quot;</p>
<h2>Composer</h2>
<p>My next question was how to use <a href="http://getcomposer.org/">Composer</a> during my
deployment process, and some some googling
<a href="https://openshift.redhat.com/community/content/support-for-git-clone-on-the-server-aka-support-php-composerphar">found some answers for me</a>.</p>
<p>Basically, I needed to create a <code>deploy</code> task that does two things:</p>
<ul>
<li>Unset the <code>GIT_DIR</code> environment variable. Evidently, the build process
operates as part of a git hook, and since Composer often uses git
repositories, this can lead to problems.</li>
<li>Change directory to <code>OPENSHIFT_REPO_DIR</code>, which is where the application root
(not document root!) lives.</li>
</ul>
<p>Once I did those, I could run my normal composer installation. The <code>deploy</code>
task looks like this:</p>
<pre><code class="language-bash hljs bash" data-lang="bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># .openshift/action_hooks/deploy</span>
( <span class="hljs-built_in">unset</span> GIT_DIR ; <span class="hljs-built_in">cd</span> <span class="hljs-variable">$OPENSHIFT_REPO_DIR</span> ; /usr/<span class="hljs-built_in">local</span>/zend/bin/php composer.phar install )
</code></pre>
<p>This leads into my next topic.</p>
<h2>Deployment</h2>
<p>First off, as you probably guessed from that last secton, there <strong>are</strong> hooks
for deployment — it doesn't have to be simply git. I like this, as I may have
additional things I want to do during deployment, such as retrieving and
installing site-specific configuration files, installing Composer-defined
dependencies (as already noted), etc.</p>
<p>Over all, this is pretty seamless, but it's not without issues. I've been told
that some of my issues are being worked on, so those I won't bring up here. The
ones that were a bit strange, and which caught me by surprise, though, were:</p>
<ul>
<li>Though the build process creates the site build from git, your <strong>submodules
are not updated recursively</strong>. This tripped me up, as I was using
<a href="https://github.com/EvanDotPro/EdpMarkdown">EdpMarkdown</a>, and had installed
it as a submodule. I ended up having to import it, and its own submodule,
directly into my project so that it would work.</li>
<li>I installed the <a href="http://www.mongodb.org/">MongoDB</a> cartridge. Ironically, it
was not then enabled in Zend Server, and I had to go do this. This should be
turnkey.</li>
<li><code>/usr/bin/php</code> is not the same as <code>/usr/local/zend/bin/php</code>. This makes no
sense to me if I've installed Zend Server as my base gear. Considering
they're different versions, this can be hugely misleading and lead to errors.
I understand there are reasons to have both — so simply be aware that if you
use the Zend Server gear, your tasks likely should use
<code>/usr/local/zend/bin/php</code>.</li>
</ul>
<h2>The good parts?</h2>
<ul>
<li><a href="https://openshift.redhat.com/community/faq/i-have-deployed-my-app-but-i-don%E2%80%99t-like-telling-people-to-visit-myapp-myusernamerhcloudcom-how-c">You can alias an application to a DNS
CNAME</a>
— meaning you can point your domain name to your OpenShift applications.
Awesome!</li>
<li>Simplicity of adding capabilities, such as Mongo, MySQL, Cron, and others. In
most cases, this is simply a &quot;click on the button&quot; and it's installed and
available.</li>
<li><a href="http://www.zend.com/en/products/server">Zend Server</a>. For most PHP
extensions, I can turn them on or off with a few mouse clicks. If I want
page-level caching, I don't have to do anything to my application; I can
simply setup some rules in the Zend Server interface and get on with it, and
enjoy tremendous boosts to performance. I used to enjoy taming and tuning
servers; most days anymore, I just want them to work.</li>
<li><a href="https://openshift.redhat.com/community/developers/remote-access">SSH</a> access
to the server, with a number of commands to which I've been given <code>sudoer</code>
access. If you're going to sandbox somebody, this is a fantastic way to do
it. Oh, also: SSH tunnels to services like Mongo and MySQL just work (via the
<code>rhc-port-forward</code> command).</li>
</ul>
<h2>Summary</h2>
<p>Over all, I'm quite pleased. While it took me a bit to find the various
incantations I needed, the service is quite flexible. For my needs, considering
I'm doing experimental stuff, the price can't be beat (the current developer
preview is free). Considering most stuff I do will fall into this or the basic
tier, and that most cartridges do not end up counting against your alotment of
gears, the pricing ($0.05/hour) is extremely competitive.</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/2012-11-01-openshift-zf2-composer.html">OpenShift, ZF2, and Composer</a> was originally
    published <time class="dt-published" datetime="2012-11-01T15:25:00-05:00">1 November 2012</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
  </channel>
</rss>
