<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Blog entries tagged security :: mwop.net</title>
  <updated>2025-01-15T14:31:20-06:00</updated>
  <generator uri="https://getlaminas.org" version="2">Laminas_Feed_Writer</generator>
  <link rel="alternate" type="text/html" href="https://mwop.net/blog/tag/security"/>
  <link rel="self" type="application/atom+xml" href="https://mwop.net/blog/tag/security/atom.xml"/>
  <id>https://mwop.net/blog/tag/security</id>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Fixing issues with Yubico's PAM U2F bindings in version 1.3.1]]></title>
    <published>2025-01-15T14:31:20-06:00</published>
    <updated>2025-01-15T14:31:20-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2025-01-15-pam-yubikey-1.3.1-fix.html"/>
    <id>https://mwop.net/blog/2025-01-15-pam-yubikey-1.3.1-fix.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I've been using a <xhtml:a href="https://www.yubico.com/products/yubikey-5-overview/">Yubikey</xhtml:a>
for years, now, and on each computer I use, I install their U2F
(Universal 2 Factor) bindings for the linux Pluggable
Authentication Modules (PAM) support, requiring usage of my Yubikey
for login and sudo access.</xhtml:p>
<xhtml:p>Today, I updated my work machine, and didn't even notice that
there were new pamu2fcfg and libpam-u2f packages, updating to
version 1.3.1; I never really care, as everything just works. But
when I came back to my machine after lunch, I was unable to login:
I'd provide my password, but my Yubikey wouldn't activate.</xhtml:p>
<xhtml:p>I tested it on my personal machine, and everything was working
fine. I tried pressing the key on my work machine, when in the
password field, and it pasted in the OTP code, so clearly there's
no USB issue.</xhtml:p>
<xhtml:p>So, after booting my rescue USB drive and disabling the U2F
support, I (a) discovered that I'd had updates for the PAM U2F
support earlier, and (b) searched for the phrase "yubikey pam u2f
1.3.1 breaks", which took me to <xhtml:a href="https://github.com/Yubico/pam-u2f/issues/330">this report</xhtml:a>.</xhtml:p>
<xhtml:p>The gist?</xhtml:p>
<xhtml:p><xhtml:strong>Due to a CVE, the PAM U2F bindings now require that the
<xhtml:code>u2f_keys</xhtml:code> file is writeable only by the
owner.</xhtml:strong></xhtml:p>
<xhtml:p>This can be accomplished pretty easily:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash"><xhtml:span class="hljs-comment"># If you have systemwide keys:</xhtml:span>
sudo chmod g-w,o-w /etc/yubico/u2f_keys
<xhtml:span class="hljs-comment"># If you have per-user keys:</xhtml:span>
chmod g-w,o-w <xhtml:span class="hljs-variable">$HOME</xhtml:span>/.config/Yubico/u2f_keys
</xhtml:code></xhtml:pre>
<xhtml:p>Once I did that, I re-enabled my PAM U2F bindings, rebooted, and
all worked fine again.</xhtml:p>
<xhtml:h2>Final thoughts</xhtml:h2>
<xhtml:p>I rarely think about permissions in my
<xhtml:code>$HOME/.config</xhtml:code> directory, but I'm well aware that
configuration for things like SSH and GPG require similar
permissions masks. I think it's great that Yubico is doing this,
but (a) it should have likely been like this all along, and (b)
they really should have provided some sort of tooling or messaging
with the update to help folks fix permissions issues before they
become a problem. The fact that I only found out when I was unable
to login to my machine was horrible, and I feel incredibly
fortunate and privileged that I (a) had a rescue USB drive handy,
and (b) the knowledge of what I needed to do to disable U2F so I
could access my machine. Not all their users will be in that
position.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2025-01-15-pam-yubikey-1.3.1-fix.html">Fixing
issues with Yubico's PAM U2F bindings in version 1.3.1</xhtml:a> was
originally published <xhtml:time class="dt-published" datetime="2025-01-15T14:31:20-06:00">15 January 2025</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Secure PHAR Automation]]></title>
    <published>2015-12-14T11:45:00-06:00</published>
    <updated>2015-12-17T09:00:00-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2015-12-14-secure-phar-automation.html"/>
    <id>https://mwop.net/blog/2015-12-14-secure-phar-automation.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>For a variety of reasons, I've been working on a utility that is
