<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Robert Ou Labs</title><link href="https://robertou.com/" rel="alternate"></link><link href="https://robertou.com/feeds/all.atom.xml" rel="self"></link><id>https://robertou.com/</id><updated>2018-05-16T01:00:00-07:00</updated><entry><title>Unofficial open-source place-and-route for Xilinx Coolrunner-II CPLDs</title><link href="https://robertou.com/unofficial-open-source-place-and-route-for-xilinx-coolrunner-ii-cplds.html" rel="alternate"></link><published>2018-05-16T01:00:00-07:00</published><updated>2018-05-16T01:00:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2018-05-16:unofficial-open-source-place-and-route-for-xilinx-coolrunner-ii-cplds.html</id><summary type="html">&lt;p&gt;Recently, I have been working on an open-source tool for producing bitstreams
for Xilinx Coolrunner-II CPLDs that I have named xc2par. It is finally at a
point where I no longer feel embarrassed to have other people try and use it.&lt;/p&gt;
&lt;h1&gt;Quick start&lt;/h1&gt;
&lt;p&gt;The first tool you will need is &lt;a href="https://github.com/YosysHQ/yosys"&gt;yosys&lt;/a&gt;. You
need a version that contains commit &lt;code&gt;14e49fb05737cfb02217b2aaf14d9f5d9e1859da&lt;/code&gt;,
but it is recommended to use the latest master branch. Follow the instructions
in the yosys README to install it (normally I would point people to my
pre-built binaries, but those are currently broken).&lt;/p&gt;
&lt;p&gt;Next, you will need to install the compiler for the
&lt;a href="https://www.rust-lang.org/"&gt;Rust&lt;/a&gt; programming language.&lt;/p&gt;
&lt;p&gt;After installing Rust, clone the
&lt;a href="https://github.com/azonenberg/openfpga"&gt;"openfpga"&lt;/a&gt; repository. Change
directories into the &lt;code&gt;src/xc2par&lt;/code&gt; directory and run &lt;code&gt;cargo build --release&lt;/code&gt;.
When this completes, the final command-line tool will be in
&lt;code&gt;target/release/xc2par&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point you are ready to try compiling some HDL! Unfortunately, xc2par
does not currently accept exactly the same code as the proprietary Xilinx tools,
and there is also currently no documentation on what is or is not accepted. You
will have to either guess or read the source code to figure out what currently
works. Most notably, &lt;code&gt;.ucf&lt;/code&gt; constraint files are not supported (you must use
&lt;code&gt;LOC&lt;/code&gt; attributes, and they must use the function block and macrocell number
rather than a package pin), and automatic insertion of &lt;code&gt;BUFG&lt;/code&gt;s is not supported.
However, to get started, the following is some example Verilog for an LED
blinker:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;led0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;led1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;led2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;led3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clk_&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// NOTE: Must use LOC attributes rather than a .ucf file&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;LOC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;FB1_9&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="n"&gt;led0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;LOC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;FB1_10&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="n"&gt;led1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;LOC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;FB1_11&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="n"&gt;led2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;LOC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;FB1_12&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="n"&gt;led3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;LOC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;FB2_5&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;input&lt;/span&gt; &lt;span class="n"&gt;clk_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// NOTE: Must manually instantiate BUFG&lt;/span&gt;
&lt;span class="kt"&gt;wire&lt;/span&gt; &lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="no"&gt;BUFG&lt;/span&gt; &lt;span class="n"&gt;bufg0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;I&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk_&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;reg&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;23&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mh"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;assign&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;led3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;led2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;led1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;led0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;23&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mh"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;always&lt;/span&gt; &lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="k"&gt;posedge&lt;/span&gt; &lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;endmodule&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To compile this code, use the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;yosys -p &lt;span class="s2"&gt;&amp;quot;synth_coolrunner2 -json blinky.json&amp;quot;&lt;/span&gt; blinky.v
./target/release/xc2par -p xc2c32a-4-vq44 blinky.json
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;At this point, if everything worked correctly, there will be a &lt;code&gt;blinky.jed&lt;/code&gt; file
in the same directory as the &lt;code&gt;blinky.json&lt;/code&gt; file. It should now be possible to
program this file into a CPLD to test it out. However, embarrassingly, there
currently isn't any code in the xc2par project that can help with that. You will
either need to use&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ISE/iMPACT (either to generate a &lt;code&gt;.svf&lt;/code&gt; file or to program parts directly)&lt;/li&gt;
&lt;li&gt;Andrew Zonenberg's &lt;a href="https://github.com/azonenberg/jtaghal"&gt;jtaghal&lt;/a&gt; (only
  supports the 32A and 64A parts)&lt;/li&gt;
&lt;li&gt;Some other JTAG programmer that can read Xilinx &lt;code&gt;.jed&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;The Origins of xc2par&lt;/h1&gt;
&lt;p&gt;In the beginning, there was only
&lt;a href="https://twitter.com/azonenberg"&gt;Andrew Zonenberg&lt;/a&gt;. Back in 2014, Andrew took
some CPLDs and exposed them to
&lt;a href="http://siliconexposed.blogspot.com/2014/03/getting-my-feet-wet-with-invasive.html"&gt;a slew of nasty chemicals and some high-energy electrons&lt;/a&gt;.
This culminated in a talk presented at &lt;a href="https://recon.cx/2015/slides/recon2015-18-andrew-zonenberg-From-Silicon-to-Compiler.pdf"&gt;REcon 2015&lt;/a&gt;.
At this point in time, the PLA (AND and OR gates) and interconnect were
understood but the macrocell configuration was not. Although Andrew had always
wanted to write an open-source compiler for these chips, yosys did not have
support for sum-of-products architectures at this point in time. This combined
with Life™ led to the project being temporarily shelved.&lt;/p&gt;
&lt;h1&gt;Robert Has Joined the Party&lt;/h1&gt;
&lt;p&gt;Being a "hacker" with extremely varied interests, I had been lurking Andrew's
work for years (since at least 2012). I had an interest in both programmable
logic devices and reverse engineering. However, since I was pretty busy with
Life™ of my own, I had never considered ever contacting Andrew or collaborating.
However, one of Andrew's projects combined with perfect timing changed this.&lt;/p&gt;
&lt;h2&gt;Quick Detour: Introducing GreenPak&lt;/h2&gt;
&lt;p&gt;I first heard about these interesting tiny programmable logic devices from
&lt;a href="https://hackaday.com/2015/03/30/new-part-day-modern-pals/"&gt;an article on Hackaday&lt;/a&gt;.
An interesting feature of these parts is that the bitstream format is completely
documented in the datasheet. After reading the article, I purchased a GreenPak
development kit with the idea of making "some kind of domain-specific-language"
for programming these parts (I was not ambitious enough to consider supporting
a traditional HDL, and I was not yet aware that yosys existed at the time).
However, since I was still working on Life™ (being a university student), this
project also got shelved.&lt;/p&gt;
&lt;p&gt;However, in the meantime, Andrew was working on
&lt;a href="http://siliconexposed.blogspot.com/2016/05/open-verilog-flow-for-silego-greenpak4.html"&gt;his open-source Verilog flow for GreenPak&lt;/a&gt;.
This tool just so happened to be released almost exactly around the time that I
finished my undergrad degree. Since I had planned to take a year off to remain
"funemployed," I decided to introduce myself to Andrew and joined the
##openfpga IRC community on Freenode somewhere around this time. Andrew
eventually suggested that I could work on a place-and-route for Coolrunner-II
parts (yosys support for sum-of-products had also gotten added around this
time).&lt;/p&gt;
&lt;h1&gt;Finishing the Reverse Engineering&lt;/h1&gt;
&lt;p&gt;When Andrew had originally set aside the Coolrunner-II toolchain project back
in 2015, the macrocell configuration was not yet understood. Andrew suggested
that I ignore the macrocell-related parts and write tooling to handle the other
parts of the device first. Macrocells would be configured by copying the entire
block of bits from an existing ISE-generated design. Dissatisfied with this
answer, I decided to schedule an IRL hackathon with Andrew to figure out all of
the remaining bits. This resulted in the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Whiteboard drawing of Coolrunner-II macrocell" src="https://robertou.com/static/xc2-re-whiteboard.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;Note that this diagram is not accurate and has errors. A more accurate
diagram needs to be drawn eventually.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Now with all the bits understood, we could begin writing tooling that did not
require any strange hacks.&lt;/p&gt;
&lt;h1&gt;xc2bit and xc2par are Born&lt;/h1&gt;
&lt;p&gt;At the end of May 2017, the &lt;a href="https://github.com/azonenberg/openfpga/commit/eac55c88f0466c28c4ef43fe616a0a9370082afd"&gt;first commit&lt;/a&gt;
of the "xc2bit" code is created. This is a Rust library for performing low-level
manipulations of Coolrunner-II bitstreams. This is analogous to Andrew's
never-finished libcrowbar library. The &lt;a href="https://github.com/azonenberg/openfpga/commit/7a411f5296496554b2a8771afcb0728c29c6fbab"&gt;first commit&lt;/a&gt;
of xc2par is created a few days later.&lt;/p&gt;
&lt;h2&gt;First Attempt: xbpar&lt;/h2&gt;
&lt;p&gt;The first version of xc2par was supposed to use xbpar, Andrew's generic
C++ framework for writing place-and-route tools for "crossbar interconnect"
architectures. The internals of xbpar are described in &lt;a href="http://siliconexposed.blogspot.com/2016/05/open-verilog-flow-for-silego-greenpak4.html"&gt;Andrew's blog post&lt;/a&gt;
about the GreenPak Verilog tooling, but the general idea is that the algorithm
takes as input two graphs, one modeling the target device and one modeling the
user's design, and attempts to check if the user's design graph is a subgraph of
the device structure graph. It does this by attempting to "pair up" nodes of the
two graphs and then checking if the edges between the nodes of the design graph
exist in the device graph. If any edges do not, it attempts to find something
better using simulated annealing.&lt;/p&gt;
&lt;p&gt;Trying to use this library for xc2par had a number of issues. The first issue
was "ideological." I wanted to use Rust for xc2par because I did not like C++
but wanted more powerful libraries and abstractions than are available in C.
Unfortunately, xbpar is a C++ library that isn't very friendly to being
interfaced with a different programming language. Among other things,
device-specific functionality is implemented by deriving from an xbpar base
class and overriding some methods. However, I persevered and attempted to write
glue code between the two languages. Unfortunately, all of this glue code ended
up being more lines of code than xbpar itself.&lt;/p&gt;
&lt;p&gt;A second issue was that the specific mechanism I had used for describing graphs
for xbpar caused extremely poor behavior. I had described every "enable-able or
disable-able" feature of the macrocell as an individual node in xbpar (so the
XOR gate of a macrocell would be a node, the register would be a different 
node, and the IO pad would be yet another different node). However, this would
cause the following behavior:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The greedy initial placement algorithm packs together "the wrong" set of
   "macrocell pieces." For example, it would place the IO pad for pin &lt;code&gt;a&lt;/code&gt; and
   the XOR gate for pin &lt;code&gt;b&lt;/code&gt; both at function block 1 macrocell 1. Meanwhile, the
   XOR gate for pin &lt;code&gt;a&lt;/code&gt; and the IO pad for pin &lt;code&gt;b&lt;/code&gt; both get placed at function
   block 1 macrocell 2.&lt;/li&gt;
