<?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 security :: mwop.net</title>
    <description>Blog entries tagged security :: mwop.net</description>
    <pubDate>Wed, 15 Jan 2025 14:31:20 -0600</pubDate>
    <generator>Laminas_Feed_Writer 2 (https://getlaminas.org)</generator>
    <link>https://mwop.net/blog/tag/security</link>
    <atom:link rel="self" type="application/rss+xml" href="https://mwop.net/blog/tag/security/rss.xml"/>
    <item>
      <title>Fixing issues with Yubico's PAM U2F bindings in version 1.3.1</title>
      <pubDate>Wed, 15 Jan 2025 14:31:20 -0600</pubDate>
      <link>https://mwop.net/blog/2025-01-15-pam-yubikey-1.3.1-fix.html</link>
      <guid>https://mwop.net/blog/2025-01-15-pam-yubikey-1.3.1-fix.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>I've been using a <a href="https://www.yubico.com/products/yubikey-5-overview/">Yubikey</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.</p>
<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.</p>


<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.</p>
<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 &quot;yubikey pam u2f 1.3.1 breaks&quot;, which took me to <a href="https://github.com/Yubico/pam-u2f/issues/330">this report</a>.</p>
<p>The gist?</p>
<p><strong>Due to a CVE, the PAM U2F bindings now require that the <code>u2f_keys</code> file is writeable only by the owner.</strong></p>
<p>This can be accomplished pretty easily:</p>
<pre><code class="language-bash hljs bash" data-lang="bash"><span class="hljs-comment"># If you have systemwide keys:</span>
sudo chmod g-w,o-w /etc/yubico/u2f_keys
<span class="hljs-comment"># If you have per-user keys:</span>
chmod g-w,o-w <span class="hljs-variable">$HOME</span>/.config/Yubico/u2f_keys
</code></pre>
<p>Once I did that, I re-enabled my PAM U2F bindings, rebooted, and all worked fine again.</p>
<h2>Final thoughts</h2>
<p>I rarely think about permissions in my <code>$HOME/.config</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.</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/2025-01-15-pam-yubikey-1.3.1-fix.html">Fixing issues with Yubico&#039;s PAM U2F bindings in version 1.3.1</a> was originally
    published <time class="dt-published" datetime="2025-01-15T14:31:20-06:00">15 January 2025</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>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>Enabling VPN split tunnel with NetworkManager</title>
      <pubDate>Mon, 31 Aug 2009 15:34:37 -0500</pubDate>
      <link>https://mwop.net/blog/223-Enabling-VPN-split-tunnel-with-NetworkManager.html</link>
      <guid>https://mwop.net/blog/223-Enabling-VPN-split-tunnel-with-NetworkManager.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>I've been using <a href="http://projects.gnome.org/NetworkManager/">NetworkManager</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.</p>
<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.</p>
<p>So, I did some searching, and found an interesting setting. In NetworkManager,
&quot;Configure...&quot; then &quot;Edit&quot; your VPN connection, and navigate to the &quot;IPv4
Settings&quot; tab. Once there, click the button that says &quot;Routes...&quot; and select the
checkbox next to &quot;Use this connection only for resources on its network&quot;. Press
Ok to close the dialog, then &quot;Apply&quot; to exit out of the VPN configuration.
Re-connect to the VPN, and you should be all set.</p>
<p><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.</em></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/223-Enabling-VPN-split-tunnel-with-NetworkManager.html">Enabling VPN split tunnel with NetworkManager</a> was originally
    published <time class="dt-published" datetime="2009-08-31T15:34:37-05:00">31 August 2009</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>
