<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Tammer Saleh</title>
  <id>http://tammersaleh.com/posts</id>
  <link href="http://tammersaleh.com/"/>
  <link href="http://tammersaleh.com/posts.atom" rel="self"/>
  <updated>2016-05-24T10:44:00+00:00</updated>
  <author>
    <name>Tammer Saleh</name>
  </author>
  <entry>
    <title>A Modern Cloud Vocabulary</title>
    <link rel="alternate" href="http://tammersaleh.com/posts/a-modern-cloud-vocabulary/"/>
    <id>http://tammersaleh.com/posts/a-modern-cloud-vocabulary/</id>
    <published>2016-05-24T10:44:00+00:00</published>
    <updated>2020-06-19T14:40:27+00:00</updated>
    <author>
      <name>Tammer Saleh</name>
    </author>
    <content type="html">&lt;p&gt;The Cloud is a quantum flux that pushes the limits of the definition of "eventually consistent".  I've seen things you people wouldn't believe.  VMs returning from a deprovision command &lt;em&gt;a year later&lt;/em&gt;.  Entire regions destroyed by a router misconfig.  API calls returning completely different results ten times in a row.  Networks on fire off the shoulder of Orion.&lt;/p&gt;

&lt;p&gt;Sorry… got carried away.&lt;/p&gt;

&lt;p&gt;In any case, &lt;strong&gt;the Cloud is Chaos&lt;/strong&gt;. Cloud-resilient systems must adhere to a new vocabulary in order to withstand it, but many developers have never been exposed to these concepts.  They have their trade-offs, but systems built using these patterns are stronger, more resilient, and are overall easier to reason about.&lt;/p&gt;

&lt;h4 id="declarative-vs-imperative"&gt;Declarative (vs Imperative)&lt;/h4&gt;

&lt;p&gt;When I was a kid, just learning about computers and programming and such, my teacher asked us to describe the precise steps necessary to make a sandwich.  She then paired us up, had us perform those steps verbatim, and sat back and cackled as we made disasters of our lunch.  She was imparting an important lesson about how we interact with computers: Their power is that they do exactly what we tell them to do, their fundamental flaw is the same.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;imperative&lt;/strong&gt; model.  We tell the machine, step by step, what actions to perform.  We cross our fingers and hope that these actions will result in a sandwich.  If our instructions are wrong, then we end up with something else.  If our assumptions were wrong, and the mayo was on the left while the peanut butter was on the right, then we end up with something else.&lt;/p&gt;

&lt;p&gt;Imperative systems work well when the conditions are controlled and predictable.  If you know &lt;code&gt;i&lt;/code&gt; started at &lt;code&gt;1&lt;/code&gt;, then you know &lt;code&gt;i++&lt;/code&gt; will result in &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Imperative systems make &lt;strong&gt;actual&lt;/strong&gt; and &lt;strong&gt;desired&lt;/strong&gt; state &lt;em&gt;implicit&lt;/em&gt;, and make moving between them &lt;em&gt;explicit&lt;/em&gt;.  In that way, they are the opposite of declarative systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declarative&lt;/strong&gt; systems make actual and desired states first class concepts, and take the responsibility of bridging the gap between them (which we call &lt;strong&gt;the delta&lt;/strong&gt;).  We interact with these systems by telling them how the world is &lt;em&gt;already supposed to be&lt;/em&gt;, and watch as they scramble to catch up.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I should have a sandwich in front of me.&lt;/p&gt;

  &lt;p&gt;Crap! You're right - there should be a sandwich there!  Let me make one!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Declarative systems are much harder to build.  They have to be smart enough to find their own path to desired, routing around any obstacles in the way.  They can't make assumptions, and instead have to be able to discover the actual state of the world.  They're also less predictable.  How are we getting there?  Who knows?  Who cares?  Look - we're here!  And they can be verbose.  Instead of instructing the system to "add mayo" to fix your sandwich, you have to declare the entire sandwich all over again (mayo included).&lt;/p&gt;

&lt;p&gt;If imperative systems thrive in controlled environments, declarative systems excel in chaos.&lt;/p&gt;

&lt;p&gt;(The Cloud is chaos)&lt;/p&gt;

&lt;p&gt;Declarative systems are one piece of the puzzle in resiliency.  Another is…&lt;/p&gt;

&lt;h4 id="convergence"&gt;Convergence&lt;/h4&gt;

&lt;p&gt;When I think of convergence, I think of &lt;a href="https://en.wikipedia.org/wiki/The_Oak_and_the_Reed"&gt;the parable of the oak and the reed&lt;/a&gt;.  The oak is strong but rigid, while the reed is weak but survives the storm.&lt;/p&gt;

&lt;p&gt;(The Cloud is that storm)&lt;/p&gt;

&lt;p class="img_right"&gt;&lt;img src="/posts/a-modern-cloud-vocabulary/useless_box.gif" alt="Image" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Convergence"&gt;Convergence&lt;/a&gt; is a property of a system that says "no matter how hard you blow, I'll eventually be standing again."  Convergence goes hand in hand with declarative systems, as a convergent system needs to understand an explicit desired state.  Convergence works by continually monitoring the delta between actual and desired state and triggering the declarative system when that delta exists.  Often this works by taking a declarative system and simply running it in a loop.&lt;/p&gt;

&lt;h4 id="idempotence"&gt;Idempotence&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning"&gt;Idempotence&lt;/a&gt; means an action has the same effect no matter if it's called once or many times.  Mathematically speaking, that means &lt;code&gt;f(f(x)) == f(x)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Idempotence is important in cloudy systems such as distributed databases.  Operating in the Cloud means operating in chaos (see a theme forming?), where messages may or may not make it to their destination.  We need to know that we can safely run the same command twice and get the desired result.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;x = 5&lt;/code&gt; is idempotent, but &lt;code&gt;x++&lt;/code&gt; isn't.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;HTTP PUT&lt;/code&gt; is idempotent, but &lt;code&gt;HTTP POST&lt;/code&gt; isn't.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;echo "foo" &amp;gt; bar&lt;/code&gt; is idempotent, but &lt;code&gt;echo "foo" &amp;gt;&amp;gt; bar&lt;/code&gt; isn't.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Declarative systems are inherently idempotent.&lt;/p&gt;

&lt;h4 id="immutability"&gt;Immutability&lt;/h4&gt;