&lt;li&gt;The algorithm would notice that there are not proper edges between the nodes.
   In the above example, there is an edge in the device graph between the XOR
   and IO pad of FB1_1, but there are no edges between the IO pad of FB1_1 with
   the XOR gate of FB1_2 or between the XOR gate of FB1_1 with the IO pad of
   FB1_2. Therefore, all components of pins &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; will get added to the
   list of nodes contributing to the bad scores.&lt;/li&gt;
&lt;li&gt;The simulated annealing step picks one of the "bad" nodes and tries to move
   it. It ends up moving e.g. the XOR gate of one pin to a completely different
   random spot. This does not improve the situation since the XOR gate needs to
   be in the same location as all of the other "macrocell bits" for the same
   pin (but it has just been moved somewhere random and unrelated).&lt;/li&gt;
&lt;li&gt;The cycle repeats without ever making progress.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One possible solution for this problem is "why not just model the entire
macrocell as one giant node with a bunch of attributes?" This solution might
work, but it has difficulties with optimal packing of buried nodes. The
Coolrunner-II architecture has the following quirk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A macrocell that needs to output to a pin cannot share a macrocell site with
  anything else&lt;/li&gt;
&lt;li&gt;A buried combinatorial feedback node can share a macrocell site with both a
  direct pin input as well as a pin input that goes through the register&lt;/li&gt;
&lt;li&gt;A buried registered feedback node can share a macrocell site with a direct
  input pin only, and it can only share this site if the registered feedback
  node is not also simultaneously a combinatorial feedback node.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It was not clear to me how to express this quirk in a way that was actually
useful at fixing the above behavior. Since this also required me to write a lot
of device-specific code, I was wondering if I could somehow avoid having to do
that.&lt;/p&gt;
&lt;h2&gt;Second Attempt: SAT/SMT&lt;/h2&gt;
&lt;p&gt;While I was encountering the above problems, I discussed them with one of my
housemates. They suggested that I should try feeding the place-and-route problem
into a SAT/SMT solver. The idea behind this was that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modern SAT/SMT solvers have quite a few advanced algorithms in them&lt;/li&gt;
&lt;li&gt;The problem size seemed "small"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this suggestion, I quickly hacked together a naive lowering of the
place-and-route problem into a SMT problem. Since I added this hack into xbpar,
it was possible to test it for both GreenPak designs and Coolrunner-II designs.&lt;/p&gt;
&lt;p&gt;Unfortunately, although I did not keep very detailed measurements, this approach
did not work very well. A major problem was that this was my first attempt at
using SMT solvers. Firstly, I selected the QF_LIA theory because I was expecting
that I might use it to eventually implement timing-driven place-and-route.
Unfortunately, this restricted me to using the SMT solvers that tended to be
relatively slower. Secondly, the particular lowering that I chose was relatively
naive and probably did not help the SMT solver to try to optimize the problem.
Overall, the results were that Coolrunner-II designs never completed in a
reasonable amount of time. GreenPak designs would complete relatively quickly if
they did indeed fit in the device (solver returns SAT) but would take forever if
they did not actually fit in the device (solver returns UNSAT).&lt;/p&gt;
&lt;h3&gt;Second-and-a-Half Attempt: Naive backtracking search&lt;/h3&gt;
&lt;p&gt;Since using a SMT solver did not appear to be working, I decided to try other
"brute-force" techniques. As part of this, I wrote a naive backtracking search
solver to assign design graph nodes to device graph nodes. I implemented only
the "min-remaining-values" optimization. This actually completed incredibly
quickly for GreenPak designs that fit. However, GreenPak designs that did not
fit were still somewhat slow, and Coolrunner-II designs still did not work at
all.&lt;/p&gt;
&lt;p&gt;Since this solver was a completely standalone program and written in Python, I
could quickly add hacks to it and observe how it behaved. This ended up giving
me the following insights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are symmetries or "shortcuts" that naive solvers cannot easily take
  advantage of (even with methods like forward checking or arc consistency). For
  example, every product term in a PLA is accessible to every OR gate, and so
  it does not "really matter" where these product terms are placed. However,
  backtracking search cannot easily discover this. The same goes for the ZIA -
  every ZIA row mux output is accessible to every product term, and it does not
  matter which specific rows are chosen.&lt;/li&gt;
&lt;li&gt;Following on from the above, the only placements that "really matter" are the
  placements of the macrocells. Everything else can be "mostly" done with greedy
  assignments. (However, note the scare quotes. Read below a less simplified
  explanation)&lt;/li&gt;
&lt;li&gt;Backtracking search cannot be used even for just placing macrocells. An
  approximation must be used here. This is because, at worst, backtracking
  search can take exponential time and explore every single possibility. If the
  algorithm happens to be working on the XC2C512 and is trying to place every
  macrocell into every possible site, this is &lt;span class="math"&gt;\(512!\approx2^{3875}\)&lt;/span&gt;
  possibilities. Even if we try to get a much lower bound by pretending that all
  macrocells in the user design are identical and can be freely swapped, we
  &lt;em&gt;still&lt;/em&gt; have too many worst-case possibilities. To count how many, we would
  like to know the "number of ways to put identical (unlabeled) objects into
  labeled bins, where all of the bins have a maximum size." Unfortunately my
  combinatorics is a bit rusty, but &lt;a href="https://twitter.com/eggleroy"&gt;@eggleroy&lt;/a&gt;
  managed to help me out with the following formula:
  &lt;div class="math"&gt;$$
  f(N, X, S) = \begin{cases}
    0 &amp;amp; \mbox{if } N &amp;lt; 0\\
    1 &amp;amp; \mbox{if } N = 0\\
    0 &amp;amp; \mbox{if } N &amp;gt; 0, X = 0\\
    \sum_{k=0}^S f(N-k, X-1, S) &amp;amp; \mbox{otherwise}
  \end{cases}
  $$&lt;/div&gt;
  where &lt;span class="math"&gt;\(N\)&lt;/span&gt; is the number of objects (macrocells), &lt;span class="math"&gt;\(X\)&lt;/span&gt; is the number of bins
  (function blocks), and &lt;span class="math"&gt;\(S\)&lt;/span&gt; is the maximum size of each bin (macrocells per
  function block). &lt;span class="math"&gt;\(N\)&lt;/span&gt; depends on the size of the user design while &lt;span class="math"&gt;\(X\)&lt;/span&gt; depends
  on the specific CPLD device chosen. &lt;span class="math"&gt;\(S\)&lt;/span&gt; is fixed for all devices in the
  family. Somewhat unintuitively (at least to me initially), this function is
  maximized (for our purposes) when &lt;span class="math"&gt;\(N=256, X=32, S=16\)&lt;/span&gt;. Computing this yields
  &lt;span class="math"&gt;\(33926222943232064651934548544483314385 \approx 2^{125}\)&lt;/span&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If anybody who is more familiar with SAT/SMT solvers would ever like to
continue to play with these ideas, there is some code
&lt;a href="https://github.com/rqou/openfpga/tree/ng-par"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Detour: xc2jed2json&lt;/h2&gt;
&lt;p&gt;While working on reverse engineering efforts, I wrote a tool that can
"decompile" Coolrunner-II bitstreams back into a .json netlist that can be
loaded into yosys. After some (not yet ready for production) steps, this can be
turned into something vaguely understandable. Andrew has given
&lt;a href="https://www.youtube.com/watch?v=hPAvDtXuDYc"&gt;a talk&lt;/a&gt; about this. More on this
in the future.&lt;/p&gt;
&lt;h2&gt;Final Attempt: Explicitly Modeling Shortcuts&lt;/h2&gt;
&lt;p&gt;At this point I was rather frustrated with working with xbpar. Given the
insights I obtained from the backtracking search implementation, I
decided to write a completely new place-and-route tool. This tool explicitly
models all of the "shortcuts" that can be taken within an individual function
block but still uses heuristics for placing macrocells. The algorithm works
mostly as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There is a subroutine that takes as input an assignment of macrocells to
   macrocell sites and outputs a placement of product terms and a map of ZIA
   usage. For each function block, this subroutine does:&lt;ol&gt;
&lt;li&gt;Finds all of the product terms referenced by macrocells in this function
   block. The algorithm knows about two categories of product terms: those
   that have some kind of constraint on their placement (because they go
   into the dedicated product terms (either the per-macrocell
   PTA/B/C or the per-function-block CTx control terms) or because they
   have an explicit LOC constraint), and those that have no such
   constraints and just go into the OR array. The "shortcut" being taken
   here is that only those product terms that have a constraint need to be
   specifically handled. Everything else can be greedily assigned.&lt;/li&gt;