best distributed via <xhtml:a href="http://php.net/phar">PHAR</xhtml:a> file.
As has been <xhtml:a href="https://archive.is/hDwaA">noted by others</xhtml: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.</xhtml:p>
<xhtml:p>This is an account of my journey, as well as concrete steps you
can take to secure your own PHAR downloads.</xhtml:p>
<xhtml:h2>The Roadmap</xhtml:h2>
<xhtml:p>The steps outlined by Pádraic Brady in the afore-linked post
were essentially:</xhtml:p>
<xhtml:ul>
<xhtml:li>Distribute the PHAR over TLS-secured HTTPS.</xhtml:li>
<xhtml:li>Sign your PHAR with a private key.</xhtml:li>
<xhtml: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).</xhtml:li>
</xhtml:ul>
<xhtml:p>As such, I figured the plan should be:</xhtml:p>
<xhtml:ul>
<xhtml:li>Use <xhtml:a href="https://pages.github.com">GitHub Pages</xhtml:a> for
distribution. This gives me essentially free hosting, and free
TLS.</xhtml:li>
<xhtml:li>Create an OpenSSL key, use it to sign the package, and provide
the public key for download.</xhtml:li>
<xhtml:li>Have functionality built-in to the PHAR for updating and
rolling back.</xhtml:li>
<xhtml:li>Automate creation of the PHAR, as well as pushing it and the
version information to the site.</xhtml:li>
</xhtml:ul>
<xhtml:p>Seems simple enough, right?</xhtml:p>
<xhtml: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.</xhtml:p>
<xhtml:h2>Create an OpenSSL Key</xhtml:h2>
<xhtml:p>The first step is to create an OpenSSL private key. This will be
used to sign the packages.</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ openssl genrsa -des3 -out phar-private.pem 4096
</xhtml:code></xhtml:pre>
<xhtml:p>The above will prompt you for a passphrase, which is used to
encrypt the key.</xhtml:p>
<xhtml: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.</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ cp phar-private.pem phar-private.pem.passphrase-protected
$ openssl rsa -<xhtml:span class="hljs-keyword">in</xhtml:span> phar-private.pem -out phar-private-nopassphrase.pem
$ cp phar-private-nopassphrase.pem phar-private.pem
</xhtml:code></xhtml:pre>
<xhtml: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.</xhtml:p>
<xhtml:p>From here, create a <xhtml:code>.travis/</xhtml:code> subdirectory in your
project, put the private key in it, and then add that file to your
project <xhtml:code>.gitignore</xhtml:code>:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ mkdir .travis
$ mv phar-private.pem .travis/
$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">".travis/phar-private.pem"</xhtml:span> &gt;&gt; .gitignore
</xhtml:code></xhtml:pre>
<xhtml:p>We're telling git to ignore the key, as we don't want to push it
unencrypted to our repository!</xhtml:p>
<xhtml:h2>Use Box to create the PHAR</xhtml:h2>
<xhtml:p>Now that we have a key, we can think about creating our PHAR
file.</xhtml:p>
<xhtml:p>While PHP provides a <xhtml:a href="http://php.net/phar">ton of
functionality around PHARs</xhtml: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.</xhtml:p>
<xhtml:p>So, let others do the work for you. Specifically, the <xhtml:a href="https://box-project.github.io/box2/">Box Project</xhtml: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.</xhtml:p>
<xhtml:p>The link above details retrieving box, but the basics are:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ curl -LSs https://box-project.github.io/box2/installer.php | php
</xhtml:code></xhtml:pre>
<xhtml:p>which will leave an executable <xhtml:code>box.phar</xhtml:code> in the
working directory. I usually put this in my <xhtml:code>$PATH</xhtml:code> and
alias <xhtml:code>box</xhtml:code> to it.</xhtml:p>
<xhtml:p>Box, like many other PHP utilities, allows you to create either
a configuration file, or a "dist" configuration file. I like to do
the latter, as it then lets me copy it locally to provide
modifications/customizations. Here's a basic
<xhtml:code>box.json.dist</xhtml:code> file containing the options for creating
an OpenSSL-signed, gzipped, executable package:</xhtml:p>
<xhtml:pre><xhtml:code class="language-javascript hljs javascript" data-lang="javascript">{
  <xhtml:span class="hljs-string">"algorithm"</xhtml:span>: <xhtml:span class="hljs-string">"OPENSSL"</xhtml:span>,
  <xhtml:span class="hljs-string">"chmod"</xhtml:span>: <xhtml:span class="hljs-string">"0755"</xhtml:span>,
  <xhtml:span class="hljs-string">"compression"</xhtml:span>: <xhtml:span class="hljs-string">"GZ"</xhtml:span>,
  <xhtml:span class="hljs-string">"directories"</xhtml:span>: [
    <xhtml:span class="hljs-string">"src"</xhtml:span>
  ],
  <xhtml:span class="hljs-string">"files"</xhtml:span>: [
    <xhtml:span class="hljs-string">"LICENSE.md"</xhtml:span>
  ],
  <xhtml:span class="hljs-string">"finder"</xhtml:span>: [
    {
      <xhtml:span class="hljs-string">"name"</xhtml:span>: <xhtml:span class="hljs-string">"*.php"</xhtml:span>,
      <xhtml:span class="hljs-string">"exclude"</xhtml:span>: [
        <xhtml:span class="hljs-string">"tests"</xhtml:span>,
        <xhtml:span class="hljs-string">"test"</xhtml:span>
      ],
      <xhtml:span class="hljs-string">"in"</xhtml:span>: <xhtml:span class="hljs-string">"vendor"</xhtml:span>
    }
  ],
  <xhtml:span class="hljs-string">"git-version"</xhtml:span>: <xhtml:span class="hljs-string">"package_version"</xhtml:span>,
  <xhtml:span class="hljs-string">"intercept"</xhtml:span>: <xhtml:span class="hljs-literal">true</xhtml:span>,
  <xhtml:span class="hljs-string">"key"</xhtml:span>: <xhtml:span class="hljs-string">".travis/phar-private.pem"</xhtml:span>,
  <xhtml:span class="hljs-string">"main"</xhtml:span>: <xhtml:span class="hljs-string">"bin/command.php"</xhtml:span>,
  <xhtml:span class="hljs-string">"output"</xhtml:span>: <xhtml:span class="hljs-string">"command.phar"</xhtml:span>,
  <xhtml:span class="hljs-string">"stub"</xhtml:span>: <xhtml:span class="hljs-literal">true</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>Some notes on the various options:</xhtml:p>
<xhtml:ul>
<xhtml:li>"algorithm" and "key" go hand-in-hand. The algorithm indicates
what package signing algorithm to use, and the "key" is the path on
the filesystem to the key; relative paths are relative to the
<xhtml:code>box.json.dist</xhtml:code> file.</xhtml:li>
<xhtml:li>"compression" indicates the compression algorithm to use; in
this case, I used gzip.</xhtml:li>
<xhtml:li>"git-version" is a string to look for in files; technically, it
looks for <xhtml:code>@package_version@</xhtml:code>, and not just the string.
When discovered, it replaces that string with the sha1 of the most
recent commit.</xhtml:li>
<xhtml:li>"directories" is used to specify directories where all files
should be included; "files" is used to specify individual files to
include. Usually I use "files" for anything not in "directories",
like the LICENSE file.</xhtml:li>
<xhtml:li>"finder" can be used in a similar fashion to "directories" and
"files", but gives you the ability to provide filtering rules; it
uses the <xhtml:a href="http://symfony.com/doc/current/components/finder.html">Symfony
Finder component</xhtml: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 <xhtml:code>vendor/</xhtml:code>
directory, but to only include PHP files, and to exclude files in
the directories "test" or "tests". Doing so greatly reduces the
size of the generated PHAR.</xhtml:li>
<xhtml:li>"main" 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.</xhtml:li>
<xhtml:li>"stub", when <xhtml:code>true</xhtml:code>, indicates thatthe default stub
based on the "main" 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 <xhtml:code>true</xhtml:code> when I'm
having it encapsulate a command-line script.</xhtml:li>
<xhtml:li>"output" is the name of the file to generate.</xhtml:li>
<xhtml:li>"chmod" indicates the file mode mask to set on the generated
file.</xhtml:li>
</xhtml:ul>
<xhtml:p>Obviously, configure this according to your needs, and add or
remove configuration as befits your project. The <xhtml:a href="https://github.com/box-project/box2/blob/2.0/res/schema.json">schema
file</xhtml:a> is your friend when determining what should be included;
much of the information is also available via <xhtml:code>box.phar help
build</xhtml:code>.</xhtml:p>
<xhtml:p>Once you have created the file, you can attempt a build:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ box build -vv
</xhtml:code></xhtml:pre>
<xhtml:p>(<xhtml:code>-vv</xhtml:code> indicates "verbosity"; I do this so I can see
errors if they occur.)</xhtml:p>
<xhtml:blockquote>
<xhtml:h3>Make the build leaner!</xhtml:h3>
<xhtml:p>The command may take a while. In fact, if you have a lot of
dependencies, it <xhtml:em>will</xhtml:em> take a while. I found it was best to
strip any development-only dependencies prior to running a
build:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ composer install --no-dev
</xhtml:code></xhtml:pre>
<xhtml:p>I find this results in a far faster build, with a much smaller
file size.</xhtml:p>
</xhtml:blockquote>
<xhtml:p>When the build is successful, you'll end up with two files:</xhtml:p>
<xhtml:ul>
<xhtml:li><xhtml:code>command.phar</xhtml:code></xhtml:li>
<xhtml:li><xhtml:code>command.phar.pubkey</xhtml:code></xhtml:li>
</xhtml:ul>
<xhtml:p>The name will be based on what you specified for "output" in the
configuration. The second file is the OpenSSL public key derived
from your private key. Users will need to have <xhtml:em>both</xhtml: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!</xhtml:p>
<xhtml:p>Now that you have the files, add them to your
<xhtml:code>.gitignore</xhtml:code>:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">"command.phar"</xhtml:span> &gt;&gt; .gitignore
$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">"command.phar.pubkey"</xhtml:span> &gt;&gt; .gitignore
</xhtml:code></xhtml:pre>
<xhtml: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.</xhtml:p>
<xhtml:h2>Generate a version file</xhtml:h2>
<xhtml:p>Now that we have the PHAR, we need to provide a way of
indicating what <xhtml:em>version</xhtml:em> we have. The simplest way to do
that is to take its sha1sum and write it to a file:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ sha1sum command.phar &gt; command.phar.version
</xhtml:code></xhtml:pre>
<xhtml: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:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">"command.phar.version"</xhtml:span> &gt;&gt; .gitignore
</xhtml:code></xhtml:pre>
<xhtml:h2>Create the gh-pages branch</xhtml:h2>
<xhtml: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.</xhtml:p>
<xhtml: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
<xhtml:code>gh-pages</xhtml:code> branch. The obvious conclusion is: let's add
that branch to our current repository!</xhtml:p>
<xhtml:p>That said, the new branch really shouldn't have the build tools
and code.</xhtml:p>
<xhtml:p>First, let's make sure you've committed everything you need on
the master branch:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git add .gitignore box.json.dist
$ git commit -m <xhtml:span class="hljs-string">'Build tools for PHAR file'</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>Now we can create what's called an "orphan" branch — a branch
with no history and no parents:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git checkout --orphan gh-pages
$ git rm -rf .
</xhtml:code></xhtml:pre>
<xhtml:p>We perform the <xhtml:code>git rm -rf .</xhtml: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!</xhtml:p>
<xhtml:p>So, let's add them:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git add composer.phar*
</xhtml:code></xhtml:pre>
<xhtml: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 <xhtml:code>index.html</xhtml: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 <xhtml:a href="https://getbootstrap.com">Bootstrap</xhtml:a> from a CDN for this, which
gives a reasonable default look for the package.</xhtml:p>
<xhtml:p>Once all your files are added, execute:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git commit -m <xhtml:span class="hljs-string">'Initial gh-pages files'</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>and then push to your GitHub repository.</xhtml:p>
<xhtml:p>Within a minute or so, you should be able to browse to
<xhtml:code>https://&lt;your username or org&gt;.github.io/&lt;your
repo&gt;/</xhtml:code>.</xhtml:p>
<xhtml:h2>Write self-update/rollback commands</xhtml:h2>
<xhtml:p>Now that we have a process for creating a PHAR, how will users
update or rollback?</xhtml:p>
<xhtml:p>Fortunately, Pádraic has a solution for that as well: <xhtml:a href="https://github.com/padraic/phar-updater">PHAR Updater</xhtml: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.</xhtml:p>
<xhtml:p>First, add the utility to your package:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ composer require padraic/phar-updater
</xhtml:code></xhtml:pre>
<xhtml:p>Regardless of how you write your console commands in PHP, your
self-update command will execute something like the following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-keyword">use</xhtml:span> <xhtml:span class="hljs-title">Humbug</xhtml:span>\<xhtml:span class="hljs-title">SelfUpdate</xhtml:span>\<xhtml:span class="hljs-title">Updater</xhtml:span>;

$updater = <xhtml:span class="hljs-keyword">new</xhtml:span> Updater();
$updater-&gt;getStrategy()-&gt;setPharUrl($urlToGithubPagesPharFile);
$updater-&gt;getStrategy()-&gt;setVersionUrl($urlToGithubPagesVersionFile);
<xhtml:span class="hljs-keyword">try</xhtml:span> {
    $result = $updater-&gt;update();
    <xhtml:span class="hljs-keyword">if</xhtml:span> (! $result) {
        <xhtml:span class="hljs-comment">// No update needed!</xhtml:span>
        <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
    }
    $new = $updater-&gt;getNewVersion();
    $old = $updater-&gt;getOldVersion();
    printf(<xhtml:span class="hljs-string">'Updated from %s to %s'</xhtml:span>, $old, $new);
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
} <xhtml:span class="hljs-keyword">catch</xhtml:span> (\<xhtml:span class="hljs-keyword">Exception</xhtml:span> $e) {
    <xhtml:span class="hljs-comment">// Report an error!</xhtml:span>
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">1</xhtml:span>);
}
</xhtml:code></xhtml:pre>
<xhtml: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 <xhtml:code>&lt;name of phar&gt;.pubkey</xhtml:code>. If the version is
different, it updates; if not, it doesn't. Exceptions typically
occur for things like:</xhtml:p>
<xhtml:ul>
<xhtml:li>File permissions.</xhtml:li>
<xhtml:li>Inability to reach the remote PHAR or version files.</xhtml:li>
<xhtml:li>Inability to validate the downloaded PHAR.</xhtml:li>
<xhtml:li>Inability to perform TLS negotation.</xhtml:li>
</xhtml:ul>
<xhtml: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 <xhtml:a href="http://curl.haxx.se/ca/cacert.pem">http://curl.haxx.se/ca/cacert.pem</xhtml:a>
and setting <xhtml:code>openssl.cafile=/path/to/cacert.pem</xhtml:code> in your
<xhtml:code>php.ini</xhtml:code> file.</xhtml:p>
<xhtml:p>So, self-update is taken care of; what about rollback?</xhtml:p>
<xhtml:p>When a self-update is performed using padraic/phar-updater, it
writes the original PHAR to <xhtml:code>command-old.phar</xhtml:code> in the
same directory as the original PHAR file. If that file is
available, you can write a rollback routine like the following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-keyword">use</xhtml:span> <xhtml:span class="hljs-title">Humbug</xhtml:span>\<xhtml:span class="hljs-title">SelfUpdate</xhtml:span>\<xhtml:span class="hljs-title">Updater</xhtml:span>;

$updater = <xhtml:span class="hljs-keyword">new</xhtml:span> Updater();
<xhtml:span class="hljs-keyword">try</xhtml:span> {
    $result = $updater-&gt;rollback();
    <xhtml:span class="hljs-keyword">if</xhtml:span> (! $result) {
        <xhtml:span class="hljs-comment">// report failure!</xhtml:span>
        <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">1</xhtml:span>);
    }
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
} <xhtml:span class="hljs-keyword">catch</xhtml:span> (\<xhtml:span class="hljs-keyword">Exception</xhtml:span> $e) {
    <xhtml:span class="hljs-comment">// Report an error!</xhtml:span>
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">1</xhtml:span>);
}
</xhtml:code></xhtml:pre>
<xhtml:p>Again, quite easy!</xhtml:p>
<xhtml:p>Checkout your project's <xhtml:code>master</xhtml:code> branch, add the
above commands, and re-build your PHAR…</xhtml:p>
<xhtml: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!</xhtml:p>
<xhtml:h2>Enable Travis-CI for the repository</xhtml:h2>
<xhtml:p>How do we automate? Via continuous integration and deployment,
of course!</xhtml:p>
<xhtml:p>For this step, I'm choosing <xhtml:a href="https://travis-ci.org">Travis-CI</xhtml: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.</xhtml:p>
<xhtml:p>As a CI service, it provides a number of stages that trigger,
often conditionally. We're going to use one of these,
<xhtml:code>after_success</xhtml:code>, to build the PHAR, update the version
file, and push them to our gh-pages branch.</xhtml:p>
<xhtml:p>First, though, we need to enable Travis-CI for our repository.
You will need to do the following:</xhtml:p>
<xhtml:ul>
<xhtml:li>Register for a Travis-CI account if you haven't already. The
homepage will guide you there.</xhtml:li>
<xhtml: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).</xhtml:li>
<xhtml: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 "Sync" button next
to where your name appears above the repository list.)</xhtml:li>
</xhtml:ul>
<xhtml:p>From here, you should add a <xhtml:code>.travis.yml</xhtml:code> file to
your project, if you haven't already. Here's a template:</xhtml:p>
<xhtml:pre><xhtml:code class="language-yaml hljs yaml" data-lang="yaml"><xhtml:span class="hljs-attr">sudo:</xhtml:span> <xhtml:span class="hljs-literal">false</xhtml:span>
<xhtml:span class="hljs-attr">language:</xhtml:span> <xhtml:span class="hljs-string">php</xhtml:span>

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

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

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

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

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

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

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

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

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