&lt;p&gt;When I was a Unix geek&lt;sup id="fnref:geek"&gt;&lt;a href="#fn:geek" class="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;, I managed 400 machines the old fashioned way – SSH and hand-crafted bash loops.  This was a nightmare for a number of reasons…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It was error prone – what if the server's down?&lt;/li&gt;
  &lt;li&gt;It created snowflakes with slightly different configurations than the rest.&lt;/li&gt;
  &lt;li&gt;It was impossible to reason about the contents of a server.  The only way to know the actual state was to SSH in and find out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chef and Puppet automated this process, but perpetuated the fundamental flaw of gradually applying changes to the system.&lt;/p&gt;

&lt;p&gt;The programming world has long had the concept of immutable data structures (C's &lt;code&gt;const&lt;/code&gt; being the simplest), but this concept has been extended to everything from server configurations to distributed databases.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://chadfowler.com/2013/06/23/immutable-deployments.html"&gt;Immutable infrastructure&lt;/a&gt; is the practice of burning entire VM images for each software deployment, and reprovisioning those servers entirely.  This &lt;a href="https://medium.com/built-to-adapt/the-three-r-s-of-enterprise-security-rotate-repave-and-repair-f64f6d6ba29d#.tbxfshlry"&gt;increases security&lt;/a&gt; and reliability, removes snowflakes, and makes the servers easier to reason about.&lt;/p&gt;

&lt;p&gt;Immutability can also have a big impact on data stores.  It's easy to make performance optimisations if you can decide that the data will never be altered.  This also makes many distributed data strategies tractable.  Even if your data &lt;em&gt;can&lt;/em&gt; be changed, you can fake it by storing immutable versions and reading the most recent one.  This is the underlying principle of many modern eventually-consistent systems.  I don't know this for sure, but I'm willing to bet cash money that Twitter uses this trick to store tweets.&lt;/p&gt;

&lt;h3 id="know-your-vocabulary"&gt;Know Your Vocabulary&lt;/h3&gt;

&lt;p&gt;Understanding concepts like Declarative systems, Convergence, Idempotence, and Immutability, and knowing when and how to apply them will help you as you build architectures stable enough to withstand the chaotic mess of the Cloud.&lt;/p&gt;

&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:geek"&gt;
      &lt;p&gt;Past tense… Yeah, right.&amp;nbsp;&lt;a href="#fnref:geek" class="reversefootnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry>
    <title>Stop Looking for Trouble</title>
    <link rel="alternate" href="http://tammersaleh.com/posts/stop-looking-for-trouble/"/>
    <id>http://tammersaleh.com/posts/stop-looking-for-trouble/</id>
    <published>2015-01-25T10:16:00+00:00</published>
    <updated>2020-06-19T14:40:27+00:00</updated>
    <author>
      <name>Tammer Saleh</name>
    </author>
    <content type="html">&lt;p&gt;One of the hardest judgement calls in programming is determining the balance between refactoring and progress.  Focusing on the quality of the codebase is an investment in its maintainability. Of course, when taken to the extreme, this can completely derail our progress on shipping functionality to our users.&lt;/p&gt;

&lt;p&gt;So, how do we find the right balance?  In an agile development workflow, we rely upon the TDD cycle: red, green, refactor.  This produces a rhythm that maintains feature development without sacrificing quality.&lt;/p&gt;

&lt;p&gt;However, some developers who are new to this workflow question whether it allows them to step back and find better ways to design the overall system.  One of the misconceptions that leads to this doubt is in believing that the refactor step in "red, green, refactor" should only apply to the code you're currently writing. This creates a myopic behavior of only touching those bits of the codebase that are currently visible in the editor.  Clearly that would be foolish – refactorings often apply throughout an entire project.&lt;/p&gt;

&lt;p&gt;This isn't what the agile process encourages.  Instead, it dictates that the refactoring &lt;em&gt;must&lt;/em&gt; be driven by pain you're experiencing in the story you're currently working on.&lt;/p&gt;

&lt;p&gt;But why do we not spend that time looking for refactorings outside the scope of the current story?  Surely cleaning up code is a good thing, no matter where or why?&lt;/p&gt;

&lt;h3 id="you-cant-predict-the-future"&gt;You can't predict the future.&lt;/h3&gt;

&lt;p&gt;Code changes constantly.  Continual refactoring, customer requirements, adoption of 3rd party libraries and more translates into entire continents of code disappearing overnight.  Investing time in cleaning up these undiscovered countries is often utterly wasted when those swaths of code are later removed.&lt;/p&gt;

&lt;p&gt;What's more: these premature architectural changes solidify design choices.  Since we can't predict future demands that will be placed upon our code, these new walls often have to be moved at great expense.&lt;/p&gt;

&lt;p&gt;Most importantly, when we base our refactorings on tangible pain being felt at this moment, it's much more likely that we're actually avoiding future pain.  We can say with confidence: "That hurt.  Let's fix it to make sure it doesn't hurt again."&lt;/p&gt;

&lt;h3 id="stop-looking-for-trouble"&gt;Stop looking for trouble.&lt;/h3&gt;

&lt;p&gt;To help keep my attention on useful refactorings, I like to remind myself to &lt;strong&gt;stop looking for trouble&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you find yourself finished with a story and itching to find undiscovered places where code could be improved, &lt;strong&gt;you're just looking for trouble&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you come up with clever ways of abstracting modules that aren't yet being used in other places, &lt;strong&gt;you're just looking for trouble&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you start your greenfield application using Microservices™ and a message bus, &lt;strong&gt;you're just looking for trouble&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you say to your pair "Did you hear that noise down the alley?  Let's take a look!" then &lt;strong&gt;you're just looking for trouble&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id="trust"&gt;Trust.&lt;/h3&gt;

&lt;p&gt;There are two realisations that allowed me to stop looking for trouble:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;If code is never touched, the interest on the debt it contains is zero.&lt;/li&gt;
  &lt;li&gt;My teammates are more than capable of tackling said code with a machete if and when they do finally touch that code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This really all comes down to trust.  I realised that when I wanted to seek out strange and new places for technical debt, it was because I wasn't respecting my team.  I didn't trust them to clean up after themselves.&lt;/p&gt;

&lt;p&gt;By basing your refactorings on the pain you're feeling, and by trusting that your team will do the same, you can be confident that you're making the product better, without spinning your wheels by over-engineering.&lt;/p&gt;

</content>
  </entry>
  <entry>
    <title>UNIX Programming by Example: Runit</title>
    <link rel="alternate" href="http://tammersaleh.com/posts/unix-programming-by-example-runit/"/>
    <id>http://tammersaleh.com/posts/unix-programming-by-example-runit/</id>
    <published>2014-07-09T11:29:00+00:00</published>
    <updated>2020-06-19T14:40:27+00:00</updated>
    <author>
      <name>Tammer Saleh</name>
    </author>
    <content type="html">&lt;p&gt;I've often heard from fellow developers that they don't feel they have a strong foundation of UNIX principles.  &lt;a href="http://en.wikipedia.org/wiki/Unix_philosophy"&gt;The Unix Philosophy&lt;/a&gt; is a good set of meta-rules (much like SOLID principles), but I do better by seeing concrete examples.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://rubyists.github.io/2011/05/02/runit-for-ruby-and-everything-else.html"&gt;This description of Runit&lt;/a&gt; is such an example.  It makes graceful use of the filesystem, symlinks, convention-over-configuration, process semantics, tool modularity and so on.&lt;/p&gt;

&lt;p&gt;If you read that article and fully understand all of the features Runit gives you and the benefit of the features it doesn't, then you're on your way to really understanding good UNIX development.&lt;/p&gt;

&lt;p&gt;Take a second to read through &lt;a href="http://rubyists.github.io/2011/05/02/runit-for-ruby-and-everything-else.html"&gt;"Runit for Ruby (And Everything Else)"&lt;/a&gt; if you haven't already.  We're going to go through it, point by point, to get a practical understanding of what we should be aiming for when building UNIX friendly tools.&lt;/p&gt;

&lt;h3 id="conventions-over-requirements-over-configuration"&gt;Conventions over Requirements over Configuration&lt;/h3&gt;

&lt;p&gt;Good UNIX tools favor rewarding conformity over enforcing dogma.  Runit demonstrates this in spades.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;As you can see, all services live in one place, defined by &lt;code&gt;$SVDIR&lt;/code&gt; or &lt;code&gt;/service&lt;/code&gt; by default, which &lt;code&gt;runsvdir&lt;/code&gt; manages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;…and, later:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We can […] give a user control over their own &lt;code&gt;$SVDIR&lt;/code&gt;, &lt;code&gt;/home/username/service&lt;/code&gt; here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Everything Runit needs in order to operate is encapsulated in that one directory, conventionally &lt;code&gt;/service&lt;/code&gt;, but also easily changed by a non-root user.&lt;/p&gt;

&lt;p&gt;It would be tempting but misguided for the Runit developers to add a check to ensure it's being run as root.  By not enforcing that, and by allowing the directory to be configured by the environment, we've enabled a major feature: per-user service directories.&lt;/p&gt;

&lt;p&gt;This reminds me of the Ruby duck-typing philosophy.  Don't sprinkle aggressive type checks throughout your code. Instead, be more flexible and rely on the fact that your code will break cleanly when used improperly.  By doing so, you can open entirely new use-cases.&lt;/p&gt;

&lt;p&gt;We see this again in the implementation of runlevels:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Runlevels become (unlimited amount of) directories of services (in &lt;code&gt;/etc/runit/runsvdir&lt;/code&gt;) which can be switched to quickly and simply.&lt;/p&gt;

  &lt;pre&gt;&lt;code&gt;# Switch to the services described in `/etc/runit/runsvdir/primary`
$ runsvchdir primary

# Switch to the services described in `/etc/runit/runsvdir/failover`
$ runsvchdir failover
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;Runit doesn't place unnecessary and arbitrary constraints on the user.  It's simpler to outline conventions and allow the user to build functionality through normal tools like symlinks.  See this description of &lt;code&gt;runsvchdir&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;runsvchdir switches to the directory &lt;code&gt;/etc/runit/runsvdir/&lt;/code&gt;, copies &lt;code&gt;current&lt;/code&gt; to &lt;code&gt;previous&lt;/code&gt;, and replaces &lt;code&gt;current&lt;/code&gt; with a symlink pointing to dir.&lt;/p&gt;

  &lt;p&gt;Normally &lt;code&gt;/service&lt;/code&gt; is a symlink to &lt;code&gt;current&lt;/code&gt;, and runsvdir(8) is running &lt;code&gt;/service/&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, all &lt;code&gt;runsvchdir&lt;/code&gt; actually does is move symlinks around.  This is just an encouragement to follow good conventions. A user who "knows better" could easily build scripts to enable more complex tactics.  This is similar to how &lt;a href="https://github.com/capistrano/capistrano/wiki/Capistrano-Tasks#deploycreate_symlink"&gt;capistrano manages the currently deployed application&lt;/a&gt;.  Symlinks are also used for the &lt;code&gt;/service/*&lt;/code&gt; entries:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The services in &lt;code&gt;/service/*&lt;/code&gt; are symlinks to directories (usually in &lt;code&gt;/etc/sv/&lt;/code&gt;) which must contain one executable file, named ‘run’.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note that &lt;code&gt;/etc/svs&lt;/code&gt; is just an encouraged location for your actual service directories.  As King of Your Domain™, you could decide to keep those in &lt;code&gt;/tmp/goingawaynextboot&lt;/code&gt; – Runit doesn't need to care, so it doesn't care.  But it does strongly suggest the convention of using &lt;code&gt;/etc/svs&lt;/code&gt; by repeatedly referring to it in the documentation.&lt;/p&gt;

&lt;p&gt;On the other hand, Runit &lt;em&gt;does&lt;/em&gt; need to know how to run your service.  It could allow you to configure that in a &lt;code&gt;runit.rc&lt;/code&gt; file or some such.  But it takes a much easier and more oppinionated approach:  it requires that you have a &lt;code&gt;run&lt;/code&gt; script.  It's simple for both Runit and the user, and it's much more predictable.&lt;/p&gt;

&lt;p&gt;Runit also leans on convention to enable an entire set of features around failure notifications:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When a process stops, if a file named &lt;code&gt;finish&lt;/code&gt; exists and is executable, finish will be run with two arguments, the exit code and exit status of run.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note that the name of the script isn't configurable (since there's no real need) - it's just &lt;code&gt;finish&lt;/code&gt;.  It's also entirely optional, since Runit can operate without it.  Runit is able to gracefully punt on the entire notification quagmire, because it respects the fact that…&lt;/p&gt;

&lt;h3 id="your-users-are-developers"&gt;Your Users are Developers&lt;/h3&gt;

&lt;p&gt;Let's look at the output of &lt;code&gt;sv s&lt;/code&gt; (short for &lt;code&gt;sv status&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash"&gt;$ sudo sv s /service/*
run: /service/callcenter: (pid 2870) 5266009s
run: /service/postgres: (pid 3732) 7700117s
run: /service/lighttpd: (pid 27321) 5208602s; run: log: (pid 3724) 7700117s
run: /service/ssh: (pid 3757) 7700116s; run: log: (pid 3731) 7700117s  
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that all of the details for each service are listed on a single line, in a simple, but well-defined format.  This is easily parsable by both machines and humans.&lt;/p&gt;

&lt;p&gt;Of course it'd be easier for me to read if it converted the seconds to hours or days as appropriate, or if it aligned all of the columns.  But that would make it much harder for me to drive &lt;code&gt;sv&lt;/code&gt; through other scripts.  As a UNIX user, I'm more than happy to make that trade-off.&lt;/p&gt;

&lt;p&gt;You can see this respect for developers in the way Runit enables dependencies between services.  Runit simply provides the ability to wait for another service to come up, and expects the user to write &lt;code&gt;run&lt;/code&gt; scripts that use it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Basic dependency example &lt;code&gt;/service/lighttpd/run&lt;/code&gt;:&lt;/p&gt;

  &lt;pre&gt;&lt;code&gt;#!/bin/sh -e
sv -w7 check postgresql
exec 2&amp;gt;&amp;amp;1 myprocess
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;This single feature (wait 7 seconds for the &lt;code&gt;postgresql&lt;/code&gt; service to be running, exiting with an error if that timout is reached), when used by the end user in their &lt;code&gt;run&lt;/code&gt; command, solves a whole world of dependency-management features in one blow.&lt;/p&gt;

&lt;p&gt;You can even see this focus on scriptability in the lack of output in the happy paths:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Generally there will be no output from such commands, use -v to get some output (examples from here on out will use -v)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This follows Eric Raymond’s &lt;a href="http://en.wikipedia.org/wiki/Unix_philosophy"&gt;"Rule of Silence"&lt;/a&gt;: When a program has nothing surprising to say, it should say nothing.&lt;/p&gt;

&lt;p&gt;Similarly, consider Runit's strategy for managing environment variables for each service:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A directory of files where an enviroment variable will be created for each file, with the value set to the contents of that file, may appear cumbersome at first glance.&lt;/p&gt;

  &lt;p&gt;In practice, however, we find that changing one or two options is the most likely workflow.&lt;/p&gt;

  &lt;p&gt;With the envdir setup, this becomes&lt;/p&gt;

  &lt;pre&gt;&lt;code&gt;echo "value" &amp;gt; /service/foo/env/VARIABLE_NAME
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you respect the fact that your users are programmers, then it becomes immediately clear that this is much better than a flat file of key/value pairs.&lt;/p&gt;

&lt;p&gt;This also shows how we can gain simplicity by leaning heavily on the UNIX filesystem…&lt;/p&gt;

&lt;h3 id="the-filesystem-is-your-database"&gt;The Filesystem is Your Database&lt;/h3&gt;

&lt;p&gt;Consider, again, the example above.  To set environment variables for your service, you simply create a file.  Contrast that with &lt;a href="https://devcenter.heroku.com/articles/config-vars"&gt;the heroku CLI&lt;/a&gt; (&lt;code&gt;heroku config:set GITHUB_USERNAME=joesmith&lt;/code&gt;), or with &lt;a href="http://docs.travis-ci.com/user/build-configuration/#Set-environment-variables"&gt;setting variables in the &lt;code&gt;.travis.yml&lt;/code&gt; file&lt;/a&gt;.  Both of these solutions require more code on the author's part, more congnitive load on the user, and (at least in the travis case) more effort to script.&lt;/p&gt;

&lt;p&gt;Runit's approach, however, is both easy and incredibly simple.  It achieved this simplicity because the authors understood that the UNIX filesystem wasn't just intended to hold MP3s.  It's your local database.&lt;/p&gt;

&lt;p&gt;You can see this same simplicity in adding and removing services:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Remove a service (stop it and make it not start back up, even on boot)&lt;/p&gt;

  &lt;pre&gt;&lt;code&gt;$ rm /service/sshd
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's it.  Runit polls the filesystem, and constantly converges on the expected state.  Much simpler for the user to interact with than learning a new command line option.&lt;/p&gt;

&lt;h3 id="simplicity-through-composability"&gt;Simplicity through Composability&lt;/h3&gt;

&lt;p&gt;Runit also achieves simplicity by breaking itself up into many different tools with clear responsibilities: &lt;a href="http://smarden.org/runit/runsvdir.8.html"&gt;runsvdir&lt;/a&gt;, &lt;a href="http://smarden.org/runit/runsvchdir.8.html"&gt;runsvchdir&lt;/a&gt; &lt;a href="http://smarden.org/runit/runsv.8.html"&gt;runsv&lt;/a&gt;, &lt;a href="http://smarden.org/runit/svlogd.8.html"&gt;svlogd&lt;/a&gt;, &lt;a href="http://smarden.org/runit/chpst.8.html"&gt;chpst&lt;/a&gt;, &lt;a href="http://smarden.org/runit/sv.8.html"&gt;sv&lt;/a&gt;, and more.  Most programmers understand the need to break their code into clean classes and modules, but they often fail to extend that into the overall user interface.&lt;/p&gt;

&lt;p&gt;By breaking the interface up into multiple executables like this, you simplify the implementation and make each individual tool easier for the end user to understand.  The overall system may seem more complex, but that comes with the benefit of being able to easily drive various parts of the system through user-written scripts.&lt;/p&gt;

&lt;p&gt;Consider the Runit process hierarchy:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;runit
`- runsvdir
   |- runsv
   |  `- apache-ssl
   |- runsv
   |  `- crond
   `- runsv
      `- tinydns
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The responsibilities are cleanly split between running many services (&lt;code&gt;runsvdir&lt;/code&gt;) and monitoring/restarting a single service (&lt;code&gt;runsv&lt;/code&gt;).  By moving the restarting logic to many &lt;code&gt;runsv&lt;/code&gt; processes, the semantics of each is much simipler to understand and debug.  Having many processes with a single task each is better than a single master process with too many responsibilities.&lt;/p&gt;

&lt;p&gt;Runit also gains simplicity through a strong understanding of processes and pipes.&lt;/p&gt;

&lt;h3 id="understand-processes"&gt;Understand Processes&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Processes run in the foreground logging to stdout/stderr.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The only thing cool about daemons is the name.  They're &lt;a href="http://en.wikipedia.org/wiki/Daemon_(computing)#Creation"&gt;a terrible hack&lt;/a&gt; involving a voodoo ritual of over 10 steps that makes &lt;a href="http://en.wikipedia.org/wiki/Terminate_and_stay_resident_program"&gt;old TSRs&lt;/a&gt; look like "good architecture."  Daemons were only ever devised as a workaround for the poor design of the original &lt;a href="http://www.unix.com/man-page/linux/5/init/"&gt;init&lt;/a&gt;.  Runit understands this, and expects that the services it monitors simply run in the forground as a normal god-fearing executable.&lt;/p&gt;

&lt;p&gt;Furthermore, it makes graceful use of stdout and UNIX pipes to handle service logging:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If the directory &lt;code&gt;log/&lt;/code&gt; exists, it will be treated as a log service.&lt;/p&gt;

  &lt;p&gt;runsv will create a pipe and redirect standard out of the service (and its optional finish script) to &lt;code&gt;log/run&lt;/code&gt;.&lt;/p&gt;

  &lt;p&gt;Log Service (for sshd) &lt;code&gt;/etc/sv/sshd/log/run&lt;/code&gt;:&lt;/p&gt;

  &lt;pre&gt;&lt;code&gt;#!/bin/sh
exec svlogd -t /var/log/sshd/
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using UNIX pipes simplifies the entire logging problem.  Processes no longer need to understand how to talk to syslog - they just print to STDOUT.  This is something &lt;a href="http://12factor.net/logs"&gt;Twelve-Factor systems like Heroku and Cloud Foundry&lt;/a&gt; get right.&lt;/p&gt;

&lt;h3 id="become-a-better-developer-by-understanding-unix"&gt;Become a Better Developer by Understanding UNIX&lt;/h3&gt;

&lt;p&gt;The logging example above actually sums up everything we've talked about quite nicely:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;By respecting the fact that the user is a programmer, Runit enables them to implement whatever filtering or strange logging logic they need.&lt;/li&gt;
  &lt;li&gt;Runit doesn't enforce that users manage logs in any specific way, but encourages conventions by providing the &lt;code&gt;svlogd&lt;/code&gt; logging tool.&lt;/li&gt;
  &lt;li&gt;Runit achieves simplicity by treating the logging service like any other process.&lt;/li&gt;
  &lt;li&gt;It leans on the filesystem: the &lt;code&gt;log&lt;/code&gt; subdirectory is just another service directory, and could be shared amongst other services via symlinks.&lt;/li&gt;
  &lt;li&gt;It only enforces the conventions it has to: the &lt;code&gt;log&lt;/code&gt; directory is entirely optional, but the &lt;code&gt;run&lt;/code&gt; file is necessary (and isn't configurable).  Similarly, logging isn't required, but if it's used, then all logs &lt;em&gt;must&lt;/em&gt; come via &lt;code&gt;STDOUT&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These principles are old, but there's a lot to learn from revisiting them.  You can become a significantly better developer by studying how a single, well-written UNIX tool makes graceful use of the filesystem, symlinks, convention-over-configuration, process semantics, tool modularity and so on.&lt;/p&gt;

&lt;h4 class="hero" id="were-looking-for-engineers-who-are-excited-by-this-sort-of-topic-on-the-london-services-team-if-youre-interested-in-joining-us-then-id-love-to-hear-from-you"&gt;We're looking for engineers who are excited by this sort of topic on &lt;a href="http://t.co/SyEnZoyzlW"&gt;the London Services team&lt;/a&gt;. If you're interested in joining us, then I’d love to hear from you!&lt;/h4&gt;

&lt;p&gt;Finally, thanks to &lt;a href="http://www.mikeperham.com/"&gt;Mike Perham&lt;/a&gt;, who recently wrote &lt;a href="http://www.mikeperham.com/2014/07/07/use-runit"&gt;a nice little piece about how much he appreciates runit&lt;/a&gt;.  It prompted this post by reminding me of how wonderful an example of proper UNIX programming runit (and it's lineage, &lt;a href="http://en.wikipedia.org/wiki/Daemontools"&gt;Daemontools&lt;/a&gt;) truly is.&lt;/p&gt;

</content>
  </entry>
  <entry>
    <title>Bringing Data to the Cloud</title>
    <link rel="alternate" href="http://tammersaleh.com/posts/bringing-data-to-the-cloud/"/>
    <id>http://tammersaleh.com/posts/bringing-data-to-the-cloud/</id>
    <published>2014-07-02T13:15:00+00:00</published>
    <updated>2020-06-19T14:40:27+00:00</updated>
    <author>
      <name>Tammer Saleh</name>
    </author>
    <content type="html">&lt;p&gt;I lead a team of engineers here in the London Pivotal office, who are responsible for building the next generation of Data Services for &lt;a href="http://www.gopivotal.com/platform-as-a-service/pivotal-cf"&gt;Pivotal CF&lt;/a&gt;.  Here's some of our thinking around building reliable, self-sustaining, dynamically provisioned data services.&lt;/p&gt;

&lt;p&gt;Bringing data services to a platform like Cloud Foundry is absolutely key.  Building a production-grade PaaS is by no means easy, but superficial stateless platforms are becoming commodities. This is a good thing - it's the progress driving agility and the DevOps movement.  However, &lt;strong&gt;everything changes when you add state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Managing stateful processes, juggling volumes, managing backups, ensuring uptime, gardening AOFs and snapshots, dealing with startup dependencies…  It's hard.  And doing all of this in a completely automated way, behind unknown firewalls, in unknown environments is crazy-hard.  However, we have a &lt;a href="http://bosh.cloudfoundry.org/"&gt;secret weapon&lt;/a&gt; which moves this problem just out of "insane" and into merely "ill-advised."&lt;/p&gt;

&lt;h4 id="challenges"&gt;Challenges&lt;/h4&gt;

&lt;p&gt;We have &lt;em&gt;a lot&lt;/em&gt; of services to build for 2014 - Cassandra, Redis, Neo4j, MongoDB, Memcached, Elasticsearch, and more.  Because of this, we're forced to look at deploying and managing services from a much more holistic point of view.  We have to concentrate on building patterns that can be reproduced instead of bespoke snowflakes.&lt;/p&gt;

&lt;p&gt;&lt;img src="/posts/bringing-data-to-the-cloud/logos.png" alt="Logos" /&gt;&lt;/p&gt;

&lt;p&gt;Also, because we're building Cloud Foundry Services, we have the added complexity of building these to be provisioned on-demand - either as multi-tenant or single-tenant instances.&lt;/p&gt;

&lt;h4 id="cloud-foundry-services-api"&gt;Cloud Foundry Services API&lt;/h4&gt;

&lt;p&gt;Because services that work with the Cloud Foundry platform must provide databases to many different application developers, they need to be able to perform &lt;a href="http://docs.cloudfoundry.org/services/api.html"&gt;two important actions&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a new instance.&lt;/li&gt;
  &lt;li&gt;Return a binding to that instance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most important question you have to answer when building a new service is: &lt;strong&gt;Exactly what is "an instance?"&lt;/strong&gt;  This informs the rest of your service design, and is the most important aspect of the application developer's experience.  There are roughly two paths to go down: Multi and Single-tenant.  Of course, the Devil is in the details.&lt;/p&gt;

&lt;h4 id="multi-tenant-cluster"&gt;Multi-tenant Cluster&lt;/h4&gt;

&lt;p&gt;By far, the easiest path is building a multi-tenant service.  In the multi-tenant scenario, we deploy a single service cluster which is shared amongst all of the application developers.  This basically pushes all of the scaling and resource isolation down into the service itself.&lt;/p&gt;

&lt;p&gt;&lt;img src="/posts/bringing-data-to-the-cloud/Multi-tenant.png" alt="Multi-tenant" /&gt;&lt;/p&gt;

&lt;p&gt;Some services handle this well.  MySQL and PostgreSQL are examples of services that work fairly well from a multi-tenant point of view.  While they're difficult to scale horizontally, and they don't deal well with noisy neighbors, they do have good authorization and authentication.&lt;/p&gt;

&lt;p&gt;Cassandra is another example of a service that can work in a multi-tenant environment.  It scales very well horizontally, and has strong authentication.  However, there are still issues with its authorization system, and it has no good way of dealing with noisy neighbors.&lt;/p&gt;

&lt;h4 id="single-tenant-processes"&gt;Single-tenant Processes&lt;/h4&gt;

&lt;p class="img_right"&gt;&lt;img src="/posts/bringing-data-to-the-cloud/Single-Tenant-Processes.png" alt="Single-tenant" /&gt;&lt;/p&gt;

&lt;p&gt;Some services, however, don't work at all in a multi-tenant environment.  Redis is such a service - it has no multi-user authentication or authorization, and it doesn't yet scale horizontally.  Furthermore, its single-threaded, single-process architecture makes it extremely sensitive to noisy neighbors.&lt;/p&gt;

&lt;p&gt;For services such as this, it's better to provision dedicated processes for each requested instance.  While the VM is still shared amongst users, because users get a dedicated process, we categorize these as &lt;strong&gt;single-tenant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Security with Single-tenant services is much easier. However, now we have to deal with real-time provisioning, configuration and orchestration.  We have to keep hundreds of dynamic processes running, and we have to upgrade each of them when a new release is deployed.  Each process has its own directory for persistent storage, which needs to be baby-sat during this whole process.  And we still haven't solved the noisy neighbor problem, since a single process &lt;em&gt;could&lt;/em&gt; hog a single CPU or all of the available IOPS on the VM.&lt;/p&gt;

&lt;h4 id="single-tenant-clusters-of-processes"&gt;Single-tenant Clusters of Processes&lt;/h4&gt;

&lt;p&gt;&lt;img src="/posts/bringing-data-to-the-cloud/Single-Tenant-Clustered-Processes.png" alt="Single-tenant Clustered Processes" /&gt;&lt;/p&gt;

&lt;p&gt;The difficulty of provisioning and orchestrating these processes becomes much greater when we need to scale out horizontally.  Now we need to manage &lt;em&gt;clusters&lt;/em&gt; of processes across multiple VMs.  We must migrate processes when VMs are removed, maintain state across VMs, control the boot-sequence, manage master/slave IPs, etc.  For this, we need some level of process scheduling - likely using our own &lt;a href="https://github.com/cloudfoundry-incubator/diego-release"&gt;diego&lt;/a&gt;.&lt;/p&gt;

&lt;h4 class="clear" id="containers"&gt;Containers&lt;/h4&gt;

&lt;p class="img_right"&gt;&lt;img src="/posts/bringing-data-to-the-cloud/docker.png" alt="Docker" /&gt;&lt;/p&gt;

&lt;p&gt;Wrapping each process in a Docker container seems like a no-brainer at first glance.  This solves the noisy neighbor problem, and adds another layer of security around the service.  It certainly is a good idea, but it's also not trivial.  Docker hasn't yet figured out the software-defined-networking problem.  It also doesn't solve the issues around stateful services and inter-VM data migrations.&lt;/p&gt;

&lt;h4 id="single-tenant-clusters-of-virtual-machines"&gt;Single-tenant Clusters of Virtual Machines&lt;/h4&gt;

&lt;p&gt;Once we start getting into real production workloads, splitting up VMs via processes or containers becomes a non-starter.  Production workloads require &lt;strong&gt;all&lt;/strong&gt; of the resources available on the VM.&lt;/p&gt;

&lt;p&gt;While BOSH (our large-scale deployment tool of choice) is generally geared towards static deployments, we &lt;em&gt;can&lt;/em&gt; use it to dynamically spin up entire dedicated clusters of VMs when an application developer asks for an instance of a service.  The processes running on those VMs may also be containerized, if only for consistency and another layer of security, but the VMs are dedicated to a single instance.  There are no more noisy neighbor problems, and the cluster can make full use of the CPU, IOPS, and network.&lt;/p&gt;

&lt;p&gt;&lt;img src="/posts/bringing-data-to-the-cloud/Single-Tenant-Clustered-VMs.png" alt="Single-tenant Clustered VMs" /&gt;&lt;/p&gt;

&lt;h4 id="still-exploring"&gt;Still Exploring&lt;/h4&gt;

&lt;p&gt;This is exactly the topic that Chris Brown and I spoke about at the &lt;a href="http://cfsummit.com/"&gt;2014 Cloud Foundry Summit&lt;/a&gt;.  You can find &lt;a href="/speaking/"&gt;the video here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To be clear, this is still all very much a work in progress.  We're constantly exploring new and improved ways of tackling this challenge, and we fully expect to get it wrong before we get it right.  It's a process that's just as fulfilling as it is challenging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If this sounds exciting to you and you’re interested in &lt;a href="http://t.co/SyEnZoyzlW"&gt;joining the London Services team&lt;/a&gt;, I’d love to hear from you!&lt;/strong&gt;&lt;/p&gt;

</content>
  </entry>
  <entry>
    <title>Mind the Map</title>
    <link rel="alternate" href="http://tammersaleh.com/posts/mind-the-map/"/>
    <id>http://tammersaleh.com/posts/mind-the-map/</id>
    <published>2014-05-20T10:00:00+00:00</published>
    <updated>2020-06-19T14:40:27+00:00</updated>
    <author>
      <name>Tammer Saleh</name>
    </author>
    <content type="html">&lt;p&gt;I've dealt with a lot of technical debt in my life.  &lt;a href="http://www.amazon.com/Rails-AntiPatterns-Refactoring-Addison-Wesley-Professional/dp/0321604814"&gt;Enough to fill a book&lt;/a&gt;.  I've come to a conclusion:  &lt;strong&gt;Technical Debt is almost always manageable.  Product debt often isn't.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id="surface-area"&gt;Surface Area&lt;/h4&gt;

&lt;p&gt;What do I mean by "product debt?"&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Features that drastically and immeasurably increase your product's surface area.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p class="img_right"&gt;&lt;img src="/posts/mind-the-map/terrain.jpg" alt="Terrain" /&gt;&lt;/p&gt;

&lt;p&gt;This form of debt usually comes around because of laziness on the product owner's part.  Instead of dedicating time and energy towards discovering exactly what knobs customers need, they give them everything in one fell swoop.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Want to clean up your configuration file?  &lt;a href="http://www.getchef.com/chef/"&gt;How about we just give you all of Ruby?&lt;/a&gt;"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Want to install a package in the cloud?  &lt;a href="https://www.engineyard.com/"&gt;How about we just give you full root access?&lt;/a&gt;"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Want to gather stats from your processes?  &lt;a href="http://en.wikipedia.org/wiki/Java_Management_Extensions"&gt;How about we just expose the entire runtime over the network?&lt;/a&gt;"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At first these feel like clever solutions to entire classes of customer feature requests.  "If we just give them X, then we never have to deal with feature requests like this again."  But by being lazy, you've created a number of huge problems for yourself.&lt;/p&gt;

&lt;h5 id="unknown-unknowns"&gt;Unknown unknowns&lt;/h5&gt;

&lt;p&gt;These decisions impart more features to the end user than they actually need.  It's hard as hell to take features away from customers, even the rarely used ones.  You're now committed to maintaining features you haven't yet thought of.&lt;/p&gt;

&lt;h5 id="lack-of-visibility"&gt;Lack of Visibility&lt;/h5&gt;

&lt;p&gt;To make matters worse, when you add sweeping features such as these, you're left blind as to how customers are actually using your product.  You've lost the tooling to give you insight as to how to make your product really shine.&lt;/p&gt;

&lt;p&gt;Which packages are they installing?  Which commands are they running?  Which metrics do they care about?&lt;/p&gt;

&lt;h5 id="painted-in-a-corner"&gt;Painted in a Corner&lt;/h5&gt;

&lt;p&gt;Iterating yourself out of technical debt is easier because only your team deals with each change.  The channels of communication are few, and you can coordinate migrations through each step.&lt;/p&gt;

&lt;p&gt;With product debt, however, each change impacts a huge number of people who you have no direct contact with.  Removing an all-encompassing feature requires that you gradually add focused replacements until you believe the majority of your customers will be satisfied.  But, because you can't control when they migrate to these new tools, and you can't see exactly why they're still using the meta-feature you gave them years ago, this becomes a herculean task.&lt;/p&gt;

&lt;p class="img_left"&gt;&lt;img src="/posts/mind-the-map/topo.jpg" alt="Topo" /&gt;&lt;/p&gt;

&lt;h4 id="maintain-the-map"&gt;Maintain the Map&lt;/h4&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; get yourself out of product debt, but it's incredibly painful.  Apple did it when they abandoned MacOS 9 in favor of a daring new OS X.  Heroku did it when they abandoned Bamboo for Cedar, and that took them years.  It involves patience, communication, insight and bravery.&lt;/p&gt;

&lt;p&gt;To avoid this situation in the first place, you need to start actively thinking about your product's surface area.&lt;/p&gt;

&lt;p&gt;Imagine a topography that describes the way your customers interact with your product.  Each new feature broadens that landscape, increasing the size of the terrain that you have to monitor and maintain.  Some features add fjords, and some add entire continents.  Elegant products achieve a high degree of focused utility by exposing very little territory for the customer to understand and interact with.  Instead, they focus on keeping that area meticulously maintained.  This focus on quality over quantity produces highly maintainable and flexible products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your job is to figure out what customers need, and give it to them using as little land as possible.&lt;/strong&gt;&lt;/p&gt;

</content>
  </entry>
  <entry>
    <title>Building an Encrypted USB Drive for your SSH Keys</title>
    <link rel="alternate" href="http://tammersaleh.com/posts/building-an-encrypted-usb-drive-for-your-ssh-keys-in-os-x/"/>
    <id>http://tammersaleh.com/posts/building-an-encrypted-usb-drive-for-your-ssh-keys-in-os-x/</id>
    <published>2014-05-10T11:00:00+00:00</published>
    <updated>2020-06-19T14:40:27+00:00</updated>
    <author>
      <name>Tammer Saleh</name>
    </author>
    <content type="html">&lt;p class="img_right"&gt;&lt;img src="/posts/building-an-encrypted-usb-drive-for-your-ssh-keys-in-os-x/keychain.jpg" alt="Erase" /&gt;&lt;/p&gt;

&lt;p&gt;UPDATE: This post's process has been encoded and published &lt;a href="https://github.com/pivotal/usb-login-scripts"&gt;in this repo, pivotal/usb-login-scripts&lt;/a&gt;. Try out the "scripts-autoexpire" for a similar experience with a few extra features.&lt;/p&gt;

&lt;p&gt;Working on a Platform like &lt;a href="http://pivotal.io/platform"&gt;Cloud Foundry&lt;/a&gt;, which is relied upon by a growing community of "serious" companies, requires us to take security seriously as well.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Security is something you know, something you have, and something you are.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The commonly agreed upon tenets of strong security is that it requires a combination of "something you know, something you have, and something you are."  Two factor authentication includes both of those - usually something you know and something you have.&lt;/p&gt;

&lt;p&gt;Here's how we've implemented two factor authentication across the board for our SSH keys using &lt;a href="http://www.amazon.com/Kingston-Digital-DataTraveler-DTSE9H-16GBZET/dp/B00DYQYITG"&gt;USB keychain drives&lt;/a&gt;.  This strengthens our access to Github repositories and the numerous deployments we manage.&lt;/p&gt;

&lt;p&gt;Follow these instructions to increase your security at home and work as well.&lt;/p&gt;

&lt;h3 class="clear" id="choosing-a-usb-key"&gt;Choosing a USB Key&lt;/h3&gt;

&lt;p&gt;We prefer the &lt;a href="http://www.amazon.com/Kingston-Digital-DataTraveler-DTSE9H-16GBZET/dp/B00DYQYITG"&gt;Kingston DataTraveler&lt;/a&gt; drive due to its size and cost.  Once you've found a USB keychain drive to your liking, you'll want to reformat it using macOS's built-in encrypted filesystem.&lt;/p&gt;

&lt;h3 id="format-the-drive"&gt;Format the Drive&lt;/h3&gt;

&lt;p&gt;&lt;img src="/posts/building-an-encrypted-usb-drive-for-your-ssh-keys-in-os-x/erase-el-capitan.png" alt="Erase" /&gt;&lt;/p&gt;

&lt;p&gt;Plug your drive into your computer and open Disk Utility.  Select the disk (not the volume) on the left and navigate to the "Erase" tab.  You'll want to name the volume something simple (such as "keys") to make it easier to access on the command line.&lt;/p&gt;

&lt;p&gt;If you see the encrypted options in the dropdown, then just go ahead and format your drive with &lt;code&gt;Mac OS Extended (Case-sensitive, Journaled, Encrypted)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;However, some USB keys' partition tables are MBR, which doesn't support encryption, and you won't see encrypted partitions as options in the "Format" dropdown.  In that case, you'll have to do a two-step dance, formatting the drive twice:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Once as &lt;code&gt;OS X Extended (Journaled)&lt;/code&gt; using the &lt;code&gt;GUID Partition Map&lt;/code&gt;, then..&lt;/li&gt;
  &lt;li&gt;Again, using &lt;code&gt;Mac OS Extended (Case-sensitive, Journaled, Encrypted)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, you'll be prompted for your decryption password whenever you insert the drive.  Be sure not to save the password into the OS X Keychain.&lt;/p&gt;

&lt;h3 class="clear" id="add-your-ssh-keys"&gt;Add your SSH Keys&lt;/h3&gt;

&lt;p&gt;If you don't already have SSH keys, then you'll want to generate a new set.  In fact, it's probably a good idea to use this as a chance to create a fresh set either way, just in case yours have been compromised.&lt;/p&gt;

&lt;p&gt;You create a new SSH key pair by running &lt;code&gt;ssh-keygen&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash"&gt;$ ssh-keygen -f /Volumes/keys/id_rsa -C "Tammer Saleh"
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx Tammer Saleh
The key's randomart image is:
+--[ RSA 2048]----+
|             x   |
|            x  x |
|      x x xxxxxxx|
|     x x x x xxxx|
|      x x x   x  |
|     x x x     x |
|    x x x x      |
|     x   x       |
|                 |
+-----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then add the newly-created public key to &lt;a href="https://github.com/settings/keys"&gt;your Github account&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="script-to-load-keys-and-eject"&gt;Script to Load Keys and Eject&lt;/h3&gt;

&lt;p&gt;At this point, you could use the drive by manually adding the keys to your running agent and ejecting the drive.  But that's a lot of typing and feels fairly error prone.  Instead, let's script it.&lt;/p&gt;

&lt;p&gt;Create the following script on your drive, and name it &lt;code&gt;load&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash"&gt;#!/usr/bin/env bash

set -e

HOURS=$1
DIR=$(dirname $0)
KEY=$DIR/id_rsa

if [ -z $HOURS ]; then
  HOURS=1
fi

/usr/bin/ssh-add -D
/usr/bin/ssh-add -t ${HOURS}H $KEY
/usr/sbin/diskutil umount force $DIR
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will need to mark the script as executable:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;chmod +x /Volumes/keys/load
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, you can simply run &lt;code&gt;/Volumes/keys/load&lt;/code&gt; to load your keys and eject the drive automatically.  This makes for a very quick workflow.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash"&gt;$ /Volumes/keys/load
All identities removed.
Enter passphrase for /Volumes/keys/id_rsa: **********
Identity added: /Volumes/keys/id_rsa (/Volumes/keys/id_rsa)
Lifetime set to 3600 seconds
Volume keys on disk3 force-unmounted
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="make-a-backup-image"&gt;Make a Backup Image&lt;/h3&gt;

&lt;p&gt;Finally, you can make an encrypted backup image in case our USB drive shits the bed.&lt;/p&gt;

&lt;p&gt;&lt;img src="/posts/building-an-encrypted-usb-drive-for-your-ssh-keys-in-os-x/backup-image-el-capitan.png" alt="Backup Image" /&gt;&lt;/p&gt;

&lt;p&gt;Again, open Disk Utility.  This time, we select the &lt;code&gt;New Image&lt;/code&gt;&amp;gt;&lt;code&gt;Image from "keys"&lt;/code&gt; menu item.  Save the image as &lt;code&gt;keys_backup&lt;/code&gt;, and configure it to be compressed and encrypted.&lt;/p&gt;

&lt;p&gt;Ideally, you'll store your backup somewhere super secure.  Another option is to simply create two USB drives, and store one in a locked box.  My wife and I have a &lt;a href="http://www.amazon.com/SentrySafe-500-FIRE-SAFE-Cubic-Black/dp/B000W8J75E"&gt;locked fireproof box&lt;/a&gt; that we use for all of our personal documents (passports, etc), which is an ideal location.&lt;/p&gt;

&lt;h3 class="clear" id="weaknesses-and-future-improvements"&gt;Weaknesses and Future Improvements&lt;/h3&gt;

&lt;p&gt;While this is infinitely better than leaving your ssh key unprotected on your computer, there are some weaknesses and potential future improvements.&lt;/p&gt;

&lt;p&gt;The major weakness is that we're trusting that the host machine hasn't been tampered with.  If it has, then we're handing our private keys over to it.  Again, this risk exists either way, and isn't made worse through this technique.&lt;/p&gt;

&lt;p&gt;The forced eject is blatant cargo culting.  When tried without forcing, the drive reports being in use, but &lt;code&gt;lsof&lt;/code&gt; shows no processes using it.  This is the case even when the script is run from outside the drive, so I'm at a loss.&lt;/p&gt;

&lt;p&gt;Ideally, we could store our entire &lt;code&gt;~/.ssh&lt;/code&gt; directory on the keychain.  This requires a symlink from &lt;code&gt;~/.ssh&lt;/code&gt; to &lt;code&gt;/Volumes/keys/.ssh&lt;/code&gt;, and has a number of other complications around permissions.  We haven't investigated furthur.&lt;/p&gt;

&lt;h3 id="alternatives"&gt;Alternatives&lt;/h3&gt;

&lt;p&gt;A friend of mine &lt;a href="http://plumber.sublimia.nl/post/introducing-keyguard/"&gt;recently blogged about his alternative approach&lt;/a&gt;, which makes use of &lt;a href="https://github.com/cromega/keyguard"&gt;an HTTP service that serves your SSH keys&lt;/a&gt;, and authenticates with a &lt;a href="https://www.yubico.com/why-yubico/for-individuals/"&gt;YubiKey&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
</feed>