&lt;li&gt;For the first type of product terms (those that have restrictions on
   their placement), store all the candidate locations for each of them.
   Possibly filter by the explicit LOC constraints.&lt;/li&gt;
&lt;li&gt;Use backtracking search to assign the locations of these product terms.
   If it does not succeed, return an error. This backtracking search is much
   smaller because macrocells will almost always use the per-macrocell
   PTA/B/C terms. The only terms that can be contended are the
   per-function-block CTx control terms. There are only 4 of them, and at
   worst the algorithm can try assigning one product term from each
   macrocell into each of the control terms. This is &lt;span class="math"&gt;\(16^4=2^{16}\)&lt;/span&gt; which can
   complete almost instantly.&lt;/li&gt;
&lt;li&gt;Take the remaining product terms (that have no restrictions on their
   placement) and assign each one to the first open site. If there are no
   open sites left, return an error.&lt;/li&gt;
&lt;li&gt;Independently of the product term placement logic, gather up all inputs
   into the product terms. The ZIA muxes need to be programmed to provide
   these as inputs into the function block. Because we are starting
   with a complete macrocell placement, we know which &lt;em&gt;specific&lt;/em&gt;
   choices we want to search for in the ZIA.&lt;/li&gt;
&lt;li&gt;Search the ZIA data table for all of the rows that can possibly give each
   output. Then use backtracking search to pick a feasible assignment. I do
   not currently have a complexity estimate for this step, but the
   ZIA was originally designed such that "almost all" combinations should be
   able to be routed. The "shortcut" here (that was also the insight
   originally used by Xilinx to design the ZIA) is that it does not matter
   which row of the ZIA is used to obtain each specific input. It only
   matters that the entire set of inputs is somehow present (because AND is
   commutative).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;The actual PAR tool first reads a yosys .json file and parses it.&lt;/li&gt;
&lt;li&gt;We create a data structure consisting of "nodes" and "nets" connecting them.
   The nodes are of a type such as "AND gate," "OR gate," "register," or "XOR
   gate," but they are not distinguished from each other (they are all a
   generic "node" data type). Net objects store their source and sink nodes, but
   nets can refer to any nodes whatsoever. Essentially, this step takes the
   yosys data model and filters/parses the individual cell types without any
   checking of connectivity between them. It turned out that this data
   structure was too difficult to work with in subsequent steps, so there is
   also a second data structure.&lt;/li&gt;
&lt;li&gt;The second data structure no longer models nets. Instead, there are multiple
   distinguished types of nodes that directly refer to each other. For example,
   a node corresponding to an OR gate will directly refer to the AND gates that
   feed it. All of the functionality related to macrocells is also packed into a
   large "macrocell" node with subcomponents for "the register part," "the IO
   part," and "the combinatorial (XOR) part." This step does most of the
   checking for proper connectivity.&lt;/li&gt;
&lt;li&gt;Macrocells are initially placed with a greedy placement algorithm. One
   observation from earlier experiments was that most simple designs can be
   placed with just greedy placement.&lt;/li&gt;
&lt;li&gt;Run the subroutine described in 1. If it succeeds, the place-and-route is
   done!&lt;/li&gt;
&lt;li&gt;If it did not succeed, something needs to be improved. Instead of using
   simulated annealing, xc2par uses "min-conflicts." When the subroutine
   described in part 1 fails, it also tries to calculate which macrocells in the
   placement contribute most to the failing. To do so, it simply brute-force
   deassigns each macrocell in the design and checks how many conflicts are
   left. The min-conflicts algorithm then chooses a "bad" macrocell to move
   weighted by the number of conflicts each macrocell causes.&lt;/li&gt;
&lt;li&gt;Once a macrocell to move is chosen, the algorithm tries every location in the
   device and tries swapping the macrocell into that location (ignoring those
   that have LOC constraints on them). The algorithm is looking for a site that
   improves the number of conflicts the most.&lt;/li&gt;
&lt;li&gt;If such a site is found, commit to the given swap. If not, randomly perform a
   swap. If an iteration limit is exceeded, give up.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After this is complete, xc2par then outputs a programming file.&lt;/p&gt;
&lt;h1&gt;Future work&lt;/h1&gt;
&lt;p&gt;Since this is an early prototype, there is always room for improvement. Hop
into ##openfpga on Freenode to discuss and report bugs.&lt;/p&gt;
&lt;p&gt;Notable areas needing improvement are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More tests&lt;/li&gt;
&lt;li&gt;Support for IO standards (currently hardcoded)&lt;/li&gt;
&lt;li&gt;Directly generating .svf files (or other means of programming chips)&lt;/li&gt;
&lt;li&gt;Ability to use the XOR-with-PTC functionality more effectively&lt;/li&gt;
&lt;li&gt;Extra passes to handle Xilinx-isms (e.g. things described in the "Xilinx CPLD Libraries Guide")&lt;/li&gt;
&lt;li&gt;Devices other than the XC2C32/A&lt;/li&gt;
&lt;/ul&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
    mathjaxscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</summary></entry><entry><title>Lab notes on decapping ICs with sulfuric acid</title><link href="https://robertou.com/lab-notes-on-decapping-ics-with-sulfuric-acid.html" rel="alternate"></link><published>2017-10-09T05:00:00-07:00</published><updated>2017-10-09T05:00:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2017-10-09:lab-notes-on-decapping-ics-with-sulfuric-acid.html</id><summary type="html">&lt;p&gt;Recently, I've been working on setting up my lab for decapping ICs for the
purpose of reverse engineering and future invasive attacks. Here are some notes
from my initial round of using sulfuric acid to perform the decap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DISCLAIMER&lt;/strong&gt;: I am not trained as a chemist. This experiment is dangerous.
Perform your own risk assessment before repeating. Repeat this experiment at
your own risk.&lt;/p&gt;
&lt;h1&gt;Materials&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Glass beakers (one 50mL, one 250mL, and one 1000mL)&lt;/li&gt;
&lt;li&gt;Watch glass&lt;/li&gt;
&lt;li&gt;Sulfuric acid drain cleaner. I used "Liquid Lightning" brand which was
  purchased at a local Walmart. This product is a clear liquid with no dyes.
  It is thick and viscous and probably at least 90% concentration. I have not
  attempted to determine the exact composition (including possible inhibitors).&lt;/li&gt;
&lt;li&gt;A cheap kitchen hotplate with no stirring capability, purchased
  &lt;a href="https://www.amazon.com/gp/product/B06XD3JZ95"&gt;from Amazon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's it! There is actually very little equipment needed if you only want to
completely dissolve the plastic package on a chip and leave only a bare die.
However, the following additional items are recommended:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sand&lt;/li&gt;
&lt;li&gt;Baking soda&lt;/li&gt;
&lt;li&gt;Type-K thermocouple&lt;/li&gt;
&lt;li&gt;Beaker tongs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And finally, the following safety equipment is required:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A cheap box fan used to exhaust fumes.&lt;/li&gt;
&lt;li&gt;A full face respirator. These two items are used in lieu of having a proper
  fume hood.&lt;/li&gt;
&lt;li&gt;Chemical-resistant gloves&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Procedure&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Prepare the 1000mL beaker by filling it with about 250mL of tap water. We
   will need this later.&lt;/li&gt;
&lt;li&gt;Place the chips to be decapped into the 50mL beaker. &lt;strong&gt;Caution&lt;/strong&gt;: If the
   chip package is physically very large, it is strongly recommended to
   mechanically trim it to just the material around the die. I currently have
   not tried this, but others have recommended either carefully grinding until
   bond wires are visible or grinding the back side of the package until the
   paddle the die is attached to is visible.&lt;/li&gt;
&lt;li&gt;Pour some sulfuric acid drain cleaner into the beaker. &lt;strong&gt;Caution&lt;/strong&gt;: Do not
   fill the beaker more than one-quarter with acid. Otherwise there will be
   a large risk of the acid bubbling over and overflowing out of the beaker.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;VERY VERY IMPORTANT&lt;/strong&gt;: Place the 50mL beaker containing the ICs and acid
   inside the 250mL beaker. Optionally place some sand into the large beaker
   to improve thermal transfer.&lt;/p&gt;
&lt;p&gt;If you do not do this, you run the risk of hot sulfuric acid overflowing
out of the beaker and landing on the surface of the hotplate. Because this
cheap hotplate is intended for the kitchen, this hot acid will instantly
react with some plastic coating on the hotplate and produce a huge cloud of
probably-toxic white smoke. This will leave a detectable odor even after
several days of blowing air through the room with fans. Depending on your
living situation, this will also escalate the problems with that one other
housemate that you are not on speaking terms with.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Place the two nested beakers slightly off the center of the hotplate. Cover
   with a watch glass.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Ensure the fume exhausting box fan is turned to max speed. Turn the hotplate
   temperature to "MAX" and turn the hotplate on. Occasionally monitor the
   temperature using a thermocouple. I was monitoring the temperature by poking
   a thermocouple into the gap between the beaker and the hotplate (which
   probably overestimates the actual temperature of the solution).&lt;/li&gt;
&lt;li&gt;Initially, not much will appear to be happening. However, as the acid nears
   its boiling point (with the hotplate temperature almost at 400 degrees C),
   the mixture will suddenly begin bubbling vigorously. If it looks to be going
   out of control, &lt;strong&gt;immediately shut of the heating&lt;/strong&gt;. Otherwise, leave the
   mixture heating for about five minutes. During this process, white fumes are
   emitted. These are likely sulfur trioxide. Avoid coming into contact with
   them. At this point, you must be absolutely sure that the respirator is
   properly sealed against your face and that the box fan is blowing the fumes
   away. If you somehow determine that this is not the case, abort the
   experiment and run away as fast as you can.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the chips look sufficiently dissolved, shut off the hotplate. Wait for
   the apparatus to cool.&lt;/p&gt;
