Jekyll2021-02-21T22:53:29-08:00https://www.devzero.com/feed.xml/dev/zeroThe infinite streamMaking FileVault Use a Disk Password – 10.14 Edition2019-06-18T01:39:29-07:002019-06-18T01:39:29-07:00https://www.devzero.com/making-filevault-use-a-disk-password-1014-edition<p>I <a href="/making-filevault-use-a-disk-password.html">previously wrote about</a> how to make Mac OS’s FileVault disk encryption feature use separate passwords for unlocking the disk and logging into the system once it is running. This allows for better separation of concerns, but it goes against the proverbial grain, as the front end for FileVault tries its best to keep the passwords for unlocking the disk in sync with passwords for user accounts in an effort to keep people from locking themselves out of their machines.</p>
<p>This design remains true today, though the tech is different: rather than using HFS+ volumes and Core Storage volume groups, Mac OS now uses APFS volumes and APFS containers to do the same job. Surprisingly, the main roadblock that caused me to write this article is not the new storage back end, but rather several changes to Mac OS 10.14 that make it far more aggressive about the way it handles FileVault passwords:</p>
<ul>
<li>When initial setup runs from a FileVault-encrypted disk, it refuses to create the first user unless it can also add that user to FileVault.</li>
<li><code class="language-plaintext highlighter-rouge">fdesetup</code>, the tool for managing users on FileVault volumes, refuses to delete the last user from a volume even if that volume has a disk password.</li>
<li>The boot-time program that unlocks the disk refuses to allow one to enter a disk password at all if the volume has any users.</li>
<li>You cannot add a disk password to a volume with users.</li>
</ul>
<p>Previously, one could simply install the system as usual and delete the user created during initial setup, but these changes now make that impossible. I worked around this by skipping initial setup entirely.</p>
<h3 id="create-an-apfs-container-and-volume">Create an APFS container and volume</h3>
<p>APFS is similar to ZFS and btrfs in that formatting a partition with it creates a <em>container</em> and carves that up into thin <em>volumes</em> that occupy only the space that their contents need. An APFS volume can optionally reserve a minimum amount of space or specify a maximum that it is allowed to take, but by default it has neither. Creating an APFS container and an encrypted volume to install Mac OS onto is simpler than it was with Core Storage, taking only two commands while booted into an installer disk:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>diskutil apfs createcontainer disk0s2
<span class="go">Creating container with disk0s2
Started APFS operation on disk0s2 Untitled
Creating a new empty APFS Container
Unmounting Volumes
Switching disk0s2 to APFS
Creating APFS Container
Created new APFS Container disk1
Disk from APFS operation: disk1
Finished APFS operation on disk0s2 Untitled
</span><span class="gp">#</span><span class="w"> </span>diskutil apfs addvolume disk1 apfs <span class="s1">'Macintosh HD'</span> <span class="nt">-passprompt</span>
<span class="go">Passphrase:
Repease passphrase:
Exporting new encrypted APFS Volume "Macintosh HD" from APFS Container Reference disk1
Started APFS operation on disk1
Preparing to add APFS Volume to APFS Container disk1
Creating APFS Volume
Created new APFS Volume disk1s1
Mounting APFS Volume
Setting volume permissions
Disk from aPFS operation: disk1s1
Finished APFS operation on disk1
</span></code></pre></div></div>
<p>Though the underlying technology is different, logically, this setup is largely the same as what we had before.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>diskutil apfs list
<span class="go">APFS Container (1 found)
|
+-- Container disk1 1AA9AEE4-521E-4AA6-9BA9-08FD20DBF6AE
====================================================
APFS Container Reference: disk1
Size (Capacity Ceiling): 380295426048 B (380.3 GB)
Capacity In Use By Volumes: 149790720 B (149.8 MB) (0.0% used)
Capacity Not Allocated: 380145635328 B (380.1 GB) (100.0% free)
|
+-< Physical Storage disk0s2 B0646769-963F-4322-89A5-4D2E63510C70
| -------------------------------------------------------------
| APFS Physical Store Disk: disk0s2
| Size: 380295426048 B (380.3 GB)
|
</span><span class="gp"> +-></span><span class="w"> </span>Volume disk1s1 6296A941-52A6-42DF-93C6-363944FA5DB0
<span class="go"> ---------------------------------------------------
APFS Volume Disk (Role): disk1s1 (No specific role)
Name: Macintosh HD (Case-insensitive)
Mount Point: /Volumes/Macintosh HD
Capacity Consumed: 20480 B (20.5 KB)
FileVault: Yes (Unlocked)
</span></code></pre></div></div>
<p>Now that the machine has an encrypted volume to install to, return to the installer and install Mac OS onto it. Once it is installed it will prompt for the disk password and then boot into initial setup.</p>
<h3 id="create-the-first-user-by-hand">Create the first user by hand</h3>
<p>We can’t use the machine without creating a user, but if we proceed through the setup process to create one it will prompt for the disk password, add the new user to FileVault, and we will be stuck with it since we can’t remove it. Stuck between a rock and a hard place, we have little choice but to bypass this setup process entirely and try and replicate what it does by hand. Turn the machine off by quitting the installer with Command+Q, then reboot into single-user mode by holding Command+S and turning it back on again.</p>
<p>Once booted into single-user mode (note that since we didn’t create a user the disk password still works), we get to add the initial user by hand. This is a more ardurous process than it is on most other unix-ish systems because Mac OS stores user accounts in a directory service that it runs locally, similar to if a Linux machine were to run a small OpenLDAP instance instead of using <code class="language-plaintext highlighter-rouge">passwd(5)</code> and <code class="language-plaintext highlighter-rouge">group(5)</code> files. This service does not run in single user mode, so we need to remount the disk for writing and start it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>mount <span class="nt">-uw</span> /
<span class="gp">#</span><span class="w"> </span>launchctl load /System/Library/LaunchDaemons/com.apple.opendirectoryd.plist
</code></pre></div></div>
<p>Then add the bits that comprise a user account, one at a time. This will show some errors that appear to be there because the file name from the previous command is new in version 10.14 and the <code class="language-plaintext highlighter-rouge">dscl</code> command was never updated to address the change.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>dscl <span class="nb">.</span> <span class="nt">-create</span> /Users/gholms
<span class="gp">#</span><span class="w"> </span>dscl <span class="nb">.</span> <span class="nt">-create</span> /Users/gholms UserShell /bin/zsh
<span class="gp">#</span><span class="w"> </span>dscl <span class="nb">.</span> <span class="nt">-create</span> /Users/gholms RealName <span class="s2">"gholms"</span>
<span class="gp">#</span><span class="w"> </span>dscl <span class="nb">.</span> <span class="nt">-create</span> /Users/gholms UniqueID 501
<span class="gp">#</span><span class="w"> </span>dscl <span class="nb">.</span> <span class="nt">-create</span> /Users/gholms PrimaryGroupID 20
<span class="gp">#</span><span class="w"> </span>dscl <span class="nb">.</span> <span class="nt">-create</span> /Users/gholms NFSHomeDirectory /Local/Users/gholms
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">UniqueID</code>, the user’s ID number, can be any number greater than 500. The setup program normally uses 501 for the first user, so we do, too. The <code class="language-plaintext highlighter-rouge">PrimaryGroupID</code> of 20 corresponds to the <code class="language-plaintext highlighter-rouge">staff</code> group that all local users belong to.</p>
<p>The <code class="language-plaintext highlighter-rouge">/Local</code> portion of the home directory looks a bit strange, but it is mandatory and the new user will not be able to log in if it isn’t there.</p>
<p>Next, set a password for the new user and add that user to the <code class="language-plaintext highlighter-rouge">admin</code> group.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>dscl <span class="nb">.</span> <span class="nt">-passwd</span> /Users/gholms
<span class="gp">#</span><span class="w"> </span>dscl <span class="nb">.</span> <span class="nt">-append</span> /Group/admin GroupMembership gholms
</code></pre></div></div>
<p>Admin users on Mac OS are typically members of far more than just the <code class="language-plaintext highlighter-rouge">admin</code> group. Skipping them may break some things, but I don’t log in using the admin account on my machine and it has yet to cause me any issues. Just be warned that it is not quite a stock configuration.</p>
<p>After this, create the new user’s home directory.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span><span class="nb">install</span> <span class="nt">-d</span> <span class="nt">-o</span> 501 <span class="nt">-g</span> 20 /Users/gholms
</code></pre></div></div>
<p>Finally, tell the system that initial setup has already run and then reboot.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span><span class="nb">touch</span> /private/var/db/.AppleSetupDone
<span class="gp">#</span><span class="w"> </span>reboot
</code></pre></div></div>
<p>After reaching this point I unlocked the disk using the disk password, logged in as the new admin user, then created a non-admin user using System Preferences as usual.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Ultimately, it is still possible to continue using a disk password to boot Mac OS, but the tooling in 10.14 is far more hostile to it. To make matters worse, it does not appear to be possible to add recovery keys to such disks either:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>fdesetup changerecovery <span class="nt">-personal</span>
<span class="go">Enter the user name:
</span></code></pre></div></div>
<p>Apple’s intentions are certainly good — after all, they are simply trying to keep people from locking themselves out of their data by doing things that they don’t understand. Apple’s mistake in this release was to stop treating disk passwords and user passwords as equals and instead to consider disk passwords a degenerate case. That said, Apple could still make this scenario less painful without compromising this goal by making a few small tweaks:</p>
<ol>
<li>Allow one to add a recovery key to a volume with no users. A recovery key can only make it <em>harder</em> to lock oneself out because it provides an additional means of unlocking the disk.</li>
<li>Allow disk passwords to unlock the disk even when user passwords are present. Again, as an additional means of unlocking the disk this can only make it more difficult to lock oneself out.</li>
<li>Allow one to delete the last user on a volume when a disk password is present. Disk passwords are not normally present on the boot volume, so when one is there it is there intentionally and one should be able to use it.</li>
<li>Do not insist on adding the initial user to a volume during system setup when a disk password is present. Again, when a disk password is present it is there for a reason and should be usable.</li>
</ol>
<p>It is my hope that future releases of Mac OS will make separate FileVault disk passwords easier to manage than they are today, or at the very least, not make them <em>more</em> difficult. Disk passwords and user passwords are both useful use cases, and it is possible to support them equally without compromising the user experience for the majority who use only user passwords or making it easier for them to shoot themselves in the feet. Either way, at the moment this process seems to work. Documenting it is sure to help someone else out there who has the same needs as me.</p>{"github"=>"gholms", "twitter"=>"gholms"}I previously wrote about how to make Mac OS’s FileVault disk encryption feature use separate passwords for unlocking the disk and logging into the system once it is running. This allows for better separation of concerns, but it goes against the proverbial grain, as the front end for FileVault tries its best to keep the passwords for unlocking the disk in sync with passwords for user accounts in an effort to keep people from locking themselves out of their machines.How to Get Credentials on Eucalyptus 4.22016-03-10T08:25:23-08:002016-03-10T08:25:23-08:00https://www.devzero.com/how-to-get-credentials-on-eucalyptus-4-2<p>If you run <code class="language-plaintext highlighter-rouge">euca_conf --get-credentials</code> on eucalyptus 4.2 you will see the following warning:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>warning: euca_conf is deprecated; use ``eval `clcadmin-assume-system-credentials`'' or ``euare-useraddkey''`
</code></pre></div></div>
<p>There are numerous reasons for that command’s deprecation, but what causes confusion is the fact that it has two replacements. Setting up a new cloud now involves more than just one set of credentials, and if you’re used to having fully-functional credentials immediately this is likely to trip you up.</p>
<h3 id="why-change">Why Change?</h3>
<p>One of the most common complaints about <code class="language-plaintext highlighter-rouge">euca_conf</code> is that it tries to be everything to everybody. It combines multiple types of functionality that need to run in different places, adding excess dependencies and requiring one to log into systems that one normally shouldn’t have to. Eucalyptus 4.2 introduces new administration tools that break <code class="language-plaintext highlighter-rouge">euca_conf</code>’s functionality down into three groups with more specific purposes:</p>
<ol>
<li>Whole-cloud administration tools</li>
<li>Cloud controller (CLC) support scripts</li>
<li>Cluster controller (CC) support scripts</li>
</ol>
<p>Cloud controller and cluster controller support scripts can run only on those specific systems, and thus are only installed alongside them. The rest of the administration tools are web service clients, similar to euca2ools, that can run from anywhere. All they need are access keys.</p>
<p>But where do those access keys come from?</p>
<h3 id="out-with-the-old">Out with the Old</h3>
<p>In the old regime, access keys and other credentials come in the form of a zip file containing a bunch of certificates as well as <code class="language-plaintext highlighter-rouge">eucarc</code>, a shell script that sets a bunch of environment variables that include service URLs and the access keys themselves. The first zip file it creates is missing several service URLs because those services have yet to be set up, and it doesn’t use DNS either because that has yet to be set up as well.</p>
<p>Once DNS and all of the services are ready, we then have the cloud generate a new zip file. Everything seems fine until something changes for whatever reason and we need to obtain a third one. Since we can only have two certificates at a time, though, this third zip file will not include one. This causes countless problems for automation that relies on them, including eucalyptus’s own QA scripts.</p>
<p>That said, the zip file still has some particularly useful properties:</p>
<ul>
<li>It’s a single file for the administrator to e-mail to new users</li>
<li>It contains both access keys and service URLs</li>
<li>It (usually) contains all of the certificates needed to bundle images</li>
</ul>
<p>A <a href="http://docs.hpcloud.com/eucalyptus/#shared/euca2ools_working_with_config_files.html"><code class="language-plaintext highlighter-rouge">euca2ools.ini</code></a> file also has the first two of those properties, while also managing to be more flexible. Any euca2ools commands that can create access keys, such as euare-useraddkey and euare-usercreate, can generate euca2ools.ini files automatically. That leaves just certificates, which we dealt with by making them all optional or possible to obtain automatically.</p>
<h3 id="in-with-the-new">In with the New</h3>
<p>In isolation, euca2ools commands alone have a chicken-and-egg problem: they require access keys to run, but a new cloud doesn’t have any access keys. We break this loop by splitting eucalyptus installation into two phases, each with different credentials.</p>
<h4 id="setup-credentials">Setup Credentials</h4>
<p>A cloud controller support script, <code class="language-plaintext highlighter-rouge">clcadmin-assume-system-credentials</code>, <a href="http://docs.hpcloud.com/eucalyptus/#install-guide/registering_ufs.html">provides</a> temporary <em>setup credentials</em>. This script works similarly to <code class="language-plaintext highlighter-rouge">euare-assumerole</code>, but it is much more limited and it only works on a cloud controller. Setup credentials <em>cannot be used for normal system operation</em>; they provide access only to service registration, service configuration, and IAM services – the minimum necessary to get up and running with euca2ools.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span><span class="nb">eval</span> <span class="sb">`</span>clcadmin-assume-system-credentials<span class="sb">`</span>
<span class="gp">#</span><span class="w"> </span>euserv-register-service <span class="nt">-t</span> user-api <span class="nt">-h</span> 198.51.100.2 ufs-1
<span class="gp">#</span><span class="w"> </span>euctl system.dns.dnsdomain<span class="o">=</span>mycloud.example.com
</code></pre></div></div>
<h4 id="admin-credentials">Admin Credentials</h4>
<p>Once DNS and an IAM service are set up, you can use euca2ools to <a href="http://docs.hpcloud.com/eucalyptus/#install-guide/credentials_admin_create.html">create</a> long-lived <em>admin credentials</em> that let you access the cloud’s full functionality. It is <em>these</em> credentials that are the replacements for the zip file. Once you create them, you are unlikely to ever need setup credentials again.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>euare-usercreate <span class="nt">-wld</span> mycloud.example.com gholms <span class="o">></span> ~gholms/.euca/mycloud.ini
</code></pre></div></div>
<p>Here is an explanation of the various parts of that command:</p>
<ul>
<li><strong><code class="language-plaintext highlighter-rouge">gholms</code></strong>: Create a user named gholms</li>
<li><strong><code class="language-plaintext highlighter-rouge">-w</code></strong>: Write out a euca2ools.ini file</li>
<li><strong><code class="language-plaintext highlighter-rouge">-l</code></strong>: In that file, make that user the default for this cloud</li>
<li><strong><code class="language-plaintext highlighter-rouge">-d mycloud.example.com</code></strong>: Use the domain <code class="language-plaintext highlighter-rouge">mycloud.example.com</code> as the cloud’s DNS domain</li>
</ul>
<p>Normally, when this command writes a configuration file it will pull the DNS domain from the IAM service’s URL, but since this is the very first user we have to supply it by hand because it has not yet been set.</p>
<p>If you are interested in using eucalyptus’s administration roles instead of full-blown admin credentials, you would create a new account here and add it to whatever roles you need.</p>
<h3 id="what-now">What now?</h3>
<p>Once you have a set of admin credentials you can use this for day-to-day cloud administration the same way you would with a classic <code class="language-plaintext highlighter-rouge">eucarc</code> file.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">export </span><span class="nv">AWS_DEFAULT_REGION</span><span class="o">=</span>mycloud.example.com
<span class="gp">$</span><span class="w"> </span>euare-accountcreate <span class="nt">-wl</span> alice <span class="o">></span> alice.ini
<span class="gp">$</span><span class="w"> </span>mail <span class="nt">-s</span> <span class="s2">"Try out this shiny, new cloud"</span> <span class="nt">-a</span> alice.ini ...
</code></pre></div></div>
<p>You can change the name of the region in the configuration file if you want. The domain name is simply the default.</p>{"github"=>"gholms", "twitter"=>"gholms"}If you run euca_conf --get-credentials on eucalyptus 4.2 you will see the following warning:Useful Git Commands: a Shortcut for GitHub Pull Requests2015-11-03T14:23:36-08:002015-11-03T14:23:36-08:00https://www.devzero.com/useful-git-commands-a-shortcut-for-github-pull-requests<p>Normally, when someone asks me to merge something in git I need to add his or her repository using <code class="language-plaintext highlighter-rouge">git remote add</code>, fetch the branch I need, and then merge it. When someone submits a pull request to a project hosted on GitHub, however, GitHub additionally publishes it as something I can fetch from my own repository:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>git ls-remote origin | <span class="nb">grep </span>pull/6
<span class="go">03d7fb7af91a74bb7658a0742fa68bfeb5d50a3f refs/pull/6/head
f8ebbc62555143019947f1255b064cda38fd239f refs/pull/6/merge
8a42021dbabcfc222b4d47c5ada47e344154d934 refs/pull/60/head
f4d8052000601e59e4e7d4dec4aa4094df4e39a0 refs/pull/61/head
8a519986f5b59721692ec75608edf0f404f88e87 refs/pull/62/head
9c03e723e8b50ca56a1257659d686a68a69e6e40 refs/pull/62/merge
f3a983c6fc3ff236c2bc678cfec3885da609f79a refs/pull/63/head
e7953c21a77fb37fb7158dd87fc0c156dd8f97ae refs/pull/63/merge
</span></code></pre></div></div>
<p>With this I can use one line of configuration to create a convenient shortcut that lets me immediately check out any pull request:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>git config <span class="nt">--add</span> remote.origin.fetch <span class="s2">"+refs/pull/*/head:refs/remotes/origin/pull/*"</span>
<span class="gp">$</span><span class="w"> </span>git checkout pull/63
<span class="go">Branch pull/63 set up to track remote branch pull/63 from upstream.
Switched to a new branch 'pull/63'
</span></code></pre></div></div>{"github"=>"gholms", "twitter"=>"gholms"}Normally, when someone asks me to merge something in git I need to add his or her repository using git remote add, fetch the branch I need, and then merge it. When someone submits a pull request to a project hosted on GitHub, however, GitHub additionally publishes it as something I can fetch from my own repository:Useful Git Commands: URL Rewriting2014-08-29T06:50:10-07:002014-08-29T06:50:10-07:00https://www.devzero.com/useful-git-commands-url-rewriting<p>People have to use SSH or HTTPS to push to GitHub, but when fetching one can use git’s own network protocol because it is generally faster. You can make a specific repository on your machine use SSH only for pushing by cloning it with the faster <code class="language-plaintext highlighter-rouge">git://</code> URL and running something like this:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>git config remote.upstream.pushurl git@github.com:gholms/boto
</code></pre></div></div>
<p>That works nicely, but you have to do it once for every single repository you want to interact with. That quickly becomes annoying. Thankfully, you can leverage git’s URL-rewriting mechanism to make this easier:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>git config <span class="nt">--global</span> url.git://github.com/.insteadOf github:
<span class="gp">$</span><span class="w"> </span>git config <span class="nt">--global</span> url.git@github.com:.pushInsteadOf github:
</code></pre></div></div>
<p>This adds two new rules to your git configuration:</p>
<ol>
<li>If a URL starts with <code class="language-plaintext highlighter-rouge">github:</code> then replace that with <code class="language-plaintext highlighter-rouge">git://github.com/</code>.</li>
<li>If a URL starts with <code class="language-plaintext highlighter-rouge">github:</code> <em>and you are pushing</em> then replace that with <code class="language-plaintext highlighter-rouge">git@github.com:</code>.</li>
</ol>
<p>After you do that you can simply use URLs like <code class="language-plaintext highlighter-rouge">github:gholms/boto</code> when cloning. They will get rewritten to <code class="language-plaintext highlighter-rouge">git@github.com:gholms/boto</code> when pushing, and <code class="language-plaintext highlighter-rouge">git://github.com/gholms/boto</code> the rest of the time, speeding things up without creating additional work in the future.</p>
<p>This should work if you prefer HTTPS for pushing to GitHub, or if you use other servers, too. Just tweak the commands.</p>{"github"=>"gholms", "twitter"=>"gholms"}People have to use SSH or HTTPS to push to GitHub, but when fetching one can use git’s own network protocol because it is generally faster. You can make a specific repository on your machine use SSH only for pushing by cloning it with the faster git:// URL and running something like this:Useful Git Commands: Summarizing Lots of Log Entries2014-04-02T05:06:34-07:002014-04-02T05:06:34-07:00https://www.devzero.com/useful-git-commands-summarizing-lots-of-log-entries<p>When looking for a summary of a git repository’s history, the output of <code class="language-plaintext highlighter-rouge">git log</code> isn’t always as informative as one might like. It displays every commit in chronological order, which effectively hides the changes that merges bring in. It is also quite verbose, showing complete log messages, author info, commit hashes, and so on, drowning us with so much info that only a few commits will fit on the screen at once. After supplying the command with the right cocktail of options, though, its output becomes a significantly better summary:</p>
<p><img src="/assets/images/2014-04-02-useful-git-commands-summarizing-lots-of-log-entries/git-graph.png" alt="Output of ``git graph''" /></p>
<p>The output above came from a command that is long enough that I made an alias, for it, <code class="language-plaintext highlighter-rouge">git graph</code>, in my <code class="language-plaintext highlighter-rouge">~/.gitconfig</code> file:</p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[alias]</span>
<span class="py">graph</span> <span class="p">=</span> <span class="s">log --graph --abbrev-commit --date=relative --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(blue)<%an>%Creset'</span>
</code></pre></div></div>
<p>Don’t forget that <code class="language-plaintext highlighter-rouge">git log</code> accepts a list of things to show logs for as well, so if you want to look at the logs for <code class="language-plaintext highlighter-rouge">branch-1</code> and <code class="language-plaintext highlighter-rouge">branch-2</code> you can simply use <code class="language-plaintext highlighter-rouge">git graph branch-1 branch-2</code> to make them both show up in the graph.</p>{"github"=>"gholms", "twitter"=>"gholms"}When looking for a summary of a git repository’s history, the output of git log isn’t always as informative as one might like. It displays every commit in chronological order, which effectively hides the changes that merges bring in. It is also quite verbose, showing complete log messages, author info, commit hashes, and so on, drowning us with so much info that only a few commits will fit on the screen at once. After supplying the command with the right cocktail of options, though, its output becomes a significantly better summary:Making FileVault Use a Disk Password2014-03-31T04:44:28-07:002014-03-31T04:44:28-07:00https://www.devzero.com/making-filevault-use-a-disk-password<p>To unlock a disk that is encrypted with OS X’s FileVault feature one needs to type in the password that belongs to any user on the machine who is allowed to unlock the disk. The system then boots and helpfully logs you in as that user. In general that is probably a convenient little feature, but for me it just makes things awkward – I want to use different passwords for unlocking the disk and logging into my user account. To make that work I have to create a second account dedicated to unlocking the disk, get logged into that one when the system boots, then immediately log back out so I can log in as the user I actually want to use.</p>
<p>Or do I?</p>
<p>The system that powers FileVault, <em>Core Storage</em>, combines full disk encryption and some logical volume management features in a manner similar to LVM and LUKS on Linux. As a dedicated user of those features on my Linux-based machines, I jumped at the chance to read more about OS X’s version only to discover next to no official documentation whatsoever. On the bright side, after searching some more and then playing around with it for a while I finally figured out how to make it do everything I wanted it to do, including using a disk password. You just have to boot into recovery (or, in my case, the OS X 10.9 install DVD that I threw together) and use the command line.</p>
<h3 id="create-a-partition">Create a partition</h3>
<p>Since this is a laptop I want to put everything on one huge partition. The utility that handles nearly everything related to disk management on OS X, <code class="language-plaintext highlighter-rouge">diskutil</code>, requires us to choose a type of filesystem for every partition we tell it to create, so for now we’ll just tell it to mark the partition as HFS+ and not bother to format it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>diskutil partitiondisk disk0 1 gpt jhfs+ %noformat% 100%
<span class="go">Started partitioning on disk0
Unmounting disk
Creating the partition map
Waiting for the disks to reappear
Finished partitioning on disk0
/dev/disk0
</span><span class="gp"> #</span>: TYPE NAME SIZE IDENTIFIER
<span class="go"> 0: GUID_partition_scheme *256.1 GB disk0
1: EFI EFI 209.7 MB disk0s1
2: Apple_HFS 255.7 GB disk0s2
</span></code></pre></div></div>
<p>The system actually created <em>two</em> partitions for us. The first is a small EFI system partition, which helps the system start, and the second is the big partition for our data that spans the rest of the disk.</p>
<p>Or does it?</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>gpt show disk0
<span class="go"> start end index contents
0 1 PMBR
1 1 Pri GPT header
2 32 Pri GPT header
34 6
40 409600 1 GPT part - C12A7328-F81F-11D2-BA4B-00A0C93EC93B
409640 499446368 2 GPT part - 48465300-0000-11AA-AA11-00306543ECAC
499856008 262151
500118159 32 Sec GPT table
500118191 1 Sec GPT header
</span></code></pre></div></div>
<p>It seems to have left a little empty space in between the last partition and the boilerplate bits at the end of the disk. More on that later.</p>
<h3 id="create-a-volume-group">Create a volume group</h3>
<p>The next step is quite familiar to LVM users. Before we can add encryption we need to create a <em>logical volume group</em> that combines one or more partitions (<em>physical volumes</em>) into a single virtual “disk” that we can slice up.</p>
<p>Why more than one disk? That’s how <a href="https://en.wikipedia.org/wiki/Fusion_Drive">Fusion Drive</a> works.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>diskutil cs create vg_sodium disk0s2
<span class="go">Started CoreStorage operation
Touching partition type on disk0s2
Adding disk0s2 to Logical Volume Group
Creating Core Storage Logical Volume Group
Switching disk0s2 to Core Storage
Waiting for Logical Volume Group to appear
Discovered new Logical Volume Group "A642DBC3-644C-4C23-8337-ADBCDD9C85F2"
Core Storage LVG UUID: A642DBC3-644C-4C23-8337-ADBCDD9C85F2
Finished CoreStorage operation
</span><span class="gp">#</span><span class="w"> </span>diskutil cs list
<span class="go">CoreStorage logical volume groups (1 found)
|
+-- Logical Volume Group A642DBC3-644C-4C23-8337-ADBCDD9C85F2
=========================================================
Name: vg_sodium
Status: Online
Size: 255716540416 B (255.7 GB)
Free Space: 255380987904 B (255.4 GB)
|
+-< Physical Volume A39FA7E9-F52F-4FFA-9A70-F07304111115
----------------------------------------------------
Index: 0
Disk: disk0s2
Status: Online
Size: 255716540416 B (255.7 GB)
</span><span class="gp">#</span><span class="w"> </span>diskutil list disk0
<span class="go">/dev/disk0
</span><span class="gp"> #</span>: TYPE NAME SIZE IDENTIFIER
<span class="go"> 0: GUID_partition_scheme *256.1 GB disk0
1: EFI EFI 209.7 MB disk0s1
2: Apple_CoreStorage 255.7 GB disk0s2
3: Apple_Boot Boot OS X 134.2 MB disk0s3
</span></code></pre></div></div>
<p>This changed the type of the second partition and constructed a volume group out of it, but it also created a third partition on which it can put whatever minimal data are needed to allow the system to boot from a Core Storage volume. With a quick look at the partition table we can see that it carved this out of the space it reserved at the end of the disk earlier:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>gpt show disk0
<span class="go"> start end index contents
0 1 PMBR
1 1 Pri GPT header
2 32 Pri GPT header
34 6
40 409600 1 GPT part - C12A7328-F81F-11D2-BA4B-00A0C93EC93B
409640 499446368 2 GPT part - 48465300-0000-11AA-AA11-00306543ECAC
499856008 262144 3 GPT part - 426F6F74-0000-11AA-AA11-00306543ECAC
500118152 7
500118159 32 Sec GPT table
500118191 1 Sec GPT header
</span></code></pre></div></div>
<h3 id="create-a-logical-volume">Create a logical volume</h3>
<p>LVM has three layers: physical volumes, volume groups, and logical volumes. Since Core Storage includes encryption it adds a fourth layer, the <em>logical volume family</em>, between the volume group and logical volume levels. When you boot a FileVault-enabled system it’s the volume family that your password unlocks. The final step before we can install OS X is to create an encrypted volume family and the logical volume that we will actually install onto.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>diskutil cs createvolume vg_sodium jhfs+ <span class="s1">'Macintosh HD'</span> 100% <span class="nt">-passphrase</span>
<span class="go">Passphrase for new volume:
Confirm new passphrase:
Started CoreStorage operation
Waiting for Logical Volume to appear
Formatting file system for Logical Volume
Initialized /dev/rdisk13 as a 238 GB case-insensitive HFS Plus volume with a 24576k journal
Mounting disk
Core Storage LV UUID: 46A6CA83-9CDC-4978-ADF2-E4DC3F203DBD
Core Storage disk: disk13
Finished CoreStorage operation
</span><span class="gp">#</span><span class="w"> </span>diskutil cs list
<span class="go">CoreStorage logical volume groups (1 found)
|
+-- Logical Volume Group A642DBC3-644C-4C23-8337-ADBCDD9C85F2
=========================================================
Name: vg_sodium
Status: Online
Size: 255716540416 B (255.7 GB)
Free Space: 0 B (0 B)
|
+-< Physical Volume A39FA7E9-F52F-4FFA-9A70-F07304111115
| ----------------------------------------------------
| Index: 0
| Disk: disk0s2
| Status: Online
| Size: 255716540416 B (255.7 GB)
|
</span><span class="gp"> +-></span><span class="w"> </span>Logical Volume Family AC7B483C-0524-4ACF-8083-9EFD963F81A5
<span class="go"> ----------------------------------------------------------
Encryption Status: Unlocked
Encryption Type: AES-XTS
Conversion Status: Complete
Conversion Direction: -none-
Has Encrypted Extents: Yes
Fully Secure: Yes
Passphrase Required: Yes
|
</span><span class="gp"> +-></span><span class="w"> </span>Logical Volume 46A6CA83-9CDC-4978-ADF2-E4DC3F203DBD
<span class="go"> ---------------------------------------------------
Disk: disk13
Status: Online
Size (Total): 255380987904 B (255.4 GB)
Conversion Progress: -none-
Revertable: No
LV Name: Macintosh HD
Volume Name: Macintosh HD
Content Hint: Apple_HFS
</span></code></pre></div></div>
<p>Now we have a disk called “Macintosh HD” that is encrypted with its own non-user-specific password, formatted, and ready to go. Just switch back to the OS X installer and let it run.</p>
<h3 id="create-a-recovery-key">Create a recovery key</h3>
<p>After the installer finishes and the system reboots we see the familiar FileVault login screen with just one option: “Disk Password.” Success! Now we just enter that, run through the usual first-time boot stuff, and finally open up a terminal to create a recovery key:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>fdesetup changerecovery <span class="nt">-personal</span>
<span class="go">Enter a password for '/':
New recovery key = 'XPO6-E4OL-XQG6-TGV9-GFOZ-GB8M'
</span></code></pre></div></div>
<h3 id="add-more-users">Add more users</h3>
<p>Since I generally don’t log in with an administrative account I created a second, regular user, but then the next time I rebooted I discovered a problem: the FileVault password screen asked for the disk password <em>or</em> the new user’s password. Thankfully, removing that user from the list of choices is simple:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>fdesetup remove <span class="nt">-user</span> gholms
</code></pre></div></div>
<h3 id="open-questions">Open questions</h3>
<p>Now that I have gone through all this my machine finally seems to be working exactly the way I want it to, but a few unanswered questions still remain in the back of my mind:</p>
<ul>
<li>How am I supposed to do this on a newer Mac that ships with neither an install DVD nor an upgrade application that I can turn into one?</li>
<li>How do I make OS X stop automatically adding new users to FileVault’s password list?</li>
<li>Where is the official Core Storage documentation?</li>
</ul>
<p>Do know the answers to any of these? Leave a comment!</p>{"github"=>"gholms", "twitter"=>"gholms"}To unlock a disk that is encrypted with OS X’s FileVault feature one needs to type in the password that belongs to any user on the machine who is allowed to unlock the disk. The system then boots and helpfully logs you in as that user. In general that is probably a convenient little feature, but for me it just makes things awkward – I want to use different passwords for unlocking the disk and logging into my user account. To make that work I have to create a second account dedicated to unlocking the disk, get logged into that one when the system boots, then immediately log back out so I can log in as the user I actually want to use.A Useful rtadvd Configuration2014-01-20T08:11:46-08:002014-01-20T08:11:46-08:00https://www.devzero.com/a-useful-rtadvd-configuration<p>When most IPv6-capable computers join a network they attempt to automatically <a href="https://en.wikipedia.org/wiki/Neighbor_Discovery_Protocol">find a router</a> on the network so they can figure out what addresses to use, how to set up routing, and so forth. On BSD systems like my router, the <code class="language-plaintext highlighter-rouge">rtadvd(8)</code> program manages the router’s side of this exchange. While <code class="language-plaintext highlighter-rouge">rtadvd</code> is rather flexible, its configuration file is frustratingly terse and its documentation assumes the reader has a fair amount of knowledge already.</p>
<p>For IPv4, my network uses DHCP to hand out addresses and DNS information. When rolling out IPv6, I wanted to set things up similarly, but without managing addresses centrally with DHCP since machines can configure themselves correctly without one. Configuration like this is the sort of thing that I tend to forget and have to re-learn periodically, so for reference, the <code class="language-plaintext highlighter-rouge">rtadvd.conf</code> file I used for that setup looked something like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vether0:\
:addr="2001:db8:1221::":\
:prefixlen#64:\
:raflags#64:\
:rdnss="2001:db8:1221::1,2001:4860:4860::8888,2001:4860:4860::8844":\
:dnssl="internal.example.com":
</code></pre></div></div>
<p>This makes machines configure themselves for the network <code class="language-plaintext highlighter-rouge">2001:db8:1221::/64</code>, DNS domain <code class="language-plaintext highlighter-rouge">internal.example.com</code>, and three DNS servers: a local one at <code class="language-plaintext highlighter-rouge">2001:db8:1221::1</code>, and both of Google’s public servers.</p>
<p>The source of most of my confusion was figuring out the <code class="language-plaintext highlighter-rouge">raflags</code> option. <code class="language-plaintext highlighter-rouge">raflags</code> is a bit mask with two flags: M, which means a DHCP server manages addresses, and O, which means that non-address-related information (in this case, DNS information) is available that way even if addresses are not. This network setup requires the O flag to be set and the M flag to be unset, which means <code class="language-plaintext highlighter-rouge">raflags</code> has to be 64.</p>
<p>Now that I’ve rolled this out I get all the niceness of auto-configuration without the need for a central DHCP server to keep track of addresses. Well, for IPv6, at least.</p>{"github"=>"gholms", "twitter"=>"gholms"}When most IPv6-capable computers join a network they attempt to automatically find a router on the network so they can figure out what addresses to use, how to set up routing, and so forth. On BSD systems like my router, the rtadvd(8) program manages the router’s side of this exchange. While rtadvd is rather flexible, its configuration file is frustratingly terse and its documentation assumes the reader has a fair amount of knowledge already.Mr. TV2013-12-16T14:36:10-08:002013-12-16T14:36:10-08:00https://www.devzero.com/mr-tv<p>I previously wrote about the big, <a href="/running-a-text-based-kiosk-with-systemd.html">Raspberry Pi</a>-powered <a href="/readable-monospace-fonts.html">TV set</a>
at Eucalyptus HQ that displays the #eucalyptus-devel IRC channel so developers can always see what is going on and jump in if they need to. That setup has worked quite well for some time now, but I recently came up with a way to make it even better:</p>
<p><img src="/assets/images/2013-12-16-mr-tv/mr-tv.jpg" alt="Mr. TV" /></p>
<p><a href="http://www.amazon.com/dp/B008BGP9KQ/">Googly eyes</a>
have yet to fail me at improving a machine’s appearance.</p>{"github"=>"gholms", "twitter"=>"gholms"}I previously wrote about the big, Raspberry Pi-powered TV set at Eucalyptus HQ that displays the #eucalyptus-devel IRC channel so developers can always see what is going on and jump in if they need to. That setup has worked quite well for some time now, but I recently came up with a way to make it even better:Making a Less-Limited USB Stick2013-12-09T07:47:45-08:002013-12-09T07:47:45-08:00https://www.devzero.com/making-a-less-limited-usb-stick<p>The FAT32 filesystem is the closest thing we have to a universal standard for passing data around, but with the capacity of modern USB flash drives its 4 GB file size limitation has become problematic. exFAT is a popular contender for dealing with that, but the patent issues that surround it make true portability a pipe dream at best.</p>
<p>Enter UDF. As the filesystem of choice for DVDs and Blu-Ray disks, UDF support is ubiquitous. Appropriately-formatted disks are readable by operating systems dating back to the early 2000s. All that remains is figuring out how to format it. In general, there seem to be three important things to keep in mind:</p>
<ol>
<li><strong>Remove all traces of previous filesystems.</strong> Different operating systems use different methods to detect what filesystems a disk contains, so ensure maximum reliability by eliminating potential sources of confusion.</li>
<li><strong>Format the entire disk, not just a partition.</strong> OS X seems to only look for UDF when the filesystem takes up the whole disk, as it does on DVDs, so overwrite the partition table before formatting the disk.</li>
<li><strong>Use the correct UDF version.</strong> UDF has a number of versions that add different features, but as you might expect, newer versions require newer operating systems. Windows XP will read up to version 2.01 out of the box, though some Blu-Ray drive manufacturers have released drivers for newer versions.</li>
</ol>
<p>I formatted my most recently-purchased USB stick on Fedora. First off, I blew away the partition table and all traces of the FAT32 filesystem it came with:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span><span class="nb">dd </span><span class="k">if</span><span class="o">=</span>/dev/zero <span class="nv">of</span><span class="o">=</span>/dev/sdb <span class="nv">bs</span><span class="o">=</span>1M <span class="nv">count</span><span class="o">=</span>1
<span class="gp">#</span><span class="w"> </span>wipefs <span class="nt">-a</span> /dev/sdb
</code></pre></div></div>
<p>Fedora uses the <code class="language-plaintext highlighter-rouge">mkudffs</code> command for creating UDF filesystems, which is part of the <code class="language-plaintext highlighter-rouge">udftools</code> package:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="w"> </span>yum <span class="nb">install </span>udftools
<span class="gp">#</span><span class="w"> </span>mkudffs <span class="nt">--revision</span><span class="o">=</span>0x0201 <span class="nt">--media-type</span><span class="o">=</span>hd /dev/sdb
</code></pre></div></div>
<p>That’s it! Now the disk seems to be usable on Fedora, OS X, and Windows, which covers just about all of my computers. I will test OpenBSD one of these days.</p>
<p>There are undoubtedly ways to do this on other operating systems. Feel free to comment with instructions for your favorite operating system if you know them.</p>{"github"=>"gholms", "twitter"=>"gholms"}The FAT32 filesystem is the closest thing we have to a universal standard for passing data around, but with the capacity of modern USB flash drives its 4 GB file size limitation has become problematic. exFAT is a popular contender for dealing with that, but the patent issues that surround it make true portability a pipe dream at best.Every DNS Server Should Support Aliases2013-07-22T05:57:46-07:002013-07-22T05:57:46-07:00https://www.devzero.com/every-dns-server-should-support-aliases<p>Amazon’s <a href="http://aws.amazon.com/route53/">Route53</a> DNS service, along with several content delivery networks and other DNS providers let one create an “alias” pseudo-record that causes the server to respond to requests for one name with results for another name. While the ways current implementations of this function vary a bit, the biggest difference between all of them and a <code class="language-plaintext highlighter-rouge">CNAME</code> is that while a <code class="language-plaintext highlighter-rouge">CNAME</code> gets applied to every query regardless of the type of record something is looking for, an alias is specific to just one type of record.</p>
<p>While this sounds like a trivial difference, the benefits are surprisingly enormous. The most obvious effect is that it lets you point a bare domain name (e.g. <code class="language-plaintext highlighter-rouge">example.com</code>) at something else (e.g. <code class="language-plaintext highlighter-rouge">www.example.com</code>). The reason you can’t normally do this is because the <code class="language-plaintext highlighter-rouge">CNAME</code> record you would normally use to do this would conflict with the <code class="language-plaintext highlighter-rouge">SOA</code> record at the top of your domain, but since the alias you would use for this only applies to <code class="language-plaintext highlighter-rouge">A</code> address records, this is no longer a problem.</p>
<p>Another property aliases have is that they don’t actually go over the wire. While a CNAME record returns to the machine looking up a DNS name, causing it to restart its search with a different name, the answer for an alias comes right out of the DNS server’s own database. This means that aliases can only be used for records for which the server is authoritative or at least has some means of reliably learning the answer it should return, but that’s good enough for a great deal of use cases, notably including those of most content delivery networks. The fact that servers look up what an alias points to before they send anything over the wire means that they can include this functionality without violating standards – no one else needs to change their servers or their clients to support it. If DNS standards evolve to support it in the future, this makes transitioning even easier as that change rolls out.</p>
<p>In short, aliases would solve one of the most commonly-encountered shortcomings of DNS, namely its inability to use a CNAME to point a bare domain at its www equivalent. Given that there are multiple proprietary systems out there that do this already, it’s about time we standardized on an approach.</p>{"github"=>"gholms", "twitter"=>"gholms"}Amazon’s Route53 DNS service, along with several content delivery networks and other DNS providers let one create an “alias” pseudo-record that causes the server to respond to requests for one name with results for another name. While the ways current implementations of this function vary a bit, the biggest difference between all of them and a CNAME is that while a CNAME gets applied to every query regardless of the type of record something is looking for, an alias is specific to just one type of record.