<xhtml:span class="hljs-comment"># Get box and build PHAR</xhtml: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 <xhtml:span class="hljs-variable">${BOX_URL}</xhtml:span>
chmod 755 box.phar
./box.phar build -vv
<xhtml:span class="hljs-comment"># Without the following step, we cannot checkout the gh-pages branch due to</xhtml:span>
<xhtml:span class="hljs-comment"># file conflicts:</xhtml:span>
mv component-installer.phar component-installer.phar.tmp

<xhtml:span class="hljs-comment"># Checkout gh-pages and add PHAR file and version:</xhtml: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

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

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

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

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

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

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

<xhtml:span class="hljs-keyword">echo</xhtml:span> $fallbackUrl;
<xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
</xhtml:code></xhtml:pre>
<xhtml: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.</xhtml:p>
<xhtml:blockquote>
<xhtml:h3>Why not use the Box installer?</xhtml:h3>
<xhtml:p>I'm trying to demonstrate a secure toolchain. As <xhtml:a href="https://archive.is/hDwaA">Pádraic outlines in his post</xhtml:a>,
installer scripts can introduce remote code execution
vulnerabilities, particularly if directly piped to the PHP
executable. In this particular case, the Box
<xhtml:code>installer.php</xhtml:code> is using an insecure URI for downloading
the <xhtml:code>manifest.json</xhtml:code> (I've proposed <xhtml:a href="https://github.com/box-project/box2/pull/122">a patch</xhtml: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.</xhtml:p>
</xhtml:blockquote>
<xhtml:p>Make the deployment script executable, add both scripts to your
repository, and commit!</xhtml:p>
<xhtml:h2>Add the script to travis</xhtml:h2>
<xhtml:p>Now we need to tell Travis-CI to execute this script. We want it
to run:</xhtml:p>
<xhtml:ul>
<xhtml:li>Only for one build environment. (No need to push multiple times
for the same commit!)</xhtml:li>
<xhtml:li>Only if the build is successful.</xhtml:li>
<xhtml:li>Only for builds on the master branch.</xhtml:li>
<xhtml:li>Only if the build is not for a pull request.</xhtml:li>
</xhtml:ul>
<xhtml:p>If you used the <xhtml:code>.travis.yml</xhtml:code> file I provided earlier
as a template, you likely noted the section where I define the env
variable <xhtml:code>$EXECUTE_DEPLOYMENT</xhtml:code>. This is what enforces
the first point (only run for one build environment).</xhtml:p>
<xhtml:p>For the second point, we're going to define an
<xhtml:code>after_success</xhtml:code> section in the configuration; this
ensures it does not trigger if our other tasks fail (such as unit
tests, CS checks, etc.).</xhtml:p>
<xhtml:p>For the third and fourth points, Travis-CI provides some
environment variables to help us:</xhtml:p>
<xhtml:ul>
<xhtml:li><xhtml:code>$TRAVIS_BRANCH</xhtml: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:</xhtml:li>
<xhtml:li><xhtml:code>$TRAVIS_PULL_REQUEST</xhtml:code>. The value of this is the
pull request ID when present; otherwise, it's the string
"false".</xhtml:li>
</xhtml:ul>
<xhtml:p>Putting it all together results in the following additions to
the <xhtml:code>.travis.yml</xhtml:code> file:</xhtml:p>
<xhtml:pre><xhtml:code class="language-yaml hljs yaml" data-lang="yaml"><xhtml:span class="hljs-attr">after_success:</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">if</xhtml:span> <xhtml:span class="hljs-string">[[</xhtml:span> <xhtml:span class="hljs-string">$EXECUTE_DEPLOYMENT</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'true'</xhtml:span> <xhtml:span class="hljs-string">&amp;&amp;</xhtml:span> <xhtml:span class="hljs-string">$TRAVIS_BRANCH</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'master'</xhtml:span> <xhtml:span class="hljs-string">&amp;&amp;</xhtml:span> <xhtml:span class="hljs-string">$TRAVIS_PULL_REQUEST</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'false'</xhtml:span> <xhtml:span class="hljs-string">]];</xhtml:span> <xhtml:span class="hljs-string">then</xhtml:span> <xhtml:span class="hljs-string">composer</xhtml:span> <xhtml:span class="hljs-string">install</xhtml:span> <xhtml:span class="hljs-string">--no-dev</xhtml:span> <xhtml:span class="hljs-string">;</xhtml:span> <xhtml:span class="hljs-string">fi</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">if</xhtml:span> <xhtml:span class="hljs-string">[[</xhtml:span> <xhtml:span class="hljs-string">$EXECUTE_DEPLOYMENT</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'true'</xhtml:span> <xhtml:span class="hljs-string">&amp;&amp;</xhtml:span> <xhtml:span class="hljs-string">$TRAVIS_BRANCH</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'master'</xhtml:span> <xhtml:span class="hljs-string">&amp;&amp;</xhtml:span> <xhtml:span class="hljs-string">$TRAVIS_PULL_REQUEST</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'false'</xhtml:span> <xhtml:span class="hljs-string">]];</xhtml:span> <xhtml:span class="hljs-string">then</xhtml:span> <xhtml:span class="hljs-string">./bin/deploy.sh</xhtml:span> <xhtml:span class="hljs-string">;</xhtml:span> <xhtml:span class="hljs-string">fi</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>Each line only executes if we are on the designated environment
(we chose 5.6 for this example), on the "master" branch, for
non-pull-request pushes. The first line removes the development
dependencies from the tree, and the second executes our deployment
script.</xhtml:p>
<xhtml:blockquote>
<xhtml:h3>Note on after_success vs deploy</xhtml:h3>
<xhtml:p>Travis-CI has another event, <xhtml:code>deploy</xhtml:code>, which is often
touted as the appropriate place to perform, well, deployments,
which is essentially what we're doing in the above.</xhtml:p>
<xhtml:p>What I found, however, is that the workflow didn't work well
when you have cached or encrypted files.</xhtml:p>
<xhtml: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 <xhtml:code>composer.lock</xhtml:code>
file and <xhtml:code>vendor/</xhtml: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.</xhtml:p>
<xhtml:p>If any readers have any feedback on this, I'd love to hear
it!</xhtml:p>
</xhtml:blockquote>
<xhtml:h2>Push and watch it work</xhtml:h2>
<xhtml: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!</xhtml:p>
<xhtml:p>The workflow for consumers will then be:</xhtml:p>
<xhtml:ul>
<xhtml:li>Go to your gh-pages site and download the PHAR file and public
key.</xhtml:li>
<xhtml:li>Periodically execute <xhtml:code>&lt;name of phar file&gt;.phar
self-update</xhtml:code> to update their installation (assuming you named
the self-update command "self-update").</xhtml:li>
<xhtml:li>If desired, they can later rollback to a previous version using
<xhtml:code>&lt;name of phar file&gt;.phar rolback</xhtml:code> (assuming you
named the rollback command "rollback").</xhtml:li>
</xhtml:ul>
<xhtml:p>All of this can be done securely, because you've setup a secure
workflow:</xhtml:p>
<xhtml:ul>
<xhtml:li>The PHAR file, public key, and version file are all secured via
TLS.</xhtml:li>
<xhtml:li>The PHAR file is signed using an OpenSSL private key, and can
be verified using its public complement.</xhtml:li>
</xhtml:ul>
<xhtml:p>In your own workflow, you're only pushing <xhtml:em>encrypted</xhtml:em>
secrets to the repository, and the keys for those are known only to
you and Travis-CI. (In fact, you only "know" them through the
<xhtml:code>travis</xhtml: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.</xhtml:p>
<xhtml:h2>Still under research</xhtml:h2>
<xhtml: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 <xhtml:em>all</xhtml:em> environments have completed
successfully. If anybody could assist me with that, I'd love to
hear from you!</xhtml:p>
<xhtml:h2>Updates</xhtml:h2>
<xhtml:p>Below is a list of updates made to this post since the time of
writing:</xhtml:p>
<xhtml:ul>
<xhtml:li>2015-12-15: Changed references to <xhtml:code>composer update</xhtml:code>
to read <xhtml:code>composer install</xhtml:code>, per a comment from
Christophe Coevoet.</xhtml:li>
<xhtml:li>2015-12-15: Changed OpenSSL key generation example to use 4096
bits instead of 2048, per a comment from sf_tristanb.</xhtml:li>
<xhtml:li>2015-12-17: Updated the <xhtml:code>bin/deploy.sh</xhtml:code> script to
download the <xhtml:code>box.phar</xhtml:code> securely, instead of using their
installer script.</xhtml:li>
</xhtml:ul>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2015-12-14-secure-phar-automation.html">Secure
PHAR Automation</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2015-12-14T11:45:00-06:00">14 December
2015</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by
<xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Enabling VPN split tunnel with NetworkManager]]></title>
    <published>2009-08-31T15:34:37-05:00</published>
    <updated>2009-08-31T15:34:37-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/223-Enabling-VPN-split-tunnel-with-NetworkManager.html"/>
    <id>https://mwop.net/blog/223-Enabling-VPN-split-tunnel-with-NetworkManager.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I've been using <xhtml:a href="http://projects.gnome.org/NetworkManager/">NetworkManager</xhtml:a> for