&lt;p&gt;If you are in a hurry, you can remove the beakers from the hotplate once
touching the top of the large beaker no longer burns your fingers. This
will make cooling slightly faster because the surface of the hotplate has
quite a bit of thermal mass. Absolutely do not attempt to do this unless
the hotplate temperature is below about 100 degrees C.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once everything is sufficiently cooled, &lt;strong&gt;carefully&lt;/strong&gt; pull the small beaker
   out of the large beaker. You should definitely use beaker tongs for this.
   If like me you do not, you risk spilling the contents of the beaker onto the
   carpet because you did not get a good grip on the beaker. If this happens,
   quickly pour baking soda on it to neutralize it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Very slowly&lt;/strong&gt; pour out the spent acid into the 1000mL beaker that was
filled with water. Make sure to avoid any splashes. Because we are pouring
a concentrated acid into water, use a huge beaker with a huge amount of
water (but still significantly less than the maximum volume of the beaker).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Because the waste water and acid solution is still acidic, it is
    recommended to neutralize it with baking soda. If you do this, make sure to
    add the baking soda slowly or else the solution will spill out of the
    beaker. (This is another reason why this waste beaker is 1000mL.) However,
    if you have an antagonistic relationship with your landlords like me, you
    might skip this step and just pour the waste solution down the drain with
    the tap turned on to further dilute.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;It should now be possible to rinse out the small 50mL beaker with water
    and search for the freed silicon dies. If for some reason some dies did
    not get freed, repeat the decap procedure until they are.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Results&lt;/h1&gt;
&lt;p&gt;&lt;img alt="Decapped chips in centrifuge tubes" src="https://robertou.com/static/IMG_20171006_162406.jpg" /&gt;&lt;/p&gt;</summary></entry><entry><title>(Re-)Hacking the Sega Saturn</title><link href="https://robertou.com/re-hacking-the-sega-saturn.html" rel="alternate"></link><published>2017-07-14T18:20:00-07:00</published><updated>2017-07-14T18:20:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2017-07-14:re-hacking-the-sega-saturn.html</id><summary type="html">&lt;p&gt;A while ago, I decided to duplicate the existing hack to extract the SH-1
CD-ROM controller firmware from the Sega Saturn. I gave a short presentation
about this at the
&lt;a href="https://www.meetup.com/Mountain-View-Reverse-Engineering-Meetup/"&gt;Mountain View Reverse-Engineering Meetup&lt;/a&gt;
a few days ago. You can find the slides
&lt;a href="https://docs.google.com/presentation/d/1fxbzjlFW-TB51VgCEzNj6egoVr5BoBdR2U23-2XxOlQ/edit?usp=sharing"&gt;here&lt;/a&gt;.&lt;/p&gt;</summary></entry><entry><title>TPM2-sealed LUKS encryption keys</title><link href="https://robertou.com/tpm2-sealed-luks-encryption-keys.html" rel="alternate"></link><published>2017-03-02T03:00:00-08:00</published><updated>2017-03-02T03:00:00-08:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2017-03-02:tpm2-sealed-luks-encryption-keys.html</id><summary type="html">&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Recently, I upgraded my NAS machine and decided I wanted to set up full disk
encryption with the disk encryption key sealed inside a TPM. This setup is very
similar to Microsoft's &lt;a href="https://en.wikipedia.org/wiki/BitLocker"&gt;BitLocker&lt;/a&gt; disk
encryption. Just to make it more difficult for myself, I decided to use a TPM2
device rather than an old TPM1.2 device. There are existing
&lt;a href="https://github.com/shpedoikal/tpm-luks"&gt;projects&lt;/a&gt; that implement this
functionality for Linux using TPM1.2, but I did not find one for TPM2.&lt;/p&gt;
&lt;h1&gt;TPM? TPM2?&lt;/h1&gt;
&lt;p&gt;A TPM, or
&lt;a href="https://en.wikipedia.org/wiki/Trusted_Platform_Module"&gt;Trusted Platform Module&lt;/a&gt;,
is a small and supposedly tamper-resistant chip that can be added to a computer
in order to safely store some secret information. Among other features, a TPM
can be programmed such that it only allows access to its secret information if
the computer has booted up in the correct state (e.g. has not been booted from
an external USB thumb drive). If a disk encryption key is stored in a TPM, then
it can be configured to automatically unlock the root disk during a normal boot
but not unlock it when something in the boot configuration changes.&lt;/p&gt;
&lt;p&gt;There are two major versions of TPM devices, 1.2 and 2.0. A good description of
the changes in 2.0 can be found
&lt;a href="https://blog.hansenpartnership.com/tpm2-and-linux/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Hardware setup&lt;/h1&gt;
&lt;p&gt;I am using an ASRock E3V5 WS motherboard with ASRock's TPM-S 2.0 device which is
based on a Nuvoton NPCT650. The TPM module just needs to be plugged into the
matching connector near the bottom of the motherboard.&lt;/p&gt;
&lt;p&gt;In the UEFI configuration for the motherboard, I ensured that the TPM module was
detected, that the software/Intel ME emulated TPM was disabled, and that the TPM
was set up to use SHA-2 hashes (which actually causes both SHA-1 and SHA-2
hashes to be available).&lt;/p&gt;
&lt;h1&gt;Software setup&lt;/h1&gt;
&lt;p&gt;On the software side, I am using
&lt;a href="https://sourceforge.net/projects/ibmtpm20tss/"&gt;IBM's TPM 2.0 TSS&lt;/a&gt; along with
some custom &lt;a href="https://github.com/rqou/tpm2-luks"&gt;shell scripts&lt;/a&gt;. Unfortunately,
despite claims on James Bottomley's linked blog, Ubuntu 16.04 LTS did not seem
to have a package for the IBM tools. I built the tools from source after
applying
&lt;a href="https://github.com/rqou/tpm2-luks/blob/master/ibmtss-noso.patch"&gt;this patch&lt;/a&gt;
that disables the building of a shared object for the common code (preferring
to statically link it instead). After compiling, I followed James Bottomley's
instructions for creating a &lt;code&gt;81000001&lt;/code&gt; key handle.&lt;/p&gt;
&lt;p&gt;At this point, I created the &lt;code&gt;/opt/tpmdisk&lt;/code&gt; directory and placed the .sh files
from my repository into it. Finally, I copy the
&lt;a href="https://github.com/rqou/tpm2-luks/blob/master/zzz-tpmdisk"&gt;zzz-tpmdisk&lt;/a&gt; file
into &lt;code&gt;/usr/share/initramfs-tools/hooks/&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Disk setup&lt;/h1&gt;
&lt;p&gt;At this point, I create a 32-byte file of random data and store it at
&lt;code&gt;/keys/rootkey.bin&lt;/code&gt;. Ensure that this file is readable only to &lt;code&gt;root&lt;/code&gt;. The
purpose of this file is to allow the shell scripts to add/remove LUKS key slots
without needing the "recovery" password. Because this file is being stored on
the disk that is to be encrypted, it should not introduce an extra security
vulnerability. I use the &lt;code&gt;cryptsetup&lt;/code&gt; utility to set key slot 7 to the randomly
generated file and key slot 6 to a "recovery" password.
&lt;code&gt;/opt/tpmdisk/new-disk-key.sh&lt;/code&gt; can now be run (as &lt;code&gt;root&lt;/code&gt;) to create a third
disk encryption key that will be sealed to the TPM. The sealed key will be
stored in &lt;code&gt;/opt/tpmdisk/sealedkey_{priv,pub}.bin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Finally, &lt;code&gt;,keyscript=/opt/tpmdisk/unseal-disk-key.sh&lt;/code&gt; is appended after the
&lt;code&gt;luks&lt;/code&gt; option in &lt;code&gt;/etc/crypttab&lt;/code&gt; and the initramfs is regenerated.&lt;/p&gt;
&lt;h1&gt;Code walkthrough&lt;/h1&gt;
&lt;h2&gt;&lt;a href="https://github.com/rqou/tpm2-luks/blob/master/new-disk-key.sh"&gt;new-disk-key.sh&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This script is relatively straightforward. It creates a temporary directory and
then creates inside it a temporary file with 32 random bytes. This will be the
new sealed-to-the-TPM encryption key, but it is currently in the clear. The
script removes the existing data in the appropriate LUKS key slot and assigns
the newly-generated key to that key slot. The script also invokes
&lt;code&gt;seal-to-pcrs.sh&lt;/code&gt; to perform the actual TPM sealing operation. The script
finally cleans up and removes the cleartext copy of the new key.&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://github.com/rqou/tpm2-luks/blob/master/seal-to-pcrs.sh"&gt;seal-to-pcrs.sh&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This script performs the bulk of the work to seal secret data to a TPM. The data
is sealed such that "PCR values" in the TPM must match certain values in order
for the data to be unsealed. "PCR values" are special append-only registers that
log various steps of the boot process. The PCRs that are used are specified 
(redundantly) by the &lt;code&gt;PCRS&lt;/code&gt; and &lt;code&gt;PCR_BITS&lt;/code&gt; variables in the script. The
&lt;code&gt;PCR_BITS&lt;/code&gt; variable is a hex value that contains 1 bits in the bit position
corresponding to values in the &lt;code&gt;PCRS&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;The PCR values chosen here are modified from the
&lt;a href="https://technet.microsoft.com/en-us/library/ee706521(v=ws.10).aspx"&gt;BitLocker recommendations&lt;/a&gt;.
However, note that all PCRs 8 and above are created by the operating system
rather than by the BIOS/UEFI, and Linux does not seem to use any of them. Also,
for a TPM2 system, PCR 7 contains the UEFI Secure Boot configuration, so that is
included in my list of PCRs.&lt;/p&gt;
&lt;p&gt;This script first gathers the current value of all the PCRs into a temp file 
(one line for each value). These values are then combined into a textual policy
file. Note that TPM2 policies are rather complicated and can contain an
arbitrary amount of AND and OR terms. Here we are just creating a single AND
term with all of the PCR values. The textual policy is then converted into a
binary policy. The data is finally sealed to the TPM using that policy file.&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://github.com/rqou/tpm2-luks/blob/master/unseal-disk-key.sh"&gt;unseal-disk-key.sh&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This simple script tries to use &lt;code&gt;unseal-from-pcrs.sh&lt;/code&gt; to unseal the disk
encryption key. If this fails for any reason (such as the kernel having been
modified/upgraded), it falls back to the normal &lt;code&gt;askpass&lt;/code&gt; utility that belongs
to cryptsetup (where the "recovery" password can be entered). This is necessary
because otherwise the Ubuntu initramfs will loop forever (or a very long time)
repeatedly trying and failing to unlock the disk.&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://github.com/rqou/tpm2-luks/blob/master/unseal-from-pcrs.sh"&gt;unseal-from-pcrs.sh&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This script performs the bulk of the work to unseal data from a TPM. First, it
loads the sealed data from files on disk into the temporary storage of the TPM.
It then starts a session where policy verification can be performed. It then
instructs the TPM to try to check the policy using (hopefully) the same list of
PCRs that was used to create it. It then calls the unseal command and, if the
PCR values match, the TPM will unseal the data. The script finally cleans up.&lt;/p&gt;</summary></entry><entry><title>My First Rust Program - Dynamic DNS Updater</title><link href="https://robertou.com/my-first-rust-program-dynamic-dns-updater.html" rel="alternate"></link><published>2017-03-01T20:00:00-08:00</published><updated>2017-03-01T20:00:00-08:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2017-03-01:my-first-rust-program-dynamic-dns-updater.html</id><summary type="html">&lt;p&gt;I recently needed a dynamic DNS updater client. Being rather unsatisfied with
the existing ones, I decided to
&lt;a href="https://github.com/rqou/rqou-ddns"&gt;write my own&lt;/a&gt; in Rust. This is the first
actual Rust program I have written. If you attempt to use this on your own
machine, do be aware that it has very limited error handling.&lt;/p&gt;
&lt;p&gt;The primary advantages of my dynamic DNS updater are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does not require polling. It instead uses
  &lt;a href="https://en.wikipedia.org/wiki/Netlink"&gt;Netlink&lt;/a&gt; to get actively informed of
  IP address changes (and as such only works on Linux).&lt;/li&gt;