some time now, and appreciate how easy it makes both connecting to
wifi as well as VPNs. That said, I've had an issue with it that I
only resolved today.</xhtml:p>
<xhtml:p>When working from home, I prefer to use a VPN split tunnel setup
— I'm behind a firewall all the time, and it's useful to be able to
run virtual machines while still connected to my VPN (e.g., when
doing training or webinar sessions). However, I noticed some months
ago that this wasn't working. I assumed at first it was a change in
our network setup, but others reported that the split tunnel was
working fine. It's been particularly problematic when on IRC — if
the VPN drops, I lose my IRC connection, meaning I have to
re-connect and re-claim my nick.</xhtml:p>
<xhtml:p>So, I did some searching, and found an interesting setting. In
NetworkManager, "Configure..." then "Edit" your VPN connection, and
navigate to the "IPv4 Settings" tab. Once there, click the button
that says "Routes..." and select the checkbox next to "Use this
connection only for resources on its network". Press Ok to close
the dialog, then "Apply" to exit out of the VPN configuration.
Re-connect to the VPN, and you should be all set.</xhtml:p>
<xhtml:p><xhtml:em>Note: this will only work if your VPN server is configured
to allow split tunnels. Additionally, only do so if you are behind
a firewall. Practice safe networking.</xhtml:em></xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/223-Enabling-VPN-split-tunnel-with-NetworkManager.html">
Enabling VPN split tunnel with NetworkManager</xhtml:a> was originally
published <xhtml:time class="dt-published" datetime="2009-08-31T15:34:37-05:00">31 August 2009</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
</feed>