&lt;li&gt;Supports both IPv4 and IPv6&lt;ul&gt;
&lt;li&gt;Supports updating additional extra hosts that share the same IPv6 /64
  prefix&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, this program does require &lt;code&gt;nsupdate&lt;/code&gt; as an external runtime dependency
to actually perform the DNS updates.&lt;/p&gt;
&lt;p&gt;This tool takes three command-line arguments: the "upstream" network interface,
the "downstream" network interface, and a configuration file. The "upstream"
network interface is used to update the IPv4 and IPv6 address of the main host.
The "downstream" network interface will have its IPv6 /64 prefix extracted and
combined with suffixes specified in the config file to update the additional
extra hosts. The config file must be formatted exactly as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&amp;lt;path to nsupdate key file&amp;gt;
&amp;lt;main hostname&amp;gt;
&amp;lt;extra hostname&amp;gt; &amp;lt;extra hostname address suffix&amp;gt;
...
&amp;lt;extra hostname&amp;gt; &amp;lt;extra hostname address suffix&amp;gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;For example, the following is a working configuration file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/etc/rqou-ddns/dyn.rqou.com.key
testhostname.dyn.rqou.com
auxhostname1.dyn.rqou.com abcd:ef00:1234:5678
auxhostname2.dyn.rqou.com abcd:ef00:1234:5679
&lt;/pre&gt;&lt;/div&gt;</summary></entry><entry><title>Yet Another Minecraft⟺IRC Bridge</title><link href="https://robertou.com/yet-another-minecraftirc-bridge.html" rel="alternate"></link><published>2017-03-01T19:00:00-08:00</published><updated>2017-03-01T19:00:00-08:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2017-03-01:yet-another-minecraftirc-bridge.html</id><summary type="html">&lt;p&gt;A while ago, I decided to write yet another server wrapper for Minecraft that
would bridge chat messages between Minecraft and an IRC channel. The program
can be found &lt;a href="https://github.com/rqou/yet-another-minecraft-wrapper"&gt;here&lt;/a&gt;. This
has been done a &lt;a href="https://minecraft.curseforge.com/projects/thump"&gt;decent&lt;/a&gt;
&lt;a href="https://minecraft.curseforge.com/projects/irc-bridge"&gt;number&lt;/a&gt;
&lt;a href="https://gist.github.com/jhawthorn/1019702"&gt;of&lt;/a&gt;
&lt;a href="https://dev.bukkit.org/projects/craftirc"&gt;times&lt;/a&gt; before. The primary reason I
created yet another one was because I wanted an excuse to use Python's
somewhat-new &lt;a href="https://docs.python.org/3/library/asyncio.html"&gt;asyncio&lt;/a&gt;
functionality. Unlike many of the existing implementations, this implementation
works by wrapping stdin/stdout of the server process rather than by modifying
the jar directly. It therefore needs minimal updating as Minecraft itself
updates. It also does not implement most of the complexity needed to connect to
"real" IRC networks. Instead, it is designed to connect via localhost to a
bouncer program such as &lt;a href="http://wiki.znc.in/ZNC"&gt;ZNC&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The wrapper program is configured using a single JSON file that is passed as a
command line argument. An example config file (live on ##openfpga on Freenode
right now) is below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;cmdline&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;java&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;-jar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;forge-1.7.10-10.13.4.1614-1.7.10-universal.jar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;-Xmx2G&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;nogui&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;quot;irc_server&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="s2"&gt;&amp;quot;2600:3c01:e000:1ab::1:1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;irc_nick&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="s2"&gt;&amp;quot;fpgacraft1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;irc_port&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="mi"&gt;6667&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;irc_channel&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="s2"&gt;&amp;quot;##openfpga&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;irc_password&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;quot;enable_irc_bridge&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;use_tellraw&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;          &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;quot;backup_interval&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;num_backups&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;quot;enable_sig_verify&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;quot;users&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;rqou&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="s2"&gt;&amp;quot;NyXS8NwA7NkpgHPlFhsh2X1lTXrdO/VQwx7a8HYf81U&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;rqou_&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="s2"&gt;&amp;quot;NyXS8NwA7NkpgHPlFhsh2X1lTXrdO/VQwx7a8HYf81U&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Most of the fields should be reasonably self-explanatory. The only part of note
is how the IRC server authentication works. This authentication is done
&lt;strong&gt;unencrypted&lt;/strong&gt; using the &lt;code&gt;PASS&lt;/code&gt; command. This is intended to connect only to a
local server over an internal loopback/bridge interface rather than the global
Internet. The server is a ZNC instance configured without any of the normal
logging or message replaying functionality but configured with the "reconnect"
and "keep nickname" functionality.&lt;/p&gt;
&lt;p&gt;Lastly, commands to the bridge are supplied via special messages in the IRC
channel. They are authenticated by checking both the IRC nickname and an Ed25519
signature. Read the source code for details.&lt;/p&gt;</summary></entry><entry><title>Yet Another Let's Encrypt ACME Client</title><link href="https://robertou.com/yet-another-lets-encrypt-acme-client.html" rel="alternate"></link><published>2016-10-26T02:45:00-07:00</published><updated>2016-10-26T02:45:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2016-10-26:yet-another-lets-encrypt-acme-client.html</id><summary type="html">&lt;p&gt;A while ago, I wrote (but forgot to document) Yet Another ACME client in Python.
You can see the code for this client
&lt;a href="https://github.com/rqou/letsencrypt-client-rqou"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tools.ietf.org/html/draft-ietf-acme-acme-03"&gt;ACME&lt;/a&gt; is a protocol
invented by the &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt; project for
automatically requesting TLS certificates from a certificate authority. Other
website describe both the protocol and the Let's Encrypt project in much more
detail, but in essence they are an attempt to standardize the ad-hoc natural
language instructions that most current certificate authorities use.&lt;/p&gt;
&lt;p&gt;Let's Encrypt has an unusual feature in that its certificates have a short
90-day validity period and are designed to be automatically issued and renewed.
To help with this automation, a huge number of ACME clients have been written by
various people. However, various small issues made me unhappy with all of the
clients I looked at, so I programmed my own.&lt;/p&gt;
&lt;p&gt;This client is designed to only work with the way my particular web server is
set up. The reason I felt I needed yet another client was because existing
clients all seemed to be a little awkward to use with my particular setup
involving many many virtual hosts that do not actually serve files but only
redirects (and thus don't have a "web root" in the normal sense).&lt;/p&gt;
&lt;p&gt;On my server, this script is run from a per-user crontab belonging to a special
user &lt;code&gt;acme-cert&lt;/code&gt; dedicated for running this script. The crontab is set up to
invoke the following small shell script once a month (giving two whole months
to notice that the script isn't working):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; -eo pipefail
. /storage/certrenewal/venv/bin/activate
&lt;span class="nb"&gt;set&lt;/span&gt; -u

python /storage/certrenewal/le-rqou.py
sudo /bin/systemctl reload nginx.service
sudo /usr/sbin/postfix reload
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This script loads a Python virtualenv containing the dependencies for this
script, runs the script itself, and then reloads servers that use the
certificate. Currently, this is only nginx and Postfix. This is allowed because
the &lt;code&gt;sudoers&lt;/code&gt; file contains the following lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;acme-cert ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx.service
acme-cert ALL=(ALL) NOPASSWD: /usr/sbin/postfix reload
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you examine &lt;a href="https://github.com/rqou/letsencrypt-client-rqou/blob/master/le-rqou.py#L29"&gt;the source code for this script&lt;/a&gt;,
you will notice that it has the following constants at the top:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ACCOUNT_KEY_PATH&lt;/code&gt; - Account key JSON file. This file is created by the
  official Let's Encrypt client &lt;a href="https://github.com/certbot/certbot"&gt;certbot&lt;/a&gt;.
  Only RSA keys are supported in this client. This client &lt;strong&gt;does not&lt;/strong&gt; have the
  ability to generate this file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CSR_PATH&lt;/code&gt; - CSR file. This file should be in PEM format and should contain a
  list of domains to issue for in the CN/subjectAltName fields.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REGISTRATION_EMAIL&lt;/code&gt; - The email address to associate with the account.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ACME_CHALLENGE_DIR&lt;/code&gt; - Common directory used for writing challenge files. This
  client only supports the http-01 challenge, and it requires &lt;strong&gt;all&lt;/strong&gt; domains to
  server the &lt;code&gt;.well-known/acme-challenge&lt;/code&gt; path from this directory. More on this
  later.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CERT_PATH_TMPL&lt;/code&gt; - A template to create a filename to save newly-issued
  certificates. The argument &lt;code&gt;{}&lt;/code&gt; will be filled in with a date stamp. Old
  certificates are &lt;strong&gt;not&lt;/strong&gt; automatically deleted in order to allow for easy
  rollback. There is currently not a mechanism to automatically prune old
  certificate files.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CERT_PATH_SYMLINK&lt;/code&gt; - A path to a symlink that will be updated to point to the
  latest issued certificate.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CHAIN_PATH&lt;/code&gt; - A path to a file where the certificate chain (issuers) will be
  saved.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As mentioned above, my server is set up to expect all virtual hosts to serve the
&lt;code&gt;.well-known/acme-challenge&lt;/code&gt; path from the same directory. This is achieved by a
fragment similar to the following in the nginx configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;server {
    listen 80;
    listen [::]:80;

    server_name robertou.com  www.robertou.com
                robertou.net  www.robertou.net
                robertou.org  www.robertou.org
                robertou.mobi www.robertou.mobi
                robertou.me   www.robertou.me
                robertou.info www.robertou.info
                robertou.us   www.robertou.us;

    location = / {
        return 301 https://robertou.com;
    }

    location / {
        return 404;
    }

    location /.well-known/acme-challenge/ {
        alias /var/www/acme-challenge/;
    }
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This fragment redirects the HTTP root of all domain variants to the HTTPS root
of the "canonical" variant, refuses all paths other than the root (because
there should never be a case where these paths get used as the site is
HTTPS-only), and serves the &lt;code&gt;.well-known/acme-challenge&lt;/code&gt; path from a fixed
location in the filesystem for all domains.&lt;/p&gt;</summary></entry><entry><title>The failure of "one post a day"</title><link href="https://robertou.com/the-failure-of-one-post-a-day.html" rel="alternate"></link><published>2016-10-26T02:00:00-07:00</published><updated>2016-10-26T02:00:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2016-10-26:the-failure-of-one-post-a-day.html</id><summary type="html">&lt;p&gt;As is obvious from the lack of posts, I have more-or-less given up on the idea
of writing one blog post per day. Often, I don't accomplish enough work to write
a detailed, thorough blog post. Instead of cluttering this blog with short
snippets, I have decided to move progress updates to
&lt;a href="https://twitter.com/rqou_"&gt;Twitter&lt;/a&gt; and keep this blog for detailed write-ups.&lt;/p&gt;</summary></entry><entry><title>Catch-up post for the last week</title><link href="https://robertou.com/catch-up-post-for-the-last-week.html" rel="alternate"></link><published>2016-09-06T19:30:00-07:00</published><updated>2016-09-06T19:30:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2016-09-06:catch-up-post-for-the-last-week.html</id><summary type="html">&lt;p&gt;Unfortunately, I have failed to write a blog post on any day for the entirety
of last week. This is a catch-up post to show off what I have not done.
I spent the 4th and 5th out with friends. Before that, I:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;did a number of uninteresting sysadmin-type tasks not worthy of detailed
  write-ups (e.g. deleting old files, updating software, etc.)&lt;/li&gt;
&lt;li&gt;read the &lt;a href="https://doc.rust-lang.org/book/"&gt;Rust book&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;distracted myself with ##openfpga&lt;/li&gt;
&lt;li&gt;did a bunch of thinking about various projects in the pipeline (but didn't
  invest "actual effort" into any of them)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far for today, I have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;finally replaced the filter on my camera lens that I broke weeks ago&lt;/li&gt;
&lt;li&gt;performed miscellaneous errands&lt;/li&gt;
&lt;li&gt;did an insufficient amount of cleaning of my room&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, I have exactly fallen into the "waffle about on different
projects without making substantial progress" trap that I am trying to avoid.&lt;/p&gt;</summary></entry><entry><title>Jenkins proxied behind nginx with TLS client certificates</title><link href="https://robertou.com/jenkins-proxied-behind-nginx-with-tls-client-certificates.html" rel="alternate"></link><published>2016-08-30T23:30:00-07:00</published><updated>2016-08-30T23:30:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2016-08-30:jenkins-proxied-behind-nginx-with-tls-client-certificates.html</id><summary type="html">&lt;p&gt;I just finished reinstalling Jenkins on my server. The reason for
reinstalling Jenkins is to move it inside an LXC container. Hopefully, this
will result in a reduced attack surface as well as making it easier to keep
track of the global state needed to build my projects.&lt;/p&gt;
&lt;p&gt;I use TLS client certificates on all the services I host that will only be used
by me or a very-limited group of technically-savvy people.
I prefer client certificates because it removes the need to use passwords,
session cookies, or other methods to track authentication state.
Once the initial setup is done, client certificates are almost fully
transparent to the user. Unfortunately, making client certificate
authentication work with Jenkins behind a proxy is tricky and requires
some hacks.&lt;/p&gt;
&lt;p&gt;Jenkins has &lt;a href="https://github.com/jenkinsci/certificate-authentication-plugin"&gt;a plugin
&lt;/a&gt;
that authenticates users via client certificates. However, because nginx
terminates TLS and sends plaintext requests to Jenkins, Jenkins obviously
can't know about client certificates directly and must be passed this
information some other way. From its source code, this plugin appears to get
information about client certificates by querying an attribute
&lt;code&gt;javax.servlet.request.X509Certificate&lt;/code&gt; that is apparently somehow set by
Jenkins' surrounding servlet container. Unfortunately, some extended Googling
seemed to suggest that the standard way to forward this information is to use
a protocol called &lt;a href="https://en.wikipedia.org/wiki/Apache_JServ_Protocol"&gt;AJP&lt;/a&gt;,
and recent versions of Jetty, the servlet wrapper for Jenkins, &lt;a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=425244"&gt;no longer
supports&lt;/a&gt; AJP.
Supposedly, AJP support can be reobtained by making Jenkins use Apache Tomcat
instead. However, because I am not really familiar with how servlets are
supposed to work and because there are various claims on the Internet that
AJP is slower than HTTP, I decided to look for an alternative solution
instead.&lt;/p&gt;
&lt;p&gt;The alternative that I decided to use instead is the &lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/Reverse+Proxy+Auth+Plugin"&gt;Reverse Proxy Auth Plugin
&lt;/a&gt;.
This plugin is intended to be used with a reverse proxy that performs some
form of HTTP authentication and then forwards a username to Jenkins via an
extra &lt;code&gt;X-Forwarded-User&lt;/code&gt; header. However, this plugin doesn't actually care
how the reverse proxy determines a username as long as the proxy does send
one, so we can use this plugin as long as we can get nginx to extract a
username from the client certificate.&lt;/p&gt;
&lt;p&gt;The certificates I use for my services have a subject like
&lt;code&gt;/OU=&amp;lt;computer&amp;gt;/CN=&amp;lt;username&amp;gt;/emailAddress=&amp;lt;email&amp;gt;&lt;/code&gt;. We therefore need
to extract the &lt;code&gt;CN&lt;/code&gt; component from the subject. Fortunately, we can use
this snippet from &lt;a href="https://github.com/aol/moloch/wiki/Client-Side-Certificate-Auth-in-Moloch"&gt;here&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;map $ssl_client_s_dn $ssl_client_s_dn_cn {
    default &amp;quot;&amp;quot;;
    ~/CN=(?&amp;lt;CN&amp;gt;[^/]+) $CN;
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This snippet needs to go &lt;strong&gt;outside&lt;/strong&gt; a &lt;code&gt;server&lt;/code&gt; block. Finally, to actually
proxy to Jenkins, add this inside a &lt;code&gt;server&lt;/code&gt; block:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssl_client_certificate &amp;lt;ca for client certs&amp;gt;;
ssl_verify_client optional;

# Jenkins proxy
location /jenkins {
    if ($ssl_client_verify = FAILED) {
        # BAD cert (rather than no cert at all)
        return 403;
    }
    proxy_set_header X-Forwarded-User $ssl_client_s_dn_cn;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_redirect http:// https://;
    proxy_pass http://&amp;lt;where Jenkins is&amp;gt;:8080;
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;(The &lt;code&gt;if&lt;/code&gt; block doesn't seem to be strictly necessary as nginx seems to
generate a 400 error for client certificates that don't verify, but I am
keeping it just in case.)&lt;/p&gt;
&lt;p&gt;Finally, set up Jenkins, change its location under "Configure System",
ensure that it doesn't complain about the proxy setup, and change the
"Security Realm" to "HTTP Header by reverse proxy" under "Configure Global
Security". If it doesn't seem to be working, you can debug by navigating to
the &lt;code&gt;/jenkins/whoAmI/&lt;/code&gt; URL.&lt;/p&gt;</summary></entry><entry><title>Configuring uWSGI Emperor, nginx, and git-based deploys</title><link href="https://robertou.com/configuring-uwsgi-emperor-nginx-and-git-based-deploys.html" rel="alternate"></link><published>2016-08-30T01:00:00-07:00</published><updated>2016-08-30T01:00:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2016-08-30:configuring-uwsgi-emperor-nginx-and-git-based-deploys.html</id><summary type="html">&lt;p&gt;After having uWSGI installed on my server but not configured for years,
I have finally decided to go set it up properly. This setup was done on
Ubuntu 16.04 and is mostly based off of these
&lt;a href="https://chriswarrick.com/blog/2016/02/10/deploying-python-web-apps-with-nginx-and-uwsgi-emperor/"&gt;two&lt;/a&gt;
&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-serve-django-applications-with-uwsgi-and-nginx-on-ubuntu-16-04"&gt;articles&lt;/a&gt;
along with a large collection of various StackOverflow and ServerFault
questions that I unfortunately didn't keep track of. These notes assume that
uWSGI will be used to deploy apps written in Python. You will need to adjust
some of the steps if you are deploying other languages.&lt;/p&gt;
&lt;p&gt;In general, this setup was as painless as I would reasonably expect.
Most of the difficultly resolved around my somewhat-unusual requirements
&amp;mdash; namely that I wanted to have more than one WSGI application per
virtual host, and I wanted the ability to add/remove WSGI applications
via the &lt;code&gt;git push&lt;/code&gt; mechanism &lt;a href="https://robertou.com/deploying-my-website-using-git.html"&gt;I documented earlier&lt;/a&gt;.
I can accept that it does seem like I am forcing uWSGI to conform to my own
deployment method rather than going for a more "normal" site organization
strategy. However, I believe that the setup I have here is more suitable for
the organization of my websites (a huge dumping ground of small components
rather than one large app), and the fact that it mostly Just Worked™ is
a testament to uWSGI's flexibility.&lt;/p&gt;
&lt;p&gt;The first step in the setup process is to install uWSGI. I opted to install
the version from the Ubuntu repository rather than to install the latest
stable via &lt;code&gt;pip&lt;/code&gt; or from source. I wanted the convenience of being able to
add support for a new language later by simply using &lt;code&gt;apt-get install&lt;/code&gt;, and
I didn't feel I was likely to &lt;em&gt;require&lt;/em&gt; the latest and greatest features.
To install from the repositories, install the &lt;code&gt;uwsgi&lt;/code&gt;, &lt;code&gt;uwsgi-emperor&lt;/code&gt;,
and &lt;code&gt;uwsgi-plugin-python&lt;/code&gt; packages. If you do not already have them,
also install &lt;code&gt;python-pip&lt;/code&gt; and &lt;code&gt;python-virtualenv&lt;/code&gt;.
(This installs the Python 2 version of the packages. Replace instances of
&lt;code&gt;python&lt;/code&gt; with &lt;code&gt;python3&lt;/code&gt; to get the corresponding Python 3 versions.)&lt;/p&gt;
&lt;p&gt;Next, I chose to replace the Ubuntu-provided init scripts for uWSGI with
a systemd unit file. There was no real reason for this other than because
the two tutorials these notes are based on decided to use systemd,
and because systemd is supposed to be The Future™.&lt;/p&gt;
&lt;p&gt;(I will now insert a mini-rant here and say that Ubuntu's "halfway adopt
systemd but keep around a bunch of older init stuff" is probably the worst
of the possible choices of init systems. Some of the issues this has caused
me include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;losing the getty on ttyS0/ttyHVC0 (it needs to be explicitly reenabled
  in systemctl)&lt;/li&gt;
&lt;li&gt;a number of warnings mentioning Upstart jobs and runlevels whenever a large
  dist-upgrade is performed (I have just been ignoring these for now because
  nothing detrimental seems to happen as a result.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these issues surfaced after an upgrade from Ubuntu 14.04 to 16.04, and
IMHO a better transition would have been appreciated.)&lt;/p&gt;
&lt;p&gt;Anyways, place the following contents into
&lt;code&gt;/etc/systemd/system/uwsgi-emperor-systemd.service&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uWSGI Emperor&lt;/span&gt;
&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;syslog.target&lt;/span&gt;

&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="na"&gt;ExecStartPre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/bash -c &amp;#39;mkdir -p /run/uwsgi; chown www-data:www-data /run/uwsgi&amp;#39;&lt;/span&gt;
&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/uwsgi --ini /etc/uwsgi-emperor/emperor.ini&lt;/span&gt;
&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="na"&gt;KillSignal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;SIGQUIT&lt;/span&gt;
&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;notify&lt;/span&gt;
&lt;span class="na"&gt;StandardError&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;syslog&lt;/span&gt;
&lt;span class="na"&gt;NotifyAccess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;

&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now run the following commands to disable the Ubuntu init scripts and activate
this new one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo systemctl stop uwsgi.service
sudo systemctl stop uwsgi-emperor.service
sudo systemctl disable uwsgi.service
sudo systemctl disable uwsgi-emperor.service
sudo systemctl enable uwsgi-emperor-systemd.service
sudo systemctl start uwsgi-emperor-systemd.service
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Note that unlike the Ubuntu-provided init scripts, this unit file will cause
uWSGI to log to &lt;code&gt;journalctl&lt;/code&gt; rather than its own log file.&lt;/p&gt;
&lt;p&gt;The next and final "global system" configuration step is to create symlinks
in &lt;code&gt;/etc/uwsgi-emperor/vassals/&lt;/code&gt; that point to
&lt;code&gt;/var/www/&amp;lt;site&amp;gt;/conf/uwsgi-master.ini&lt;/code&gt; (or some other filename of your
choice).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ls -l /etc/uwsgi-emperor/vassals/
lrwxrwxrwx &lt;span class="m"&gt;1&lt;/span&gt; root root  &lt;span class="m"&gt;43&lt;/span&gt; Aug &lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="m"&gt;05&lt;/span&gt;:25 robertou.com.ini -&amp;gt; /var/www/robertou.com/conf/uwsgi-master.ini
...
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In order to potentially contain multiple apps in one virtual host without
needing to create additional symlinks, the file &lt;code&gt;conf/uwsgi-master.ini&lt;/code&gt;
in each site's git repository contains only the following two lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[uwsgi]&lt;/span&gt;
&lt;span class="na"&gt;emperor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/www/&amp;lt;site&amp;gt;/conf/apps&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;(Note that the %d magic variable will not work here because uWSGI doesn't
follow the symlink before computing %d .)&lt;/p&gt;
&lt;p&gt;Although I didn't find any explicit documentation that this would do what
I expected, uWSGI is indeed smart and understands the intention of creating
a sub-Emperor as can be observed in the logs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Aug 30 05:04:21 &amp;lt;snip&amp;gt; uwsgi[4882]: *** starting uWSGI sub-Emperor ***
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;An individual configuration file for each app can now be placed in the
&lt;code&gt;conf/apps/&lt;/code&gt; directory in each virtual host's repository. An example of one
is below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[uwsgi]&lt;/span&gt;
&lt;span class="na"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/run/uwsgi/rqou.com-testapp1.sock&lt;/span&gt;
&lt;span class="na"&gt;chmod-socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;660&lt;/span&gt;
&lt;span class="c1"&gt;# XXX do we need the build thing?&lt;/span&gt;
&lt;span class="na"&gt;chdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/www/rqou.com/build/testapp1&lt;/span&gt;
&lt;span class="na"&gt;master&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;virtualenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/www/rqou.com/venv&lt;/span&gt;
&lt;span class="na"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;testapp1:app&lt;/span&gt;
&lt;span class="na"&gt;processes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Finally, insert an appropriate proxying command into conf/nginx.conf:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class="nt"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;testapp1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;rewrite&lt;/span&gt; &lt;span class="err"&gt;^&lt;/span&gt; &lt;span class="err"&gt;/testapp1/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;location&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;testapp1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;include&lt;/span&gt; &lt;span class="err"&gt;/etc/nginx/uwsgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="err"&gt;uwsgi_pass&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;uwsgi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rqou&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com-testapp1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When these configuration changes are pushed to the &lt;code&gt;webdeploy&lt;/code&gt; user,
uWSGI seems to automatically detect the change and gracefully reloads.
Hopefully the app now works!&lt;/p&gt;</summary></entry><entry><title>Deploying my website using git</title><link href="https://robertou.com/deploying-my-website-using-git.html" rel="alternate"></link><published>2016-08-29T14:00:00-07:00</published><updated>2016-08-29T14:00:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2016-08-29:deploying-my-website-using-git.html</id><summary type="html">&lt;p&gt;All of the websites on my server are currently deployed using &lt;code&gt;git push&lt;/code&gt;.
This ensures that all of the changes are easily traceable and easily reverted
if necessary. This deploy mechanism is mostly powered by Jeff Lindsay
(progrium)'s &lt;a href="https://github.com/progrium/gitreceive"&gt;gitreceive&lt;/a&gt; script.
Here are some notes on how to create a similar setup. These notes assume you
already have a Linux server that is running nginx with virtual hosts (although
the steps can be adapted to other setups as well).&lt;/p&gt;
&lt;p&gt;The first step in this setup process is to set up a user to receive git commits
via &lt;code&gt;git push&lt;/code&gt; and the gitreceive script. Instead of duplicating the
instructions, I will simply refer to &lt;a href="https://github.com/progrium/gitreceive/blob/master/README.md"&gt;the README for the gitreceive script&lt;/a&gt; instead.
The important steps to perform are the "Set up a git user on the server" and
"Create a user by uploading a public key from your laptop" steps. Although
you might want to test the setup using the default &lt;code&gt;receiver&lt;/code&gt; script in the
README, you will replace the &lt;code&gt;receiver&lt;/code&gt; script with a completely different one 
in the next step. I will note that the username I use for gitreceive is not
&lt;code&gt;git&lt;/code&gt; but &lt;code&gt;webdeploy&lt;/code&gt;, and you must ensure that the &lt;code&gt;GITUSER&lt;/code&gt;
environment variable is set appropriately if you are also using a username
other than &lt;code&gt;git&lt;/code&gt; for your server. You will obviously also have to substitute
&lt;code&gt;webdeploy&lt;/code&gt; with the appropriate username in the steps below if you are not
using the same username in your setup.&lt;/p&gt;
&lt;p&gt;The next step is to copy an appropriate &lt;code&gt;receiver&lt;/code&gt; script into the home
directory of the &lt;code&gt;webdeploy&lt;/code&gt; user. The &lt;code&gt;receiver&lt;/code&gt; script I use can be found
&lt;a href="https://github.com/rqou/miscellany/blob/master/website-receiver"&gt;here on my GitHub&lt;/a&gt;. However,
this script makes many assumptions about how files are laid out in each
site's git repository and on the server.&lt;/p&gt;
&lt;p&gt;The git repositories for each site on my server are laid out somewhat like
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&amp;lt;root&amp;gt;
├── build.sh
├── build/
├── conf/
│   └── nginx.conf
├── content/
│   └── ...
└── ...
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Notably, the &lt;code&gt;nginx.conf&lt;/code&gt; fragment for each virtual host is located within the
source itself. This helps ensure that any possible reverse proxying,
settings for FastCGI/uWSGI/etc., and other similar configuration are all
versioned along with the actual source code. There is also a &lt;code&gt;build.sh&lt;/code&gt; script
that does any necessary processing of the source code (e.g. minifying). The
&lt;code&gt;build/&lt;/code&gt; directory becomes the actual webroot for each site. (Unfortunately
this does mean that extra copying/hardlinking is required for static files
that do not need to undergo any processing during the build process.) The rest
of the repository (e.g. &lt;code&gt;content/&lt;/code&gt;) is &lt;em&gt;not&lt;/em&gt; served despite eventually also
ending up under &lt;code&gt;/var/www&lt;/code&gt; (this is controlled by a &lt;code&gt;root&lt;/code&gt; directive in each
site's &lt;code&gt;nginx.conf&lt;/code&gt; fragment).&lt;/p&gt;
&lt;p&gt;Because changing the &lt;code&gt;nginx.conf&lt;/code&gt; file requires a reload of nginx which
normally requires root, the next step is to modify the &lt;code&gt;sudoers&lt;/code&gt; file to allow
&lt;code&gt;webdeploy&lt;/code&gt; to do this. To do so, run &lt;code&gt;sudo visudo&lt;/code&gt; and add a line like the
following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;webdeploy ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx.service
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Unfortunately, the &lt;code&gt;sudoers&lt;/code&gt; file is &lt;a href="https://xkcd.com/1343/"&gt;rather confusing&lt;/a&gt;. The line here is allowing the &lt;code&gt;webdeploy&lt;/code&gt; user to run
only the &lt;code&gt;/bin/systemctl&lt;/code&gt; binary and only with the arguments &lt;code&gt;reload
nginx.service&lt;/code&gt;. The &lt;code&gt;webdeploy&lt;/code&gt; user can run this command without a password
and can run it as any user (i.e. root). However, &lt;code&gt;webdeploy&lt;/code&gt; won't be allowed
to perform any other actions such as &lt;code&gt;disable&lt;/code&gt; or to control any other service.&lt;/p&gt;
&lt;p&gt;In order to allow the &lt;code&gt;receiver&lt;/code&gt; script to actually create new files and
directories under &lt;code&gt;/var/www&lt;/code&gt;, we need to change some permissions. One way that
this can be done is to give &lt;code&gt;webdeploy&lt;/code&gt; ownership of all of &lt;code&gt;/var/www&lt;/code&gt;.
However, I opted to use extended ACLs to grant permissions instead. Although
it makes almost no difference in this case and traditional groups can be used
rather than ACLs in many cases, extended ACLs in general help to reduce group
proliferation when granting fine-grained access to different parts of the
filesystem to different users. If you choose to use ACLs, you can grant the
&lt;code&gt;webdeploy&lt;/code&gt; user full permissions on &lt;code&gt;/var/www&lt;/code&gt; using&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo setfacl -m &amp;quot;u:webdeploy:rwx&amp;quot; /var/www
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;My &lt;code&gt;receiver&lt;/code&gt; script uses a number of symlinks in order to make site updates
less obviously non-atomic. The next step is to set these up. For each domain,
there are two symlinks &lt;code&gt;&amp;lt;site&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;site&amp;gt;-prev&lt;/code&gt; under &lt;code&gt;/var/www&lt;/code&gt;.
For example, part of &lt;code&gt;/var/www&lt;/code&gt; might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ls -l /var/www
lrwxrwxrwx  &lt;span class="m"&gt;1&lt;/span&gt; webdeploy webdeploy   &lt;span class="m"&gt;62&lt;/span&gt; Aug &lt;span class="m"&gt;29&lt;/span&gt; &lt;span class="m"&gt;09&lt;/span&gt;:33 robertou.com -&amp;gt; /var/www/robertou.com-3a8d6216872d83f2d68c82fcb09b6904796bd70a
drwxrwxr-x  &lt;span class="m"&gt;1&lt;/span&gt; webdeploy webdeploy  &lt;span class="m"&gt;270&lt;/span&gt; Aug &lt;span class="m"&gt;29&lt;/span&gt; &lt;span class="m"&gt;09&lt;/span&gt;:33 robertou.com-3a8d6216872d83f2d68c82fcb09b6904796bd70a
drwxrwxr-x  &lt;span class="m"&gt;1&lt;/span&gt; webdeploy webdeploy  &lt;span class="m"&gt;276&lt;/span&gt; Aug &lt;span class="m"&gt;29&lt;/span&gt; &lt;span class="m"&gt;09&lt;/span&gt;:22 robertou.com-6c19354ac248201a032fab3524602550051f3310
lrwxrwxrwx  &lt;span class="m"&gt;1&lt;/span&gt; webdeploy webdeploy   &lt;span class="m"&gt;62&lt;/span&gt; Aug &lt;span class="m"&gt;29&lt;/span&gt; &lt;span class="m"&gt;09&lt;/span&gt;:33 robertou.com-prev -&amp;gt; /var/www/robertou.com-6c19354ac248201a032fab3524602550051f3310
...
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Initially, the symlinks can both be set to point to dummy targets. The purpose
of the &lt;code&gt;-prev&lt;/code&gt; symlink is to facilitate emergency reverts without needing to
rerun the build process for the previous version of the site.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;nginx.conf&lt;/code&gt; fragments are included in the main &lt;code&gt;/etc/nginx.conf&lt;/code&gt;
by (assuming a distribution default configuration that uses &lt;code&gt;sites-enabled&lt;/code&gt; and
&lt;code&gt;sites-available&lt;/code&gt;) symlinking &lt;code&gt;/var/www/&amp;lt;site&amp;gt;/conf/nginx.conf&lt;/code&gt; into
&lt;code&gt;/etc/nginx/sites-available&lt;/code&gt; and then symlinking that symlink into
&lt;code&gt;/etc/nginx/sites-enabled&lt;/code&gt; like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ls -l /etc/nginx/sites-enabled/robertou.com 
lrwxrwxrwx &lt;span class="m"&gt;1&lt;/span&gt; root root &lt;span class="m"&gt;31&lt;/span&gt; Mar  &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;:04 /etc/nginx/sites-enabled/robertou.com -&amp;gt; ../sites-available/robertou.com
$ ls -l /etc/nginx/sites-available/robertou.com 
lrwxrwxrwx &lt;span class="m"&gt;1&lt;/span&gt; root root &lt;span class="m"&gt;37&lt;/span&gt; Mar  &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;:03 /etc/nginx/sites-available/robertou.com -&amp;gt; /var/www/robertou.com/conf/nginx.conf
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once this is all set up, doing a push to the &lt;code&gt;webdeploy&lt;/code&gt; user should
automatically:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Receive and unpack the new version of the site.&lt;/li&gt;
&lt;li&gt;Run its &lt;code&gt;build.sh&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;Move the "current site" symlink to point to the new version.&lt;/li&gt;
&lt;li&gt;Move the "old site" symlink to point to the old version.&lt;/li&gt;
&lt;li&gt;Delete the "old old" version.&lt;/li&gt;
&lt;li&gt;Reload nginx.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My setup here has a number of known bugs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A build failure will leave stray files in /var/www. This problem was ignored
  for my use case because I assumed that I would always run the build locally
  before pushing and because I can simply use sudo to remove the stray files
  if a build does unexpectedly fail.&lt;/li&gt;
&lt;li&gt;Submodules are not handled. The gitreceive repository has
  &lt;a href="https://github.com/progrium/gitreceive/wiki/TipsAndTricks#handling-submodules"&gt;a hint on how to fix this&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;It is not resistant against malicious users. Because the &lt;code&gt;webdeploy&lt;/code&gt; user
  runs &lt;code&gt;build.sh&lt;/code&gt; under its privileges, a malicious user can grief &lt;strong&gt;any&lt;/strong&gt;
  site on the same server. This script as such is therefore not suitable for
  a shared hosting environment. gitreceive itself also apparently has a
  &lt;a href="https://github.com/progrium/gitreceive/issues/32"&gt;bug&lt;/a&gt; that allows creating
  repositories anywhere on the system (subject to normal write permission
  checks). Only give trusted users access to the &lt;code&gt;webdeploy&lt;/code&gt; user.&lt;/li&gt;
&lt;/ul&gt;</summary></entry><entry><title>Current purpose of this blog thing</title><link href="https://robertou.com/current-purpose-of-this-blog-thing.html" rel="alternate"></link><published>2016-08-29T02:00:00-07:00</published><updated>2016-08-29T02:00:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2016-08-29:current-purpose-of-this-blog-thing.html</id><summary type="html">&lt;p&gt;I currently feel that I am not progressing on my personal projects to my
satisfaction. This blog is my attempt to force myself to rectify that by making
my (lack of) progress very explicitly visible. The current plan is to write a
daily post documenting the work I accomplish each day. The purpose of this is
not solely to publicly shame myself. One important "other" goal of this
exercise is to get myself to actually publicly document useful things I work
on. I also hope that this daily reflection exercise can help reduce my tendency
to waffle about and can help me clarify the direction of my projects.&lt;/p&gt;
&lt;p&gt;I will conclude the post for today by noting that my website has finally been
updated. There isn't too much notable about the site itself (it's a simple
Pelican static site), but tomorrow I will probably document the site deploy
system that has been set up.&lt;/p&gt;</summary></entry><entry><title>Hello world</title><link href="https://robertou.com/hello-world.html" rel="alternate"></link><published>2016-08-29T00:00:00-07:00</published><updated>2016-08-29T00:00:00-07:00</updated><author><name>Robert Ou</name></author><id>tag:robertou.com,2016-08-29:hello-world.html</id><summary type="html">&lt;p&gt;Hello world. Apparently Pelican is a thing that makes websites!&lt;/p&gt;</summary></entry></feed>