<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Andreas Rammhold</title><link>https://andreas.rammhold.de/</link><description>Recent content on Andreas Rammhold</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sat, 27 Nov 2021 19:00:00 +0000</lastBuildDate><atom:link href="https://andreas.rammhold.de/index.xml" rel="self" type="application/rss+xml"/><item><title>Why I started using Hydra as CI</title><link>https://andreas.rammhold.de/posts/why-hydra-is-the-ci/</link><pubDate>Sat, 27 Nov 2021 19:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/why-hydra-is-the-ci/</guid><description>&lt;p>Hydra is the Perl + C++ stack that evaluates and runs builds described by Nix
expressions. It provides a usually good-enough (but not great) web interface
and more importantly uploads (intermediary) build results into a binary cache
(local storage, S3, &amp;hellip;). The instance for NixOS, Nix and nixpkgs is running at
&lt;a href="https://hydra.nixos.org">https://hydra.nixos.org&lt;/a>.&lt;/p>
&lt;p>I&amp;rsquo;ve always been on the fence about &amp;ldquo;Hydra is the CI&amp;rdquo; when it comes to
development on PR based workflows. Hydra doesn&amp;rsquo;t really have a very strong
story around integration into those. There are some integrations but usually it
feels bulky and isn&amp;rsquo;t really a first-class citizen.&lt;/p>
&lt;p>Yet I&amp;rsquo;ve come to use it for my small scale projects that aren&amp;rsquo;t (public) on any
of the forges. Why you might ask? If the experience isn&amp;rsquo;t great, why would you
use it?&lt;/p>
&lt;p>Well, in my case I am working on pretty large builds that have many
intermediary steps. Verifying that everything (still) works usually involves
bootstrapping GCC, glibc and all their dependencies.&lt;/p>
&lt;p>I could (and from time to time still do) that locally but that doesn&amp;rsquo;t give me a nice
way to manage build logs and intermediary build results. Whenever I push
something to my hydra machine I can go back in time and look at build logs,
dependency graphs and also retrieve a cached version of the build.&lt;/p>
&lt;p>All of that could be done locally but it would require more disk space, custom
tooling to keep old derivations and their logs around etc. There are already
enough projects on my list that I&amp;rsquo;ll never finish..&lt;/p>
&lt;p>Another neat side-effect of the workflow is that I &lt;em>must&lt;/em> commit my changes and
thus gain checkpoints on my journey. More often than not I would otherwise just
hack on an expression &amp;ldquo;until it works&amp;rdquo; without having a good way of going back
in time. These intermediary commits then serve me as a log of work that went
into the change I am working on. I can squash it (into a few chunks) that make
sense. I can also write down my learnings from the intermediary steps.&lt;/p>
&lt;p>This isn&amp;rsquo;t so much a statement how great Hydra is but perhaps a statement what
is lacking in terms of Nix developer tooling. Building expressions works,
reproducing something that another developer ran into also works (most of the
time) but really diving into build graphs and logs of builds (that didn&amp;rsquo;t
happen on my machine) isn&amp;rsquo;t a great experience. We do have graphviz based
tooling to inspect dependency graphs but that isn&amp;rsquo;t usually fun to deal with.&lt;/p>
&lt;p>I think I can now finally understand the &amp;ldquo;Hydra is the CI&amp;rdquo; comment that was
made by Eelco on IRC years ago. The system isn&amp;rsquo;t that bad and it is the best we
have for some workflows.&lt;/p></description></item><item><title>Distributed Forges</title><link>https://andreas.rammhold.de/posts/distributed-forges/</link><pubDate>Sun, 07 Mar 2021 13:30:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/distributed-forges/</guid><description>&lt;p>People that know me are very likely to know my stance on GitHub.com, GitLab.com
or any other centralized software forges.&lt;/p>
&lt;p>I have the strong believe that the future of developing open and free software
&lt;em>must&lt;/em> be done on open and free platforms that allow users and contributors to
interact with the forge without requiring centralized data storage or identity
management.&lt;/p>
&lt;p>Today I came across a blog post that covers this well: &lt;a href="https://staticadventures.netlib.re/blog/decentralized-forge/">Decentralized forge: distributing the means of digital production&lt;/a> (&lt;a href="https://web.archive.org/web/20210307131107/https://staticadventures.netlib.re/blog/decentralized-forge/">archive.org&lt;/a>)&lt;/p>
&lt;p>It also links to a wide variety of other (tangential) topics that I can highly
recommend to anyone interested in F/OSS and privacy.&lt;/p></description></item><item><title>Blog deployment via git-receive hooks</title><link>https://andreas.rammhold.de/posts/git-receive-blog-hook-deployment/</link><pubDate>Thu, 08 Oct 2020 15:00:00 +0200</pubDate><guid>https://andreas.rammhold.de/posts/git-receive-blog-hook-deployment/</guid><description>&lt;p>While quickly going through the options for hosting this blog I decided to just
do it myself.. Serving some static content (for a small audience) isn&amp;rsquo;t that
hard and I&amp;rsquo;m running a few (mostly idle) machines anyway.&lt;/p>
&lt;p>My requirements were just to have IPv6, some way to tune the webserver config
and some kind of &amp;ldquo;CI&amp;rdquo; that converts the website from markdown to HTML
(preferably with Nix).&lt;/p>
&lt;p>Actually pushing the content to a server is trivial if you already have SSH
access. So I could use either rsync or SSH to do it. Since the blog is already
in a git repo I wanted to treat the target server as a git remote. Git has a
bunch of &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">built-in hooks&lt;/a>
that can be used for many things. In this case I was interested in the
&lt;code>post-receive&lt;/code> hook.&lt;/p>
&lt;p>Pushing the content (in it&amp;rsquo;s raw format) to a server is already trivial if you
have SSH access. I would just commit and push the latest version to another
remote.&lt;/p>
&lt;p>For building the static assets I could be using git &lt;code>post-receive&lt;/code> hooks that
just call &lt;code>nix-build … -o $documentRoot&lt;/code>.&lt;/p>
&lt;p>And that is what I did.&lt;/p>
&lt;p>All that was required to make this happen was adding a new file to the NixOS
configuration of the server this is running on:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">{ config&lt;span style="color:#f92672">,&lt;/span> pkgs&lt;span style="color:#f92672">,&lt;/span> &lt;span style="color:#f92672">...&lt;/span> }:
&lt;span style="color:#66d9ef">let&lt;/span>
home &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;/var/lib/blog&amp;#34;&lt;/span>;
env &lt;span style="color:#f92672">=&lt;/span> pkgs&lt;span style="color:#f92672">.&lt;/span>buildEnv {
name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;post-receive-env&amp;#34;&lt;/span>;
paths &lt;span style="color:#f92672">=&lt;/span> [
pkgs&lt;span style="color:#f92672">.&lt;/span>git
pkgs&lt;span style="color:#f92672">.&lt;/span>nix
pkgs&lt;span style="color:#f92672">.&lt;/span>coreutils
pkgs&lt;span style="color:#f92672">.&lt;/span>gnutar
pkgs&lt;span style="color:#f92672">.&lt;/span>xz
];
};
post-receive &lt;span style="color:#f92672">=&lt;/span> pkgs&lt;span style="color:#f92672">.&lt;/span>writeShellScript &lt;span style="color:#e6db74">&amp;#34;post-receive&amp;#34;&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> export PATH=&lt;/span>&lt;span style="color:#e6db74">${&lt;/span>env&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/bin
&lt;/span>&lt;span style="color:#e6db74"> set -ex
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> GIT_DIR=$(git rev-parse --git-dir 2&amp;gt;/dev/null)
&lt;/span>&lt;span style="color:#e6db74"> if [ -z &amp;#34;$GIT_DIR&amp;#34; ]; then
&lt;/span>&lt;span style="color:#e6db74"> echo &amp;gt;&amp;amp;2 &amp;#34;fatal: post-receive: GIT_DIR not set&amp;#34;
&lt;/span>&lt;span style="color:#e6db74"> exit 1
&lt;/span>&lt;span style="color:#e6db74"> fi
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> TMPDIR=$(mktemp -d)
&lt;/span>&lt;span style="color:#e6db74"> function cleanup() {
&lt;/span>&lt;span style="color:#e6db74"> rm -rf &amp;#34;$TMPDIR&amp;#34;
&lt;/span>&lt;span style="color:#e6db74"> }
&lt;/span>&lt;span style="color:#e6db74"> trap cleanup EXIT
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> git clone &amp;#34;$GIT_DIR&amp;#34; &amp;#34;$TMPDIR&amp;#34;
&lt;/span>&lt;span style="color:#e6db74"> unset GIT_DIR
&lt;/span>&lt;span style="color:#e6db74"> cd &amp;#34;$TMPDIR&amp;#34;
&lt;/span>&lt;span style="color:#e6db74"> exec nix-build &amp;#34;$TMPDIR&amp;#34; -A build -o &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>home&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/current
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
&lt;span style="color:#66d9ef">in&lt;/span>
{
config &lt;span style="color:#f92672">=&lt;/span> {
services&lt;span style="color:#f92672">.&lt;/span>nginx&lt;span style="color:#f92672">.&lt;/span>virtualHosts&lt;span style="color:#f92672">.&lt;/span>&lt;span style="color:#e6db74">&amp;#34;andreas.rammhold.de&amp;#34;&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
enableACME &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
forceSSL &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
root &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;/var/lib/blog/current&amp;#34;&lt;/span>;
};
users&lt;span style="color:#f92672">.&lt;/span>users&lt;span style="color:#f92672">.&lt;/span>blog &lt;span style="color:#f92672">=&lt;/span> {
&lt;span style="color:#66d9ef">inherit&lt;/span> home;
createHome &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
isNormalUser &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
openssh &lt;span style="color:#f92672">=&lt;/span> config&lt;span style="color:#f92672">.&lt;/span>users&lt;span style="color:#f92672">.&lt;/span>users&lt;span style="color:#f92672">.&lt;/span>andi&lt;span style="color:#f92672">.&lt;/span>openssh;
};
systemd&lt;span style="color:#f92672">.&lt;/span>services&lt;span style="color:#f92672">.&lt;/span>&lt;span style="color:#e6db74">&amp;#34;blog-prepare-git-repo&amp;#34;&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
wantedBy &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#e6db74">&amp;#34;multi-user.target&amp;#34;&lt;/span> ];
path &lt;span style="color:#f92672">=&lt;/span> [
pkgs&lt;span style="color:#f92672">.&lt;/span>git
];
script &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> set -ex
&lt;/span>&lt;span style="color:#e6db74"> cd &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>home&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> chmod +rX &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>home&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74"> # no secrets in that home dir, everyone can read my public blog
&lt;/span>&lt;span style="color:#e6db74"> test -e repo || git init --bare repo
&lt;/span>&lt;span style="color:#e6db74"> ln -nsf &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>post-receive&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74"> repo/hooks/post-receive
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
serviceConfig &lt;span style="color:#f92672">=&lt;/span> {
Kind &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;one-shot&amp;#34;&lt;/span>;
User &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;blog&amp;#34;&lt;/span>;
};
};
};
}
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>What options are there to build Rust projects with Nix?</title><link>https://andreas.rammhold.de/posts/nix-rust/</link><pubDate>Tue, 29 Sep 2020 23:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/nix-rust/</guid><description>&lt;p>This post is for those that are already convinced that they want to use Nix for
building their Rust applications. Over the course of the last years a few
different approaches to the problem have evolved. Each comes with up- &amp;amp;
downsides and the right choice depends on your requirements. I most likely forgot
one or more tools that I haven&amp;rsquo;t used. Feel free to shoot me an email and I&amp;rsquo;ll
do my best of adding it.&lt;/p>
&lt;h1 id="buildrustpackagehttpsnixosorgmanualnixpkgsstablecompiling-rust-applications-with-cargo">&lt;a href="https://nixos.org/manual/nixpkgs/stable/#compiling-rust-applications-with-cargo">buildRustPackage&lt;/a>&lt;/h1>
&lt;p>Within Nixpkgs there is a builder for Rust packages called
&lt;code>buildRustPackage&lt;/code>.There is not much secret sauce in the idea. The build is basically split into to phases. The first phase downloads all the dependencies using &lt;code>cargo-download&lt;/code>. The second phase invokes &lt;code>cargo&lt;/code> to build &amp;amp; optionally run the tests.&lt;/p>
&lt;p>Here is how you use it:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">{ nixpkgs &lt;span style="color:#f92672">?&lt;/span> &lt;span style="color:#e6db74">&amp;lt;nixpkgs&amp;gt;&lt;/span> }:
&lt;span style="color:#66d9ef">let&lt;/span> pkgs &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">import&lt;/span> nixpkgs {}; &lt;span style="color:#66d9ef">in&lt;/span>
pkgs&lt;span style="color:#f92672">.&lt;/span>buildRustPackage {
pname &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;my-package&amp;#34;&lt;/span>;
version &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;1.0&amp;#34;&lt;/span>;
src &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">./.&lt;/span>;
cargoSha256 &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;0000000000000000000000000000000000000000000000000000000000000000&amp;#34;&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see the usage is really straight forward and you do not need to
generate any files in the repository. That being said you&amp;rsquo;ll have to update the
&lt;code>cargoSha256&lt;/code> field whenever you change any of your dependencies.&lt;/p>
&lt;p>The obvious downside is that fixed output derivation can be hard to deal with
and this usage is, to some degree, an abuse of the FOD&amp;rsquo;s that were intended for
simple fetching of source archives. In the past &lt;code>buildRustPackage&lt;/code> builds had
issues across difference architectures as &lt;code>cargo-vendor&lt;/code> would generate
different outputs.&lt;/p>
&lt;p>Pro:&lt;/p>
&lt;ul>
&lt;li>Really low bar for entry&lt;/li>
&lt;li>No generated files to check into the repo&lt;/li>
&lt;/ul>
&lt;p>Con:&lt;/p>
&lt;ul>
&lt;li>Slow for big projects as it compiles everything in one
go.&lt;/li>
&lt;li>Fixed output hashes have to be maintained.&lt;/li>
&lt;/ul>
&lt;h1 id="import-cargohttpsgithubcomedolstraimport-cargo">&lt;a href="https://github.com/edolstra/import-cargo">import-cargo&lt;/a>&lt;/h1>
&lt;p>Eelco Dolstra wrote &lt;code>cargo-import&lt;/code> which, compared to the previous contender,
doesn&amp;rsquo;t require any fixed output updating or manual setps after the initial
setup. Just like &lt;code>buildRustPackage&lt;/code>, it is doing all the compilation as a single
large build without any support for incremental recompilation.&lt;/p>
&lt;p>The usage, as described in the repo, requires you to use the experimental
flakes support in nix:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">{
description &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;My Rust project&amp;#34;&lt;/span>;
inputs &lt;span style="color:#f92672">=&lt;/span> {
nixpkgs&lt;span style="color:#f92672">.&lt;/span>url &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">github:NixOS/nixpkgs/nixos-20.03&lt;/span>;
import-cargo&lt;span style="color:#f92672">.&lt;/span>url &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">github:edolstra/import-cargo&lt;/span>;
};
outputs &lt;span style="color:#f92672">=&lt;/span> { self&lt;span style="color:#f92672">,&lt;/span> nixpkgs&lt;span style="color:#f92672">,&lt;/span> import-cargo }: &lt;span style="color:#66d9ef">let&lt;/span>
&lt;span style="color:#66d9ef">inherit&lt;/span> (import-cargo&lt;span style="color:#f92672">.&lt;/span>builders) importCargo;
&lt;span style="color:#66d9ef">in&lt;/span> {
defaultPackage&lt;span style="color:#f92672">.&lt;/span>x86_64-linux &lt;span style="color:#f92672">=&lt;/span>
&lt;span style="color:#66d9ef">with&lt;/span> &lt;span style="color:#f92672">import&lt;/span> nixpkgs { system &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;x86_64-linux&amp;#34;&lt;/span>; };
stdenv&lt;span style="color:#f92672">.&lt;/span>mkDerivation {
name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;testrust&amp;#34;&lt;/span>;
src &lt;span style="color:#f92672">=&lt;/span> self;
nativeBuildInputs &lt;span style="color:#f92672">=&lt;/span> [
&lt;span style="color:#75715e"># setupHook which makes sure that a CARGO_HOME with vendored dependencies&lt;/span>
&lt;span style="color:#75715e"># exists&lt;/span>
(importCargo { lockFile &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">./Cargo.lock&lt;/span>; &lt;span style="color:#66d9ef">inherit&lt;/span> pkgs; })&lt;span style="color:#f92672">.&lt;/span>cargoHome
&lt;span style="color:#75715e"># Build-time dependencies&lt;/span>
rustc cargo
];
buildPhase &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> cargo build --release --offline
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
installPhase &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> install -Dm775 ./target/release/testrust $out/bin/testrust
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
};
};
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>(example taken from the upstream repo)&lt;/p>
&lt;p>For anyone that is familiar with regular &lt;code>stdenv.mkDerivation&lt;/code> builds this
should read very familiar. You can integrate this easily into poly-language
projects that might invoke &lt;code>cargo&lt;/code> at some point during the build.&lt;/p>
&lt;p>These days I still actively avoid experimental flakes for most of my projects.
I&amp;rsquo;ve extracted the logic into
a &lt;a href="https://gist.github.com/andir/99e3260b75a511c4462a0001cc65198e">Gist&lt;/a> that you
can just &lt;code>callPackage&lt;/code>.&lt;/p>
&lt;p>Pro:&lt;/p>
&lt;ul>
&lt;li>No generated files to check into the repo&lt;/li>
&lt;li>No manual updating of files even when dependencies change&lt;/li>
&lt;/ul>
&lt;p>Cons:&lt;/p>
&lt;ul>
&lt;li>Upstream version requires usage of experimental Nix features&lt;/li>
&lt;li>Slow for big projects as it compiles everything in one
go.&lt;/li>
&lt;/ul>
&lt;h1 id="naerskhttpsgithubcomnmattianaersk">&lt;a href="https://github.com/nmattia/naersk">naersk&lt;/a>&lt;/h1>
&lt;p>&lt;code>naersk&lt;/code> is very similar to &lt;code>import-cargo&lt;/code> but it has a major upside: It builds
dependencies independent from the actual target. This gives you caching of
dependencies and reduces the main build to just building your code and linking
it to the dependencies.&lt;/p>
&lt;p>While it is doing much better than any of the alternatives already it still
isn&amp;rsquo;t perfect for large repositories with multiple rust projects that share a
large number of overlapping dependencies. I&amp;rsquo;m not holding this against it. It is an excellent piece nonetheless.&lt;/p>
&lt;p>Instead of quoting a usage example I&amp;rsquo;d like to refer you to the upstream
documentation which is really well done and offers more options than I would
consider covering here.&lt;/p>
&lt;p>Pro:&lt;/p>
&lt;ul>
&lt;li>Incremental builds with cached dependencies&lt;/li>
&lt;li>No generated files to check into the repo&lt;/li>
&lt;li>No manual updating of files even when dependencies change&lt;/li>
&lt;/ul>
&lt;p>Cons: Not really any unless you have plenty of in-house crates that share dependencies (or depend on each other).&lt;/p>
&lt;h1 id="crate2nixhttpsgithubcomkollochcrate2nix">&lt;a href="https://github.com/kolloch/crate2nix">crate2nix&lt;/a>&lt;/h1>
&lt;p>Last but not least, there is also &lt;code>crate2nix&lt;/code>, which is the logical replacement
of &lt;code>carnix&lt;/code> (an early pioneer in the era that is effectively dead). Back in the
days &lt;code>carnix&lt;/code> introduced &lt;code>buildRustCrate&lt;/code> to &lt;code>nixpkgs&lt;/code>. The main motivation
here is to build each crate as their own derivation. This leads to very good
incremental builds. You will rebuild a sub-tree of the dependencies when you
change one instead of all of them. It also allows you to re-use already
compiled dependencies across rust builds which furthermore cuts down the
compile times in some situations.&lt;/p>
&lt;p>The downside to all the other approaches is that you&amp;rsquo;ll have to generate Nix
expressions and check them into your repository. These expressions can be quite large but for some the benefits might outweigh the costs.&lt;/p>
&lt;p>In order to use it you&amp;rsquo;ll have to make the &lt;code>crate2nix&lt;/code> tool available in your
shell. Once you&amp;rsquo;ve done that it comes down to invoking &lt;code>crate2nix generate&lt;/code> followed by &lt;code>nix-build -A rootCrate.build&lt;/code>.&lt;/p>
&lt;p>Pro:&lt;/p>
&lt;ul>
&lt;li>Suitable for large projects where build closures are overlapping as each dependency is it&amp;rsquo;s own derivation&lt;/li>
&lt;/ul>
&lt;p>Cons:&lt;/p>
&lt;ul>
&lt;li>Large generated files have to be checked into the repo&lt;/li>
&lt;/ul>
&lt;h1 id="comparison-table">Comparison table&lt;/h1>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>buildRustPackage&lt;/th>
&lt;th>import-cargo&lt;/th>
&lt;th>naersk&lt;/th>
&lt;th>crate2nix&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Requires Generated Files&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Manual FOD hashes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Requires experimental Nix&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Yes (but see comment below)&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Incremental build&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Yes, but not for dependencies&lt;/td>
&lt;td>Yes, for all crates&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table></description></item><item><title>ranz2nix - A new approach to packaging node packages with Nix</title><link>https://andreas.rammhold.de/posts/ranz2nix/</link><pubDate>Tue, 14 Jul 2020 12:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/ranz2nix/</guid><description>&lt;p>A few weeks ago I was asked to create a simple online gallery for my sisters
wedding pictures. The whole process of selecting the right software probably
deserves a blog post on it&amp;rsquo;s own. In the end I decided to go with
&lt;a href="https://photoprism.org">photoprism&lt;/a> as it looked like the best solution (on
multiple scales).&lt;/p>
&lt;p>The unfortunate fact was that they are using Node.js to build their frontend
application which meant that I had to use node2nix, yarn2nix, … or to roll my
own solution. A few attempts into using node2nix (paired with previous
experiences) I decided that it isn&amp;rsquo;t worth my time. I don&amp;rsquo;t mean to discredit
the work that was done there but for me it simply doesn&amp;rsquo;t solve the problem.&lt;/p>
&lt;p>Mostly I am not a fan of generating mostly duplicate Nix code out of some other
source repository when all the information (the native package manager
requires) are already available. I could never figure out how to properly patch
the generated &lt;code>node2nix&lt;/code> expressions without resorting to manually calling
&lt;code>patch&lt;/code> as part of the upgrade process. It also doesn&amp;rsquo;t really work without
checking out the projects source code and running the code generation within
it.&lt;/p>
&lt;p>After all I just wanted to use &lt;a href="https://github.com/nmattia/niv">niv&lt;/a> to pin and
update it without requiring further recurring manual steps.&lt;/p>
&lt;p>I started looking at the contents of the
&lt;a href="https://github.com/photoprism/photoprism/blob/04692beb60baa15d02314c8cbbdeea2ae5099822/frontend/package-lock.json">&lt;code>package-lock.json&lt;/code>&lt;/a>
to see if it contains anything of use for an alternative approach and was pleasantly surprised.&lt;/p>
&lt;p>Unlike many other lock file formats it is just plain JSON that contains both
the &lt;a href="https://www.w3.org/TR/SRI/">SRI hash&lt;/a> and the source URI of each of the
locked dependencies:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-json" data-lang="json">{
&lt;span style="color:#f92672">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;photoprism&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;version&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;1.0.0&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;lockfileVersion&amp;#34;&lt;/span>: &lt;span style="color:#ae81ff">1&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;requires&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;dependencies&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;@babel/cli&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;version&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;7.10.4&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;resolved&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;https://registry.npmjs.org/@babel/cli/-/cli-7.10.4.tgz&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;integrity&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;sha512-xX99K4V1BzGJdQANK5cwK+EpF1vP9gvqhn+iWvG+TubCjecplW7RSQimJ2jcCvu6fnK5pY6mZMdu6EWTj32QVA==&amp;#34;&lt;/span>,
&lt;span style="color:#960050;background-color:#1e0010">…&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This means I can just fetch all of the dependencies with Nix
without writing a single line of Node.js or caring about internals of &lt;code>npm&lt;/code>
like dependency resolution (and circular dependencies).&lt;/p>
&lt;p>But how does that help?&lt;/p>
&lt;p>Well, iff NPM uses a &amp;ldquo;fetcher&amp;rdquo; that supports &lt;code>file://&lt;/code> URI&amp;rsquo;s I can rewrite the
lockfile in such a way that the &lt;code>resolved&lt;/code> values point to nix store paths
without invalidating the integrity hashes. By doing this we no longer need
network access during the build and everything can be provided within the build
sandbox. The sources will be made available since the patched lockfile will
carry them as dependencies.&lt;/p>
&lt;p>At this stage all that is left is actually rewriting the contents of the file.
Since Nix has builtin support for JSON so it was straight forward to recursively
updating all the dependencies in the file:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">{
lockFilePath
}:
&lt;span style="color:#66d9ef">let&lt;/span>
&lt;span style="color:#75715e"># …&lt;/span>
lockFile &lt;span style="color:#f92672">=&lt;/span> builtins&lt;span style="color:#f92672">.&lt;/span>fromJSON (builtins&lt;span style="color:#f92672">.&lt;/span>readFile lockFilePath);
&lt;span style="color:#75715e"># …&lt;/span>
patchedLockfile &lt;span style="color:#f92672">=&lt;/span> pkgs&lt;span style="color:#f92672">.&lt;/span>writeTextFile {
name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;package-lock.json&amp;#34;&lt;/span>;
destination &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;/package-lock.json&amp;#34;&lt;/span>;
text &lt;span style="color:#f92672">=&lt;/span> (
&lt;span style="color:#66d9ef">let&lt;/span>
patchDep &lt;span style="color:#f92672">=&lt;/span> sourcesList: name: v:
&lt;span style="color:#66d9ef">let&lt;/span>
dsources &lt;span style="color:#f92672">=&lt;/span> lib&lt;span style="color:#f92672">.&lt;/span>mapAttrs (mkSource) (v&lt;span style="color:#f92672">.&lt;/span>dependencies or { });
src &lt;span style="color:#f92672">=&lt;/span> findBestSource sourcesList name;
&lt;span style="color:#66d9ef">in&lt;/span>
(v &lt;span style="color:#f92672">//&lt;/span> {
dependencies &lt;span style="color:#f92672">=&lt;/span> lib&lt;span style="color:#f92672">.&lt;/span>mapAttrs (patchDep ([ dsources ] &lt;span style="color:#f92672">++&lt;/span> sourcesList)) (v&lt;span style="color:#f92672">.&lt;/span>dependencies or { });
}) &lt;span style="color:#f92672">//&lt;/span> (&lt;span style="color:#66d9ef">if&lt;/span> src &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span> &lt;span style="color:#66d9ef">then&lt;/span> packageOverride name v &lt;span style="color:#66d9ef">else&lt;/span> {
resolved &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;file://&amp;#34;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> (toString src);
}
);
file &lt;span style="color:#f92672">=&lt;/span> lockFile &lt;span style="color:#f92672">//&lt;/span> {
dependencies &lt;span style="color:#f92672">=&lt;/span> lib&lt;span style="color:#f92672">.&lt;/span>mapAttrs (patchDep [ sources ]) (lockFile&lt;span style="color:#f92672">.&lt;/span>dependencies or { });
};
&lt;span style="color:#66d9ef">in&lt;/span>
builtins&lt;span style="color:#f92672">.&lt;/span>toJSON file
);
};
&lt;span style="color:#75715e"># …&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>(&lt;a href="https://github.com/andir/ranz2nix/blob/637276c3110716d2a798e737fe62e54b37190d50/default.nix#L57-L80">Full source&lt;/a>)&lt;/p>
&lt;p>Now we can use the patched lockfile and create a new source tree with the
lockfile being replaced. All that is left then is to call &lt;code>npm --offline install&lt;/code> to install all the dependencies Node.js projects.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">{
&lt;span style="color:#75715e"># …&lt;/span>
patchedBuildRoot &lt;span style="color:#f92672">=&lt;/span> pkgs&lt;span style="color:#f92672">.&lt;/span>symlinkJoin {
name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;patched-root-&lt;/span>&lt;span style="color:#e6db74">${&lt;/span>lockFile&lt;span style="color:#f92672">.&lt;/span>name&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>;
paths &lt;span style="color:#f92672">=&lt;/span> [
patchedLockfile
sourcePath
];
};
patchedBuild &lt;span style="color:#f92672">=&lt;/span> pkgs&lt;span style="color:#f92672">.&lt;/span>stdenv&lt;span style="color:#f92672">.&lt;/span>mkDerivation {
name &lt;span style="color:#f92672">=&lt;/span> lockFile&lt;span style="color:#f92672">.&lt;/span>name;
src &lt;span style="color:#f92672">=&lt;/span> patchedBuildRoot;
buildInputs &lt;span style="color:#f92672">=&lt;/span> [ nodejs ];
buildPhase &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> export HOME=$(mktemp -d)
&lt;/span>&lt;span style="color:#e6db74"> chmod -R +rw .
&lt;/span>&lt;span style="color:#e6db74"> npm --offline install
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
installPhase &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> ls -la
&lt;/span>&lt;span style="color:#e6db74"> mkdir $out
&lt;/span>&lt;span style="color:#e6db74"> cp -rv node_modules $out/node_modules
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
passthru&lt;span style="color:#f92672">.&lt;/span>lockFile &lt;span style="color:#f92672">=&lt;/span> patchedLockfile &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#e6db74">&amp;#34;/package-lock.json&amp;#34;&lt;/span>;
};
&lt;span style="color:#75715e"># …&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>(&lt;a href="https://github.com/andir/ranz2nix/blob/637276c3110716d2a798e737fe62e54b37190d50/default.nix#L57-L80">Full source&lt;/a>)&lt;/p>
&lt;p>With the &lt;code>node_modules&lt;/code> folder being handled we can move on to the
actual build process. For the case of &lt;code>photoprism&lt;/code> there was one dependency
that didn&amp;rsquo;t have any sources in the lockfile (namely &lt;code>minimist&lt;/code>). For that
purpose I quickly added a &lt;code>packageOverride&lt;/code> attribute to the lockfile patching
code. It allows specifying sources for all those where sources are missing. I
expected there to be more of these edge cases but apparently that was all that
was required. The entire frontend build expression turned out to be fairly
simple:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">{
&lt;span style="color:#75715e"># …&lt;/span>
frontend &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">let&lt;/span>
noderanz &lt;span style="color:#f92672">=&lt;/span> callPackage ranz2nix {
nodejs &lt;span style="color:#f92672">=&lt;/span> nodejs-12_x;
sourcePath &lt;span style="color:#f92672">=&lt;/span> src &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#e6db74">&amp;#34;/frontend&amp;#34;&lt;/span>;
packageOverride &lt;span style="color:#f92672">=&lt;/span> name: spec: &lt;span style="color:#66d9ef">if&lt;/span> name &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#34;minimist&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> spec &lt;span style="color:#f92672">?&lt;/span> resolved &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> spec&lt;span style="color:#f92672">.&lt;/span>resolved &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">then&lt;/span> {
resolved &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;file://&amp;#34;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> (
toString (
fetchurl {
url &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz&amp;#34;&lt;/span>;
sha256 &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;0w7jll4vlqphxgk9qjbdjh3ni18lkrlfaqgsm7p14xl3f7ghn3gc&amp;#34;&lt;/span>;
}
)
);
} &lt;span style="color:#66d9ef">else&lt;/span> {};
};
node_modules &lt;span style="color:#f92672">=&lt;/span> noderanz&lt;span style="color:#f92672">.&lt;/span>patchedBuild;
&lt;span style="color:#66d9ef">in&lt;/span>
stdenv&lt;span style="color:#f92672">.&lt;/span>mkDerivation {
name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;photoprism-frontend&amp;#34;&lt;/span>;
nativeBuildInputs &lt;span style="color:#f92672">=&lt;/span> [ nodejs-12_x ];
&lt;span style="color:#66d9ef">inherit&lt;/span> src;
sourceRoot &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;photoprism-src/frontend&amp;#34;&lt;/span>;
postUnpack &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> chmod -R +rw .
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
NODE_ENV &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;production&amp;#34;&lt;/span>;
buildPhase &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> export HOME=$(mktemp -d)
&lt;/span>&lt;span style="color:#e6db74"> ln -sf &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>node_modules&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/node_modules node_modules
&lt;/span>&lt;span style="color:#e6db74"> ln -sf &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>node_modules&lt;span style="color:#f92672">.&lt;/span>lockFile&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74"> package-lock.json
&lt;/span>&lt;span style="color:#e6db74"> npm run build
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
installPhase &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> cp -rv ../assets/static/build $out
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
};
&lt;span style="color:#75715e"># …&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>(&lt;a href="https://github.com/andir/infra/blob/f804276a8ac4ad5b3cccda59586bb34c7bb706c0/nix/packages/photoprism/default.nix#L25-L65">Full source&lt;/a>)&lt;/p>
&lt;p>And that is all! Hurray!&lt;/p>
&lt;p>When compared to any other Node.js approach this was actually fun to use and to
develop. With a bit of dedicated time and brainstorming this little PoC could
evolve into a more sophisticated solution. For now I am done with this project
as it just does what I need and I can finally get back to deploying the gallery.
:-)&lt;/p>
&lt;p>&lt;strong>EDIT&lt;/strong>: Since all of the above was just a PoC and was still very rough on the
edges I started working on a cleaner version of this approach over at
&lt;a href="https://github.com/andir/npmlock2nix">npmlock2nix&lt;/a>.&lt;/p></description></item><item><title>NixOS IPv6 Prefix Delegation</title><link>https://andreas.rammhold.de/posts/nixos-networkd-ipv6-prefix-delegation/</link><pubDate>Fri, 01 May 2020 21:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/nixos-networkd-ipv6-prefix-delegation/</guid><description>&lt;blockquote>
&lt;p>Note: This used to be a draft that I had lying around for more than a year now. I&amp;rsquo;ve decided to just publish it now.&lt;/p>
&lt;/blockquote>
&lt;p>My home internet connection comes with proper dual-stack support. I get a
public IPv4 address via DHCP, a /128 (&lt;code>IA_NA&lt;/code>) and a /48 IPv6 prefix (&lt;code>IA_PD&lt;/code>) via DHCPv6.&lt;/p>
&lt;p>Traditionally you would configure your networking via
&lt;a href="https://wiki.linuxfoundation.org/networking/iproute2">iproute2&lt;/a> and then
fork-off a DHCP client to configure the external addresses.&lt;/p>
&lt;p>Usually all of that is being hidden from you through wrappers (like Debian&amp;rsquo;s
ifupdown). The configuration would be set, the daemons fired off and hopefully
everything would go well.&lt;/p>
&lt;p>The limitations of the system become visible once you have a more dynamic set
of interfaces that have to be initialized in some order, some VPN device that
depend on the uplink connection etc. While systems like ifupdown have employed
hooks of all sorts that you still end up writing a bunch of (inlined) shell
scripts that deal with some little details of your setup. Adding sleep
statements at worst. Things get tricky when one interface going up changes
things on another interface or even system wide (think sysctl, iptables,
starting a VPN daemon, …).&lt;/p>
&lt;p>As soon as your configuration grows (over days, months, years, …) it becomes
harder to verify that everything still works after a clean reboot of the
system. After all you&amp;rsquo;ve been modifying the system with each &amp;ldquo;restart&amp;rdquo; of the
network configuration script/tooling.&lt;/p>
&lt;p>I&amp;rsquo;ve had systems work reliably after a reboot with huge networking scripts on
these systems. Was it fun? Was it straightforward? No, not really. You really
had to know what you were doing. Having to manually fiddle with some parameters
after a reboot wasn&amp;rsquo;t uncommon. Sometimes services wouldn&amp;rsquo;t come up properly
because the network was not up when they were being scheduled.&lt;/p>
&lt;p>Here is where we should start talking about &lt;code>systemd-networkd&lt;/code> (or just
&lt;code>networkd&lt;/code>). &lt;code>networkd&lt;/code> allows you to declare all (or really almost everything)
that you previously encoded in lines and lines of shell-like configuration
files as structured configuration. That doesn&amp;rsquo;t just include the (static)
addresses, routes, VLANs, &lt;a href="./posts/linux-ip-vrf-systemd-networkd">VRFs&lt;/a>, bridges,
policy routing, wirguard, … but also runtime configuration like DHCPv4 and
DHCPv6 and prefix delegation. You can configure when the system is to be
considered &amp;ldquo;online&amp;rdquo; and which interfaces (in which states) contribute to
that.&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/p>
&lt;p>This makes for a very nice property: You now have a single directory of plain
text files that define all your network configurations. As long as you have
&lt;code>networkd&lt;/code> installed and a way to render your &lt;code>/etc/systemd/network&lt;/code>
configuration you are good. Rolling back a change now just means rolling back
that directory. Versioning text files is what we are good at, no?&lt;/p>
&lt;p>Ontop of that we can make use of &lt;a href="https://nixos.org/nix">Nix&lt;/a> and specifically
the &lt;a href="https://nixos.org/nixos">NixOS&lt;/a> module system to generate the NixOS
configuration and also manage the services on our system in the very same way.&lt;/p>
&lt;p>And this gets me back to the topic of this post. My residential internet
connection and configuring my CPE aka router. I was planning to do the
migration from my old venerable Debian system to a shiny new NixOS machine but
I didn&amp;rsquo;t just want to pull the plug and fiddle until everything is seeminlgy
working again.&lt;/p>
&lt;p>I decided to just simulate a simplified version of the relevant pieces of the
ISP infra and test my poential configuration against that. Since the
termination just happens on an (isolated) Ethernet L2 all I really worried
about was getting the DHCPv6 client configuration right. Especially the prefix
delegation. For this blog post I trimmed it down to just use the DHCPv6 prefix
delegation part.&lt;/p>
&lt;p>This is the high level definition of the test:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">&lt;span style="color:#66d9ef">let&lt;/span>
makeTest &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">import&lt;/span> (&lt;span style="color:#e6db74">&amp;lt;nixpkgs&amp;gt;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#e6db74">&amp;#34;/nixos/tests/make-test-python.nix&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">in&lt;/span>
makeTest (
{ pkgs&lt;span style="color:#f92672">,&lt;/span> lib&lt;span style="color:#f92672">,&lt;/span> &lt;span style="color:#f92672">...&lt;/span> }:
{
nodes &lt;span style="color:#f92672">=&lt;/span> {
&lt;span style="color:#75715e"># The `isp` node will represent my upstream ISP. It will run a&lt;/span>
&lt;span style="color:#75715e"># traditional network stack with isc&amp;#39;s dhcpd4 and dhcpd6&lt;/span>
isp &lt;span style="color:#f92672">=&lt;/span> {
virtualisation&lt;span style="color:#f92672">.&lt;/span>vlans &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#ae81ff">1&lt;/span> ];
imports &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#e6db74">./isp.nix&lt;/span> ];
};
&lt;span style="color:#75715e"># My CPE / router that will handle internal as well as external&lt;/span>
&lt;span style="color:#75715e"># DHCPv{4,6}, RA etc..&lt;/span>
router &lt;span style="color:#f92672">=&lt;/span> {
virtualisation&lt;span style="color:#f92672">.&lt;/span>vlans &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span> ];
&lt;span style="color:#75715e"># import the custom module that contains all the implementation details&lt;/span>
imports &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#e6db74">./router.nix&lt;/span> ];
};
&lt;span style="color:#75715e"># A test client that will verify the result&lt;/span>
client &lt;span style="color:#f92672">=&lt;/span> {
virtualisation&lt;span style="color:#f92672">.&lt;/span>vlans &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#ae81ff">2&lt;/span> ];
imports &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#e6db74">./client.nix&lt;/span> ];
};
};
testScript &lt;span style="color:#f92672">=&lt;/span> builtins&lt;span style="color:#f92672">.&lt;/span>readFile &lt;span style="color:#e6db74">./test.py&lt;/span>;
}
)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>First the &lt;code>makeTest&lt;/code> function is being imported from &lt;code>&amp;lt;nixpkgs&amp;gt;&lt;/code>. It serves as
a way to construct NixOS VM tests from the package set. Each test contains a
number of machine definitions and a test script.&lt;/p>
&lt;p>Each of the nodes can be in multiple virtual networks. The ISP and the Router
node will share one VLAN (&lt;code>1&lt;/code>), the Router and the Client node will share another
(&lt;code>2&lt;/code>). This simulates the missing wires/switches/wireless between the devices.&lt;/p>
&lt;p>Each of the nodes imports a single file that contains the detailed
configuration.&lt;/p>
&lt;p>The next step was filling in the details about how the ISPs side of things
operates. I mostly just copied in some files that I had lying around from a
previous setup. Here is the commented &lt;code>isp.nix&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">{ lib&lt;span style="color:#f92672">,&lt;/span> pkgs&lt;span style="color:#f92672">,&lt;/span> &lt;span style="color:#f92672">...&lt;/span> }:
{
&lt;span style="color:#75715e"># The ISP&amp;#39;s routers job is to delegate IPv6 prefixes via DHCPv6. Like with&lt;/span>
&lt;span style="color:#75715e"># regular IPv6 auto-configuration it will also emit IPv6 router&lt;/span>
&lt;span style="color:#75715e"># advertisements (RAs). Those RA&amp;#39;s will not carry a prefix but in contrast&lt;/span>
&lt;span style="color:#75715e"># just set the &amp;#34;Other&amp;#34; flag to indicate to the receiving nodes that they&lt;/span>
&lt;span style="color:#75715e"># should attempt DHCPv6.&lt;/span>
&lt;span style="color:#75715e">#&lt;/span>
&lt;span style="color:#75715e"># Note: On the ISPs device we don&amp;#39;t really care if we are using networkd in&lt;/span>
&lt;span style="color:#75715e"># this example. That being said we can&amp;#39;t use it (yet) as networkd doesn&amp;#39;t&lt;/span>
&lt;span style="color:#75715e"># implement the serving side of DHCPv6. We will use ISC&amp;#39;s well aged dhcpd6&lt;/span>
&lt;span style="color:#75715e"># for that task.&lt;/span>
networking &lt;span style="color:#f92672">=&lt;/span> {
useDHCP &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>;
firewall&lt;span style="color:#f92672">.&lt;/span>enable &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>;
interfaces&lt;span style="color:#f92672">.&lt;/span>eth1&lt;span style="color:#f92672">.&lt;/span>ipv4&lt;span style="color:#f92672">.&lt;/span>addresses &lt;span style="color:#f92672">=&lt;/span> lib&lt;span style="color:#f92672">.&lt;/span>mkForce []; &lt;span style="color:#75715e"># no need for legacy IP&lt;/span>
interfaces&lt;span style="color:#f92672">.&lt;/span>eth1&lt;span style="color:#f92672">.&lt;/span>ipv6&lt;span style="color:#f92672">.&lt;/span>addresses &lt;span style="color:#f92672">=&lt;/span> lib&lt;span style="color:#f92672">.&lt;/span>mkForce [
{ address &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;2001:DB8::&amp;#34;&lt;/span>; prefixLength &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">64&lt;/span>; }
];
};
&lt;span style="color:#75715e"># Since we want to program the routes that we delegate to the &amp;#34;customer&amp;#34;&lt;/span>
&lt;span style="color:#75715e"># into our routing table we must have a way to gain the required privs.&lt;/span>
&lt;span style="color:#75715e"># This security wrapper will do in our test setup.&lt;/span>
&lt;span style="color:#75715e">#&lt;/span>
&lt;span style="color:#75715e"># DO NOT COPY THIS TO PRODUCTION AS IS. Think about it at least twice.&lt;/span>
&lt;span style="color:#75715e"># Everyone on the &amp;#34;isp&amp;#34; machine will be able to add routes to the kernel.&lt;/span>
security&lt;span style="color:#f92672">.&lt;/span>wrappers&lt;span style="color:#f92672">.&lt;/span>add-dhcpd-lease &lt;span style="color:#f92672">=&lt;/span> {
source &lt;span style="color:#f92672">=&lt;/span> pkgs&lt;span style="color:#f92672">.&lt;/span>writeShellScript &lt;span style="color:#e6db74">&amp;#34;add-dhcpd-lease&amp;#34;&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> exec &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>pkgs&lt;span style="color:#f92672">.&lt;/span>iproute&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/bin/ip -6 route replace &amp;#34;$1&amp;#34; via &amp;#34;$2&amp;#34;
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
capabilities &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;cap_net_admin+ep&amp;#34;&lt;/span>;
};
services &lt;span style="color:#f92672">=&lt;/span> {
&lt;span style="color:#75715e"># Configure the DHCPv6 server&lt;/span>
&lt;span style="color:#75715e">#&lt;/span>
&lt;span style="color:#75715e"># We will hand out /48 prefixes from the subnet 2001:DB8:F000::/36.&lt;/span>
&lt;span style="color:#75715e"># That gives us ~8k prefixes. That should be enough for this test.&lt;/span>
&lt;span style="color:#75715e">#&lt;/span>
&lt;span style="color:#75715e"># Since (usually) you will not receive a prefix with the router&lt;/span>
&lt;span style="color:#75715e"># advertisements we also hand out /128 leases from the range&lt;/span>
&lt;span style="color:#75715e"># 2001:DB8:0000:0000:FFFF::/112.&lt;/span>
dhcpd6 &lt;span style="color:#f92672">=&lt;/span> {
enable &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
interfaces &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#e6db74">&amp;#34;eth1&amp;#34;&lt;/span> ];
extraConfig &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> subnet6 2001:DB8::/36 {
&lt;/span>&lt;span style="color:#e6db74"> range6 2001:DB8:0000:0000:FFFF:: 2001:DB8:0000:0000:FFFF::FFFF;
&lt;/span>&lt;span style="color:#e6db74"> prefix6 2001:DB8:F000:: 2001:DB8:FFFF:: /48;
&lt;/span>&lt;span style="color:#e6db74"> }
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> # This is the secret sauce. We have to extract the prefix and the
&lt;/span>&lt;span style="color:#e6db74"> # next hop when commiting the lease to the database. dhcpd6
&lt;/span>&lt;span style="color:#e6db74"> # (rightfully) has not concept of adding routes to the systems
&lt;/span>&lt;span style="color:#e6db74"> # routing table. It really depends on the setup.
&lt;/span>&lt;span style="color:#e6db74"> #
&lt;/span>&lt;span style="color:#e6db74"> # In a production environment your DHCPv6 server is likely not the
&lt;/span>&lt;span style="color:#e6db74"> # router. You might want to consider BGP, custom NetConf calls, …
&lt;/span>&lt;span style="color:#e6db74"> # in those cases.
&lt;/span>&lt;span style="color:#e6db74"> on commit {
&lt;/span>&lt;span style="color:#e6db74"> set IP = pick-first-value(binary-to-ascii(16, 16, &amp;#34;:&amp;#34;, substring(option dhcp6.ia-na, 16, 16)), &amp;#34;n/a&amp;#34;);
&lt;/span>&lt;span style="color:#e6db74"> set Prefix = pick-first-value(binary-to-ascii(16, 16, &amp;#34;:&amp;#34;, suffix(option dhcp6.ia-pd, 16)), &amp;#34;n/a&amp;#34;);
&lt;/span>&lt;span style="color:#e6db74"> set PrefixLength = pick-first-value(binary-to-ascii(10, 8, &amp;#34;:&amp;#34;, substring(suffix(option dhcp6.ia-pd, 17), 0, 1)), &amp;#34;n/a&amp;#34;);
&lt;/span>&lt;span style="color:#e6db74"> log(concat(IP, &amp;#34; &amp;#34;, Prefix, &amp;#34; &amp;#34;, PrefixLength));
&lt;/span>&lt;span style="color:#e6db74"> execute(&amp;#34;/run/wrappers/bin/add-dhcpd-lease&amp;#34;, concat(Prefix,&amp;#34;/&amp;#34;,PrefixLength), IP);
&lt;/span>&lt;span style="color:#e6db74"> }
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
};
&lt;span style="color:#75715e"># Finally we have to set up the router advertisements. While we could be&lt;/span>
&lt;span style="color:#75715e"># using networkd or bird for this task `radvd` is probably the most&lt;/span>
&lt;span style="color:#75715e"># venerable of them all. It was made explicitly for this purpose and&lt;/span>
&lt;span style="color:#75715e"># the configuration is much more straightforward than what networkd&lt;/span>
&lt;span style="color:#75715e"># requires.&lt;/span>
&lt;span style="color:#75715e"># As outlined above we will have to set the `Managed` flag as otherwise&lt;/span>
&lt;span style="color:#75715e"># the clients will not know if they should do DHCPv6. (Some do&lt;/span>
&lt;span style="color:#75715e"># anyway/always)&lt;/span>
radvd &lt;span style="color:#f92672">=&lt;/span> {
enable &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
config &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74"> interface eth1 {
&lt;/span>&lt;span style="color:#e6db74"> AdvSendAdvert on;
&lt;/span>&lt;span style="color:#e6db74"> AdvManagedFlag on;
&lt;/span>&lt;span style="color:#e6db74"> AdvOtherConfigFlag off; # we don&amp;#39;t really have DNS or NTP or anything like that to distribute
&lt;/span>&lt;span style="color:#e6db74"> prefix ::/64 {
&lt;/span>&lt;span style="color:#e6db74"> AdvOnLink on;
&lt;/span>&lt;span style="color:#e6db74"> AdvAutonomous on;
&lt;/span>&lt;span style="color:#e6db74"> };
&lt;/span>&lt;span style="color:#e6db74"> };
&lt;/span>&lt;span style="color:#e6db74"> &amp;#39;&amp;#39;&lt;/span>;
};
};
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next the &lt;code>router.nix&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">&lt;span style="color:#75715e"># This will be our (residential) router that receives the IPv6 prefix (IA_PD)&lt;/span>
&lt;span style="color:#75715e"># and /128 (IA_NA) allocation.&lt;/span>
&lt;span style="color:#75715e">#&lt;/span>
&lt;span style="color:#75715e"># Here we will actually start using networkd.&lt;/span>
{
systemd&lt;span style="color:#f92672">.&lt;/span>services&lt;span style="color:#f92672">.&lt;/span>systemd-networkd&lt;span style="color:#f92672">.&lt;/span>environment&lt;span style="color:#f92672">.&lt;/span>SYSTEMD_LOG_LEVEL &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;debug&amp;#34;&lt;/span>;
boot&lt;span style="color:#f92672">.&lt;/span>kernel&lt;span style="color:#f92672">.&lt;/span>sysctl &lt;span style="color:#f92672">=&lt;/span> {
&lt;span style="color:#75715e"># we want to forward packets from the ISP to the client and back.&lt;/span>
&lt;span style="color:#e6db74">&amp;#34;net.ipv6.conf.all.forwarding&amp;#34;&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>;
};
networking &lt;span style="color:#f92672">=&lt;/span> {
useNetworkd &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
useDHCP &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>;
&lt;span style="color:#75715e"># Consider enabling this in production and generating firewall rules&lt;/span>
&lt;span style="color:#75715e"># for fowarding/input from the configured interfaces so you do not have&lt;/span>
&lt;span style="color:#75715e"># to manage multiple places&lt;/span>
firewall&lt;span style="color:#f92672">.&lt;/span>enable &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>;
};
systemd&lt;span style="color:#f92672">.&lt;/span>network &lt;span style="color:#f92672">=&lt;/span> {
networks &lt;span style="color:#f92672">=&lt;/span> {
&lt;span style="color:#75715e"># Configuration of the interface to the ISP.&lt;/span>
&lt;span style="color:#75715e"># We must request accept RAs and request the PD prefix.&lt;/span>
&lt;span style="color:#e6db74">&amp;#34;01-eth1&amp;#34;&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;eth1&amp;#34;&lt;/span>;
networkConfig &lt;span style="color:#f92672">=&lt;/span> {
Description &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;ISP interface&amp;#34;&lt;/span>;
IPv6AcceptRA &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
&lt;span style="color:#75715e">#DHCP = false; # no need for legacy IP&lt;/span>
};
linkConfig &lt;span style="color:#f92672">=&lt;/span> {
&lt;span style="color:#75715e"># We care about this interface when talking about being &amp;#34;online&amp;#34;.&lt;/span>
&lt;span style="color:#75715e"># If this interface is in the `routable` state we can reach&lt;/span>
&lt;span style="color:#75715e"># others and they should be able to reach us.&lt;/span>
RequiredForOnline &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;routable&amp;#34;&lt;/span>;
};
&lt;span style="color:#75715e"># This configures the DHCPv6 client part towards the ISPs DHCPv6 server.&lt;/span>
dhcpV6Config &lt;span style="color:#f92672">=&lt;/span> {
&lt;span style="color:#75715e"># We have to include a request for a prefix in our DHCPv6 client&lt;/span>
&lt;span style="color:#75715e"># request packets.&lt;/span>
&lt;span style="color:#75715e"># Otherwise the upstream DHCPv6 server wouldn&amp;#39;t know if we want a&lt;/span>
&lt;span style="color:#75715e"># prefix or not. Note: On some installation it makes sense to&lt;/span>
&lt;span style="color:#75715e"># always force that option on the DHPCv6 server since there are&lt;/span>
&lt;span style="color:#75715e"># certain CPEs that are just not setting this field but happily&lt;/span>
&lt;span style="color:#75715e"># accept the delegated prefix.&lt;/span>
PrefixDelegationHint &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;::/48&amp;#34;&lt;/span>;
};
ipv6PrefixDelegationConfig &lt;span style="color:#f92672">=&lt;/span> {
&lt;span style="color:#75715e"># Let networkd know that we would very much like to use DHCPv6&lt;/span>
&lt;span style="color:#75715e"># to obtain the &amp;#34;managed&amp;#34; information. Not sure why they can&amp;#39;t&lt;/span>
&lt;span style="color:#75715e"># just take that from the upstream RAs.&lt;/span>
Managed &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
};
};
&lt;span style="color:#75715e"># Interface to the client. Here we should redistribute a /64 from&lt;/span>
&lt;span style="color:#75715e"># the prefix we received from the ISP.&lt;/span>
&lt;span style="color:#e6db74">&amp;#34;01-eth2&amp;#34;&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;eth2&amp;#34;&lt;/span>;
networkConfig &lt;span style="color:#f92672">=&lt;/span> {
Description &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;Client interface&amp;#34;&lt;/span>;
&lt;span style="color:#75715e"># the client shouldn&amp;#39;t be allowed to send us RAs, that would be weird.&lt;/span>
IPv6AcceptRA &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>;
&lt;span style="color:#75715e"># Just delegate prefixes from the DHCPv6 PD pool.&lt;/span>
&lt;span style="color:#75715e"># If you also want to distribute a local ULA prefix you want to&lt;/span>
&lt;span style="color:#75715e"># set this to `yes` as that includes both static prefixes as well&lt;/span>
&lt;span style="color:#75715e"># as PD prefixes.&lt;/span>
IPv6PrefixDelegation &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;dhcpv6&amp;#34;&lt;/span>;
};
&lt;span style="color:#75715e"># finally &amp;#34;act as router&amp;#34; (according to systemd.network(5))&lt;/span>
ipv6PrefixDelegationConfig &lt;span style="color:#f92672">=&lt;/span> {
RouterLifetimeSec &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">300&lt;/span>; &lt;span style="color:#75715e"># required as otherwise no RA&amp;#39;s are being emitted&lt;/span>
&lt;span style="color:#75715e"># In a production environment you should consider setting these as well:&lt;/span>
&lt;span style="color:#75715e">#EmitDNS = true;&lt;/span>
&lt;span style="color:#75715e">#EmitDomains = true;&lt;/span>
&lt;span style="color:#75715e">#DNS= = &amp;#34;fe80::1&amp;#34;; # or whatever &amp;#34;well known&amp;#34; IP your router will have on the inside.&lt;/span>
};
&lt;span style="color:#75715e"># This adds a &amp;#34;random&amp;#34; ULA prefix to the interface that is being&lt;/span>
&lt;span style="color:#75715e"># advertised to the clients.&lt;/span>
&lt;span style="color:#75715e"># Not used in this test.&lt;/span>
&lt;span style="color:#75715e"># ipv6Prefixes = [&lt;/span>
&lt;span style="color:#75715e"># {&lt;/span>
&lt;span style="color:#75715e"># ipv6PrefixConfig = {&lt;/span>
&lt;span style="color:#75715e"># AddressAutoconfiguration = true;&lt;/span>
&lt;span style="color:#75715e"># PreferredLifetimeSec = 1800;&lt;/span>
&lt;span style="color:#75715e"># ValidLifetimeSec = 1800;&lt;/span>
&lt;span style="color:#75715e"># };&lt;/span>
&lt;span style="color:#75715e"># }&lt;/span>
&lt;span style="color:#75715e"># ];&lt;/span>
};
&lt;span style="color:#75715e"># finally we are going to add a static IPv6 unique local address to&lt;/span>
&lt;span style="color:#75715e"># the &amp;#34;lo&amp;#34; interface. This will serve as ICMPv6 echo target to&lt;/span>
&lt;span style="color:#75715e"># verify connectivity from the client to the router.&lt;/span>
&lt;span style="color:#e6db74">&amp;#34;01-lo&amp;#34;&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;lo&amp;#34;&lt;/span>;
addresses &lt;span style="color:#f92672">=&lt;/span> [
{ addressConfig&lt;span style="color:#f92672">.&lt;/span>Address &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;FD42::1/128&amp;#34;&lt;/span>; }
];
};
};
};
&lt;span style="color:#75715e"># make the network-online target a requirement, we wait for it in our test script&lt;/span>
systemd&lt;span style="color:#f92672">.&lt;/span>targets&lt;span style="color:#f92672">.&lt;/span>network-online&lt;span style="color:#f92672">.&lt;/span>wantedBy &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#e6db74">&amp;#34;multi-user.target&amp;#34;&lt;/span> ];
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>And finally the most simple of them all the &lt;code>client.nix&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-nix" data-lang="nix">&lt;span style="color:#75715e"># This is the client behind the router. We should be receving router&lt;/span>
&lt;span style="color:#75715e"># advertisements for both the ULA and the delegated prefix.&lt;/span>
&lt;span style="color:#75715e"># All we have to do is boot with the default (networkd) configuration.&lt;/span>
{
systemd&lt;span style="color:#f92672">.&lt;/span>services&lt;span style="color:#f92672">.&lt;/span>systemd-networkd&lt;span style="color:#f92672">.&lt;/span>environment&lt;span style="color:#f92672">.&lt;/span>SYSTEMD_LOG_LEVEL &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;debug&amp;#34;&lt;/span>;
networking &lt;span style="color:#f92672">=&lt;/span> {
useNetworkd &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
useDHCP &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>;
};
&lt;span style="color:#75715e"># make the network-online target a requirement, we wait for it in our test script&lt;/span>
systemd&lt;span style="color:#f92672">.&lt;/span>targets&lt;span style="color:#f92672">.&lt;/span>network-online&lt;span style="color:#f92672">.&lt;/span>wantedBy &lt;span style="color:#f92672">=&lt;/span> [ &lt;span style="color:#e6db74">&amp;#34;multi-user.target&amp;#34;&lt;/span> ];
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>While this looks pretty verbose the actual information density is pretty high
once you remove all the comments.&lt;/p>
&lt;p>Now that all the system configuraitons have been layed out we can start
thinking about testing the setup.&lt;/p>
&lt;p>The key results we want to gather from executing this test are:&lt;/p>
&lt;ol>
&lt;li>Does our router configure all the interfaces as we expect?&lt;/li>
&lt;li>Do we receive router advertisements on the client?&lt;/li>
&lt;li>Does the router request a prefix and advertise a subnet from that to the client?&lt;/li>
&lt;li>Is forwarding from / to the delegated prefix working?&lt;/li>
&lt;/ol>
&lt;p>We can do that fairly straightforward by filling in the previously left blank
&lt;code>testScript&lt;/code> attribute.&lt;/p>
&lt;p>As a first step we start the &lt;code>router&lt;/code> node and ensure that &lt;code>systemd-networkd&lt;/code>
had a chance to configure the interfaces and is ready to send out router
advertisements.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#75715e"># First start the router and wait for it it reach a state where we are&lt;/span>
&lt;span style="color:#75715e"># certain networkd is up and it is able to send out RAs&lt;/span>
router&lt;span style="color:#f92672">.&lt;/span>start()
router&lt;span style="color:#f92672">.&lt;/span>wait_for_unit(&lt;span style="color:#e6db74">&amp;#34;systemd-networkd.service&amp;#34;&lt;/span>)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Afterwards we can start the client and wait for it&amp;rsquo;s network stack to reache
the &lt;code>online&lt;/code> target. That means the system has brought up all the configured
interfaces and whatever interface was configured as uplink has reached the
desired state (&lt;code>routeable&lt;/code>) by waiting for the &lt;code>network-online.target&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">client&lt;span style="color:#f92672">.&lt;/span>start()
client&lt;span style="color:#f92672">.&lt;/span>wait_for_unit(&lt;span style="color:#e6db74">&amp;#34;network-online.target&amp;#34;&lt;/span>)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Since we previously configured a static address on the &lt;code>router&lt;/code> node
(&lt;code>FD42::1&lt;/code>) we can verify that we got a valid default router via the router by
attempting to reach said address.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#75715e"># the static address on the router should become reachable within a few seconds&lt;/span>
client&lt;span style="color:#f92672">.&lt;/span>wait_until_succeeds(&lt;span style="color:#e6db74">&amp;#34;ping -6 -c 1 FD42::1&amp;#34;&lt;/span>)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We did use the &lt;code>wait_until_succeeds&lt;/code> function since it might take a few seconds
for RAs to arrive, everything being setup etc.&lt;/p>
&lt;p>Once this succeeded we know that we have a working L3 connectivity between the
router and the client. That likely means that our objective 2. is fullfilled,
likely 1. is fine as well but we will continue being skeptical.&lt;/p>
&lt;p>At this point the upstream router is still powered off and we shouldn&amp;rsquo;t be able to reach any of the addresses configured on there. Just to make sure we didn&amp;rsquo;t screw up elsewhere in the setup we can verify that:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#75715e"># the global IP of the ISP router should still not be a reachable&lt;/span>
router&lt;span style="color:#f92672">.&lt;/span>fail(&lt;span style="color:#e6db74">&amp;#34;ping -6 -c 1 2001:DB8::&amp;#34;&lt;/span>)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now that we are somehwat certain that the &lt;code>router&lt;/code> and &lt;code>client&lt;/code> setup is kinda
working we can start the isp node and finally start checking the router
delegation.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">isp&lt;span style="color:#f92672">.&lt;/span>start()
&lt;span style="color:#75715e"># Since for the ISP &amp;#34;being online&amp;#34; should have no real meaning we just&lt;/span>
&lt;span style="color:#75715e"># wait for the target where all the units have been started.&lt;/span>
&lt;span style="color:#75715e"># It probably still takes a few more seconds for all the RA timers to be&lt;/span>
&lt;span style="color:#75715e"># fired etc..&lt;/span>
isp&lt;span style="color:#f92672">.&lt;/span>wait_for_unit(&lt;span style="color:#e6db74">&amp;#34;multi-user.target&amp;#34;&lt;/span>)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Since the router considers his uplink interface to be required to be online (as without uplink there is no external connectivity) we can now just wait for our router to reach the online state. Once that is reached we know that the ISP did hand out an IPv6 address and potentially an &lt;code>IA_PD&lt;/code> prefix:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#75715e"># wait until the uplink interface has a good status&lt;/span>
router&lt;span style="color:#f92672">.&lt;/span>wait_for_unit(&lt;span style="color:#e6db74">&amp;#34;network-online.target&amp;#34;&lt;/span>)
router&lt;span style="color:#f92672">.&lt;/span>wait_until_succeeds(&lt;span style="color:#e6db74">&amp;#34;ping -6 -c1 2001:DB8::&amp;#34;&lt;/span>)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This now tells us that the router node has obtained a default route and some
address that is routeable between it and the isp node. Shortly after that the client should obtain a public routeable addresss from the assigned prefix delegation. Yet again we give it a few seconds to propagate and thus we just use the &lt;code>wait_until_succeeds&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">client&lt;span style="color:#f92672">.&lt;/span>wait_until_succeeds(&lt;span style="color:#e6db74">&amp;#34;ping -6 -c1 2001:DB8::&amp;#34;&lt;/span>)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now that we know that we have connectivity all the way &lt;code>client &amp;lt;-&amp;gt; router &amp;lt;-&amp;gt; isp&lt;/code> we do a final check on the prefix we got assigned on the client. We expect an IPv6 prefix that has global scope and starts with the documentation prefix (&lt;code>2001:DB8:/32&lt;/code>):&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#75715e"># verify that we got a globally scoped address in eth1 from the&lt;/span>
&lt;span style="color:#75715e"># documentation prefix&lt;/span>
ip_output &lt;span style="color:#f92672">=&lt;/span> client&lt;span style="color:#f92672">.&lt;/span>succeed(&lt;span style="color:#e6db74">&amp;#34;ip --json -6 address show dev eth1&amp;#34;&lt;/span>)
&lt;span style="color:#f92672">import&lt;/span> json
ip_json &lt;span style="color:#f92672">=&lt;/span> json&lt;span style="color:#f92672">.&lt;/span>loads(ip_output)[&lt;span style="color:#ae81ff">0&lt;/span>]
&lt;span style="color:#66d9ef">assert&lt;/span> any(
addr[&lt;span style="color:#e6db74">&amp;#34;local&amp;#34;&lt;/span>]&lt;span style="color:#f92672">.&lt;/span>upper()&lt;span style="color:#f92672">.&lt;/span>startswith(&lt;span style="color:#e6db74">&amp;#34;2001:DB8:&amp;#34;&lt;/span>)
&lt;span style="color:#66d9ef">for&lt;/span> addr &lt;span style="color:#f92672">in&lt;/span> ip_json[&lt;span style="color:#e6db74">&amp;#34;addr_info&amp;#34;&lt;/span>]
&lt;span style="color:#66d9ef">if&lt;/span> addr[&lt;span style="color:#e6db74">&amp;#34;scope&amp;#34;&lt;/span>] &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#34;global&amp;#34;&lt;/span>
)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The code from the examples can be retrieved &lt;a href="./nixos-systemd-ipv6-pd-artifacts.tar.gz">here&lt;/a>&lt;/p>
&lt;hr>
&lt;p>This example is based on the &lt;a href="https://github.com/NixOS/nixpkgs/pull/86471">NixOS test&lt;/a>
that I contributed. For my actual deployment I am using a &lt;a href="https://github.com/andir/infra/blob/9e8d75d39c6a6d55e68c0fbeb9e6aef94723a512/config/servers/bertha/router.nix">custom router
module&lt;/a>
that is being tested as part of my development work on the &lt;a href="https://github.com/systemd/systemd/pull/15669">networkd DHCPv6 subnet id PR&lt;/a>.&lt;/p>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>These days there are (as far as I know) helpers that try to move these
semantics over to the traditional wrappers. I&amp;rsquo;ve not seen any of them in
years tho..&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description></item><item><title>IPv6.watch - Sometimes the results are off</title><link>https://andreas.rammhold.de/posts/ipv6-watch-fluctuations/</link><pubDate>Thu, 09 Apr 2020 16:30:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/ipv6-watch-fluctuations/</guid><description>&lt;p>&lt;figure>&lt;img src="./posts/images/ipv6-watch-nose-dive.png"/>
&lt;/figure>
A while ago I was asked to implement &lt;a href="https://github.com/andir/ipv6.watch/issues/72">prometheus
support&lt;/a> for
&lt;a href="https://ipv6.watch">ipv6.watch&lt;/a>. Ever since then I am occasionally looking at
the graphs to see how the world is doing. Two days ago I realized that my
&lt;a href="https://mon.h4ck.space/d/CFdyKR8Zz/ipv6-watch?orgId=1&amp;amp;fullscreen&amp;amp;panelId=3&amp;amp;from=now-30d&amp;amp;to=now">graphs&lt;/a>
were taking nose dives for a few hours at a time.&lt;/p>
&lt;p>Initially I thought it would be some temporary issue with Hetzner (where the VM
is hosted) and their networking. So I briefly looked through the enormous
amounts of (unhepful) logs the service generated, did some manual poking and
after not seeing any apparent errors on my side declared it transient and
closed the case.&lt;/p>
&lt;p>This afternoon I decided to give it another look.&lt;/p>
&lt;p>I fired up my &lt;a href="https://github.com/andir/infra/blob/a99bd38ba7e1d777ee3f240ff8e84a0b3f014051/tools/grafana-devel.nix">local Grafana development
environment&lt;/a>
that spawns a local (ephemeral) clone of the production Grafana instance and
started tinkering with the data.&lt;/p>
&lt;p>My first attempt was to figure out if some specific site (aka a website) is
impacting the overall results. Most of the available visualisations didn&amp;rsquo;t
really look like a good fit for my needs. Looking through the Grafana plugins
page and I eventually found the &lt;a href="https://grafana.com/grafana/plugins/flant-statusmap-panel">statusmap plugin&lt;/a>
and gave it a shot:&lt;/p>
&lt;figure>&lt;img src="./posts/images/ipv6-watch-per-site.png"/>
&lt;/figure>
&lt;p>While that graph looks fancy there isn&amp;rsquo;t much you can take away from it with a
quick glance. It has waaaaay too much information. Next try was to aggregate
the data over the providers that I am querying:&lt;/p>
&lt;figure>&lt;img src="./posts/images/ipv6-watch-per-resolver-provider.png"/>
&lt;/figure>
&lt;p>This time the results look a lot more helpful. So something seems to be up with
Level3&amp;rsquo;s public DNS service. Let&amp;rsquo;s take a closer look at how individual
resolvers are doing:&lt;/p>
&lt;figure>&lt;img src="./posts/images/ipv6-watch-per-resolver.png"/>
&lt;/figure>
&lt;p>Aha! So there is this one resolver &lt;code>4.2.2.2&lt;/code> that is very flaky with it&amp;rsquo;s
sister &lt;code>4.2.2.1&lt;/code> that also isn&amp;rsquo;t doing great at all times.&lt;/p>
&lt;p>I took a brief look at &lt;a href="https://stat.ripe.net/widget/bgplay#w.ignoreReannouncements=true&amp;amp;w.resource=4.2.2.1&amp;amp;w.starttime=1586440799&amp;amp;w.endtime=1586455199&amp;amp;w.rrcs=0%2C1%2C2%2C7%2C12&amp;amp;w.instant=null&amp;amp;w.type=bgp">today&amp;rsquo;s RIPE BGPlay history for
&lt;code>4.0.0.0/9&lt;/code>&lt;/a>
but that looked pretty stable. It probably isn&amp;rsquo;t some global routing issue.&lt;/p>
&lt;p>Since I didn&amp;rsquo;t want to invest more time I setup two RIPE Atlas measurements to
see if those errors are occurring for others (from 100 random places on the
planet) as well:&lt;/p>
&lt;ul>
&lt;li>One of them checks the DNS resolution of &lt;code>youtube.com&lt;/code> by &lt;code>4.2.2.2&lt;/code>: &lt;a href="https://atlas.ripe.net/measurements/24653529/">https://atlas.ripe.net/measurements/24653529/&lt;/a>&lt;/li>
&lt;li>The other one checks of &lt;code>4.2.2.2&lt;/code> can be reached via ICMP echo: &lt;a href="https://atlas.ripe.net/measurements/24653530/">https://atlas.ripe.net/measurements/24653530/&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Next time this issue bugs me I can hopefully just pull up those measurements
and have a look at the results.&lt;/p></description></item><item><title>Introducing broken.sh</title><link>https://andreas.rammhold.de/posts/broken-sh/</link><pubDate>Sat, 04 Apr 2020 12:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/broken-sh/</guid><description>&lt;p>A few years ago, I started working on &lt;a href="https://broken.sh">broken.sh&lt;/a>&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>, a
security issue tracker for &lt;a href="https://github.com/">NixOS&lt;/a> and
&lt;a href="https://github.com/nixos">Nixpkgs&lt;/a>. Ever since I moved most of my private
computing into the Nix ecosystem, I&amp;rsquo;ve noticed a lack of work in the field of
security issues. As I&amp;rsquo;ve been trying to improve the situation (by contributing
to Nixpkgs), it became obvious that we lack proper tooling in the area. I
wasn&amp;rsquo;t just interested in which packages were broken on my machines. I wanted
to tackle all the issues throughout Nixpkgs.&lt;/p>
&lt;p>While broken.sh started out as a tool to just give me a short list of
actionable items, it has somewhat grown. Right now I like to see it as an
attempt at creating something other distributions like
&lt;a href="https://security-tracker.debian.org/tracker/">Debian&lt;/a>, &lt;a href="https://security.archlinux.org/">Arch
Linux&lt;/a>, &lt;a href="https://security.gentoo.org/">Gentoo&lt;/a>
and many more have had for many many years as part of their infrastructure.
NixOS should have something similar. I hope this isn&amp;rsquo;t the final answer to that
question, but a (first?) step in the right direction.&lt;/p>
&lt;figure>&lt;img src="./posts/images/broken-sh-header.png"/>
&lt;/figure>
&lt;p>While people aren&amp;rsquo;t usually that bad on tracking new highly visibile issues and
fixing them, there are still plenty of not-so-famous issues that just never get
any attention. A part of the problem is likely to be their discoverability, or
the lack of it. The only issue tracking done for NixOS is one big lump of
issues in the GitHub issue tracker. However, GitHub issues cannot really be
categorized into packages, releases, maintainers, commits, etc. I figured out
early on that creating an overview outside of GitHub would be the best way
forward.&lt;/p>
&lt;p>In the very early days, broken.sh was just a static HTML page providing an
overview of one specific revision, listing all the issues that I managed to
identify. That was okay as
a one-shot report but did not fit my long-term vision.&lt;/p>
&lt;p>Below you can see the output of the very first working version. Back then the
project was still called an &amp;ldquo;update tracker&amp;rdquo;.&lt;/p>
&lt;figure>&lt;img src="./posts/images/broken-sh-early-version.png"/>
&lt;/figure>
&lt;p>I also tried to provide information on which upstream versions are
available for a given package - hoping to eventually tackle that issue, too
(hah!). I eventually decided to focus on the security issues.&lt;/p>
&lt;h2 id="how-does-it-work">How does it work?&lt;/h2>
&lt;p>In a nutshell, I am looking at every revision of a NixOS channel and extract
all the package versions and patches. For each of these, I try to figure out if
a recent snapshot of the &lt;a href="https://nvd.nist.gov/vuln/data-feeds">NVD database&lt;/a>
contains relevant information for them. If there is a positive match, it will
be shown on the website.&lt;/p>
&lt;p>My current approach consists of multiple steps. Each step is available as a
subcommand of the &lt;code>nix-vuln-scanner&lt;/code> binary:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">$ ./result/bin/nix-vuln-scanner
USAGE:
nix-vuln-scanner [FLAGS] &amp;lt;SUBCOMMAND&amp;gt;
FLAGS:
-d, --debug enable debug logging
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
channel-report Generates reports for all &amp;#34;bumps&amp;#34; in a channel
help Prints this message or the help of the given subcommand(s)
notes List, create and edit notes
process-db Process the output of the report command and generate an sqlite database from it
report Generate a new issue report file
serve serves the contents of the DB via HTTP.
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You do not always have to run all parts in order to obtain and use the results.
For example, if all you need is a brief ad-hoc information, you shouldn&amp;rsquo;t have
to run a web server or even deal with a (sqlite) database (file). Instead you
can just run the &lt;code>report&lt;/code> subcommand like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ nix-vuln-scanner report -p packages.json -s nixos-20.03 -o my-report.json
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:53:42&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::git::clone&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Locking &lt;span style="color:#e6db74">&amp;#34;nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/clone.lock&amp;#34;&lt;/span>
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:53:42&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::git::clone&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Initial cloning of git://github.com/nixos/nixpkgs to &lt;span style="color:#e6db74">&amp;#34;nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/clone&amp;#34;&lt;/span>
Cloning into bare repository &lt;span style="color:#e6db74">&amp;#39;nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/clone&amp;#39;&lt;/span>...
remote: Enumerating objects: 129, &lt;span style="color:#66d9ef">done&lt;/span>.
remote: Counting objects: 100% &lt;span style="color:#f92672">(&lt;/span>129/129&lt;span style="color:#f92672">)&lt;/span>, &lt;span style="color:#66d9ef">done&lt;/span>.
remote: Compressing objects: 100% &lt;span style="color:#f92672">(&lt;/span>109/109&lt;span style="color:#f92672">)&lt;/span>, &lt;span style="color:#66d9ef">done&lt;/span>.
remote: Total &lt;span style="color:#ae81ff">1871146&lt;/span> &lt;span style="color:#f92672">(&lt;/span>delta 5&lt;span style="color:#f92672">)&lt;/span>, reused &lt;span style="color:#ae81ff">42&lt;/span> &lt;span style="color:#f92672">(&lt;/span>delta 2&lt;span style="color:#f92672">)&lt;/span>, pack-reused &lt;span style="color:#ae81ff">1871017&lt;/span>
Receiving objects: 100% &lt;span style="color:#f92672">(&lt;/span>1871146/1871146&lt;span style="color:#f92672">)&lt;/span>, 1.07 GiB | 40.51 MiB/s, &lt;span style="color:#66d9ef">done&lt;/span>.
Resolving deltas: 100% &lt;span style="color:#f92672">(&lt;/span>1289234/1289234&lt;span style="color:#f92672">)&lt;/span>, &lt;span style="color:#66d9ef">done&lt;/span>.
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:54:52&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::git::clone&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Locking &lt;span style="color:#e6db74">&amp;#34;nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/clone.lock&amp;#34;&lt;/span>
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:54:52&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::git::clone&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Fetching from origin in &lt;span style="color:#e6db74">&amp;#34;nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/clone&amp;#34;&lt;/span>
From git://github.com/nixos/nixpkgs
* branch HEAD -&amp;gt; FETCH_HEAD
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:54:53&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::git::clone&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Locking &lt;span style="color:#e6db74">&amp;#34;nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/nix-vuln-scanner/nixos-20.03.lock&amp;#34;&lt;/span>
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:54:53&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::git::clone&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Initial cloning of git://github.com/nixos/nixpkgs to &lt;span style="color:#e6db74">&amp;#34;nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/nix-vuln-scanner/nixos-20.03&amp;#34;&lt;/span>
Cloning into &lt;span style="color:#e6db74">&amp;#39;nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/nix-vuln-scanner/nixos-20.03&amp;#39;&lt;/span>...
Updating files: 100% &lt;span style="color:#f92672">(&lt;/span>21392/21392&lt;span style="color:#f92672">)&lt;/span>, &lt;span style="color:#66d9ef">done&lt;/span>.
…
Switched to a new branch &lt;span style="color:#e6db74">&amp;#39;nixos-20.03&amp;#39;&lt;/span>
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:54:58&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Using cached data only. If no local data is available it will still be fetched.
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:54:58&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Fetching the local nixos packages
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:54:58&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::fs_cache&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> File packages.json not found: Os &lt;span style="color:#f92672">{&lt;/span> code: 2, kind: NotFound, message: &lt;span style="color:#e6db74">&amp;#34;No such file or directory&amp;#34;&lt;/span> &lt;span style="color:#f92672">}&lt;/span>
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:06&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report::nix&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Removing all occurences of /home/andi/dev/…/nixpkgs/repo/68b00da0eb30d2acc9c57208a4f921c1/nix-vuln-scanner/nixos-20.03 in meta.position
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:06&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Fetching NVD CVE database
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:06&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::fs_cache&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Read nvd-2002.json.gz from local cache.
…
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:06&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::fs_cache&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Read nvd-2019.json.gz from local cache.
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:10&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::fs_cache&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> File nvd-2020.json.gz not found: Os &lt;span style="color:#f92672">{&lt;/span> code: 2, kind: NotFound, message: &lt;span style="color:#e6db74">&amp;#34;No such file or directory&amp;#34;&lt;/span> &lt;span style="color:#f92672">}&lt;/span>
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:10&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::fs_cache&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Filling local cache with https://nvd.nist.gov/feeds/json/cve/1.0/nvdcve-1.0-2020.json.gz.
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:11&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Filtering package list...
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:17&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report::nix_patches&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> failed to execute nix eval &lt;span style="color:#66d9ef">for&lt;/span> ruby_2_5: error: value is a string with context &lt;span style="color:#66d9ef">while&lt;/span> a set was expected, at &lt;span style="color:#f92672">(&lt;/span>string&lt;span style="color:#f92672">)&lt;/span>:11:89
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:17&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report::nix_patches&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> failed to execute nix eval &lt;span style="color:#66d9ef">for&lt;/span> ruby: error: value is a string with context &lt;span style="color:#66d9ef">while&lt;/span> a set was expected, at &lt;span style="color:#f92672">(&lt;/span>string&lt;span style="color:#f92672">)&lt;/span>:11:89
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:18&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report::nix_patches&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> failed to execute nix eval &lt;span style="color:#66d9ef">for&lt;/span> ruby_2_7: error: value is a string with context &lt;span style="color:#66d9ef">while&lt;/span> a set was expected, at &lt;span style="color:#f92672">(&lt;/span>string&lt;span style="color:#f92672">)&lt;/span>:11:89
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:18&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report::nix_patches&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> failed to execute nix eval &lt;span style="color:#66d9ef">for&lt;/span> plasma-desktop: error: value is a string with context &lt;span style="color:#66d9ef">while&lt;/span> a set was expected, at &lt;span style="color:#f92672">(&lt;/span>string&lt;span style="color:#f92672">)&lt;/span>:11:89
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:18&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report::nix_patches&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> failed to execute nix eval &lt;span style="color:#66d9ef">for&lt;/span> e2fsprogs: error: value is null &lt;span style="color:#66d9ef">while&lt;/span> a set was expected, at &lt;span style="color:#f92672">(&lt;/span>string&lt;span style="color:#f92672">)&lt;/span>:11:89
&lt;span style="color:#f92672">[&lt;/span>2020-04-04&lt;span style="color:#f92672">][&lt;/span>21:55:20&lt;span style="color:#f92672">][&lt;/span>nix_vuln_scanner::report&lt;span style="color:#f92672">][&lt;/span>INFO&lt;span style="color:#f92672">]&lt;/span> Found &lt;span style="color:#ae81ff">1667&lt;/span> interesting packages.
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above command produces a &lt;code>my-report.json&lt;/code> file containing information about
the analyzed revision and the potential issues that have been found:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-json" data-lang="json">{
&lt;span style="color:#f92672">&amp;#34;version&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;1&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;repo&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;git://github.com/nixos/nixpkgs&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;revision&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;nixos-20.03&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;channel_name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;local&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;date_time&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2020-04-04 19:55:23.055948016 UTC&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;commit_time&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">null&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;advance_time&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">null&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;packages&amp;#34;&lt;/span>: {
&lt;span style="color:#960050;background-color:#1e0010">…&lt;/span>
&lt;span style="color:#f92672">&amp;#34;cairo&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;attribute_name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;cairo&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;package_name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;cairo&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;version&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;1.16.0&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;cves&amp;#34;&lt;/span>: [
{
&lt;span style="color:#f92672">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;CVE-2018-19876&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;patched&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
},
{
&lt;span style="color:#f92672">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;CVE-2019-6461&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;patched&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">false&lt;/span>
},
{
&lt;span style="color:#f92672">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;CVE-2019-6462&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;patched&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">false&lt;/span>
}
],
&lt;span style="color:#f92672">&amp;#34;patches&amp;#34;&lt;/span>: [
&lt;span style="color:#e6db74">&amp;#34;CVE-2018-19876.patch&amp;#34;&lt;/span>
]
},
&lt;span style="color:#960050;background-color:#1e0010">…&lt;/span>
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once you generated the JSON file, you can run all sorts of queries on it. For
example, you can list all the open (unpatched) CVEs:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">$ jq -r &amp;#39;.packages[]|select(.cves[].patched == false)|[.attribute_name, (.cves|map(.name)|join(&amp;#34;,&amp;#34;))] | @tsv&amp;#39; &amp;lt; my-report.json | uniq | column -t | head -n5
libmad CVE-2018-7263
gnupg1orig CVE-2018-12020
ansible_2_6 CVE-2020-10684,CVE-2020-1733,CVE-2020-1735,CVE-2020-1736,CVE-2020-1738,CVE-2020-1739,CVE-2020-1740
ncompress CVE-2001-1413
lepton CVE-2017-7448,CVE-2017-8891,CVE-2018-12108
&lt;/code>&lt;/pre>&lt;/div>&lt;p>(If you are a &lt;code>jq&lt;/code> wizard and have a better way to obtain the results above,
please let me know :))&lt;/p>
&lt;p>To generate the data displayed on broken.sh, the above procedure is run for
every channel revision (meaning all those commits have passed hydra and arrived
on the channel). Instead of analyzing each of them manually there is a handy
subcommand that downloads the list of revisions from Graham Christensen&amp;rsquo;s
archive over at &lt;a href="https://channels.nix.gsc.io">https://channels.nix.gsc.io&lt;/a>.&lt;/p>
&lt;p>Once all the revisions have been analyzed, they are imported into the sqlite
database through the &lt;code>process-db&lt;/code> subcommand. As of now, the database only
serves the purpose of providing a data backend for the web interface.&lt;/p>
&lt;p>At the time of writing I am updating all revisions of NixOS 18.03, NixOS 18.09,
NixOS 19.03, NixOS 19.09, NixOS 20.03 and NixOS unstable at least once every
day. (More frequently isn&amp;rsquo;t really possible without optimizing the code
further. It already runs for almost a full day.). That means even if we gain
knowledge of new CVEs today we will be able tell since when we have been
exposed to the vulnerabilities.&lt;/p>
&lt;h1 id="what-you-can-do-right-now">What you can do right now&lt;/h1>
&lt;p>&lt;a href="https://broken.sh">broken.sh&lt;/a> might not yet be very user-friendly and also not
very mature (in many regards), but it can be used for a bunch of useful
information:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Overview across all the channels on the index&lt;/strong>:
&lt;figure>&lt;img src="./posts/images/broken-sh-index.png"/>
&lt;/figure>
&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">$ http --json https://broken.sh
{ &amp;#34;statistics&amp;#34;: [
[
&amp;#34;nixos-unstable&amp;#34;,
{
&amp;#34;unpatched&amp;#34;: 941,
&amp;#34;patched&amp;#34;: 118
}
],
[
&amp;#34;nixos-20.03&amp;#34;,
{
&amp;#34;unpatched&amp;#34;: 1072,
&amp;#34;patched&amp;#34;: 120
}
],
[
&amp;#34;nixos-19.09&amp;#34;,
{
&amp;#34;unpatched&amp;#34;: 1340,
&amp;#34;patched&amp;#34;: 183
}
],
[
&amp;#34;nixos-19.03&amp;#34;,
{
&amp;#34;unpatched&amp;#34;: 2304,
&amp;#34;patched&amp;#34;: 195
}
],
[
&amp;#34;nixos-18.09&amp;#34;,
{
&amp;#34;unpatched&amp;#34;: 5311,
&amp;#34;patched&amp;#34;: 120
}
],
[
&amp;#34;nixos-18.03&amp;#34;,
{
&amp;#34;unpatched&amp;#34;: 5482,
&amp;#34;patched&amp;#34;: 107
}
]
]
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>List of open issues in a channel in a revision&lt;/strong>: &lt;figure>&lt;img src="./posts/images/broken-sh-revision.png"/>
&lt;/figure>
&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">$ http --json https://broken.sh/channels/nixos-unstable
{
&amp;#34;channel&amp;#34;: {
&amp;#34;id&amp;#34;: 1,
&amp;#34;name&amp;#34;: &amp;#34;nixos-unstable&amp;#34;
},
&amp;#34;commit&amp;#34;: {
&amp;#34;id&amp;#34;: 841,
&amp;#34;revision&amp;#34;: &amp;#34;57f2ea5ca13ee68bdf9f2e9965ee251301b7b8e8&amp;#34;,
&amp;#34;commit_time&amp;#34;: &amp;#34;2020-02-27 10:34:00 UTC&amp;#34;
},
&amp;#34;packages_with_issues&amp;#34;: [
{
&amp;#34;name&amp;#34;: &amp;#34;a2ps&amp;#34;,
&amp;#34;attribute_name&amp;#34;: &amp;#34;a2ps&amp;#34;,
&amp;#34;versions&amp;#34;: [
{
&amp;#34;version&amp;#34;: &amp;#34;4.14&amp;#34;,
&amp;#34;issues&amp;#34;: [
{
&amp;#34;id&amp;#34;: 429,
&amp;#34;identifier&amp;#34;: &amp;#34;CVE-2001-1593&amp;#34;
},
{
&amp;#34;id&amp;#34;: 430,
&amp;#34;identifier&amp;#34;: &amp;#34;CVE-2014-0466&amp;#34;
},
{
&amp;#34;id&amp;#34;: 431,
&amp;#34;identifier&amp;#34;: &amp;#34;CVE-2015-8107&amp;#34;
}
],
&amp;#34;patches&amp;#34;: [
{
&amp;#34;id&amp;#34;: 176,
&amp;#34;name&amp;#34;: &amp;#34;09_CVE-2001-1593.diff&amp;#34;
},
{
&amp;#34;id&amp;#34;: 177,
&amp;#34;name&amp;#34;: &amp;#34;CVE-2014-0466.diff&amp;#34;
}
]
…
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>History of a CVE and which channel revisions are affected by it&lt;/strong>: &lt;figure>&lt;img src="./posts/images/broken-sh-history.png"/>
&lt;/figure>
&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">$ http --json https://broken.sh/issues/CVE-2001-1593
{
&amp;#34;issue&amp;#34;: { &amp;#34;id&amp;#34;: 429, &amp;#34;identifier&amp;#34;: &amp;#34;CVE-2001-1593&amp;#34; },
&amp;#34;packages&amp;#34;: [
{
&amp;#34;package&amp;#34;: { &amp;#34;id&amp;#34;: 286, &amp;#34;name&amp;#34;: &amp;#34;a2ps&amp;#34;, &amp;#34;attribute_name&amp;#34;: &amp;#34;a2ps&amp;#34; },
&amp;#34;channels&amp;#34;: [
{
&amp;#34;channel&amp;#34;: { &amp;#34;id&amp;#34;: 2, &amp;#34;name&amp;#34;: &amp;#34;nixos-18.03&amp;#34; },
&amp;#34;bumps_with_versions&amp;#34;: [
{
&amp;#34;channel_bump&amp;#34;: { &amp;#34;id&amp;#34;: 146, &amp;#34;channel_id&amp;#34;: 2, &amp;#34;commit_id&amp;#34;: 146, &amp;#34;channel_bump_date&amp;#34;: &amp;#34;2019-02-20 10:25:14 UTC&amp;#34; },
&amp;#34;commit&amp;#34;: { &amp;#34;id&amp;#34;: 146, &amp;#34;revision&amp;#34;: &amp;#34;cb0e20d6db96fe09a501076c7a2c265359982814&amp;#34;, &amp;#34;commit_time&amp;#34;: &amp;#34;2018-08-11 14:42:43 UTC&amp;#34;
},
&amp;#34;versions&amp;#34;: [
{
&amp;#34;version&amp;#34;: &amp;#34;4.14&amp;#34;,
&amp;#34;patches&amp;#34;: [
{
&amp;#34;id&amp;#34;: 176,
&amp;#34;name&amp;#34;: &amp;#34;09_CVE-2001-1593.diff&amp;#34;
},
{
&amp;#34;id&amp;#34;: 177,
&amp;#34;name&amp;#34;: &amp;#34;CVE-2014-0466.diff&amp;#34;
},
{
&amp;#34;id&amp;#34;: 178,
&amp;#34;name&amp;#34;: &amp;#34;fix-format-security.diff&amp;#34;
}
]
}
]
},
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>**Diff of two revisions to see what issues have been resolved, remained the&lt;/p>
&lt;/li>
&lt;li>
&lt;p>same or been fixed**:
Currently there is no UI to select which revisions to compare. The format of
the URLs should be self-explanatory:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">$ http --json https://broken.sh/diff/revisions/27a5ddcf747fb2bb81ea9c63f63f2eb3eec7a2ec/4cd2cb43fb3a87f48c1e10bb65aee99d8f24cb9d
{
&amp;#34;old&amp;#34;: { &amp;#34;id&amp;#34;: 655, &amp;#34;revision&amp;#34;: &amp;#34;27a5ddcf747fb2bb81ea9c63f63f2eb3eec7a2ec&amp;#34;, &amp;#34;commit_time&amp;#34;: &amp;#34;2019-10-14 19:23:11 UTC&amp;#34; },
&amp;#34;new&amp;#34;: { &amp;#34;id&amp;#34;: 678, &amp;#34;revision&amp;#34;: &amp;#34;4cd2cb43fb3a87f48c1e10bb65aee99d8f24cb9d&amp;#34;, &amp;#34;commit_time&amp;#34;: &amp;#34;2019-10-23 18:19:15 UTC&amp;#34; },
&amp;#34;issues&amp;#34;: {
&amp;#34;common&amp;#34;: [
{
&amp;#34;attribute_name&amp;#34;: &amp;#34;a2ps&amp;#34;,
&amp;#34;version&amp;#34;: &amp;#34;4.14&amp;#34;,
&amp;#34;issue&amp;#34;: &amp;#34;CVE-2015-8107&amp;#34;
},
…
],
&amp;#34;new&amp;#34;: [
{
&amp;#34;attribute_name&amp;#34;: &amp;#34;clamav&amp;#34;,
&amp;#34;version&amp;#34;: &amp;#34;0.102.0&amp;#34;,
&amp;#34;issue&amp;#34;: &amp;#34;CVE-2020-3123&amp;#34;
},
…
],
…
&lt;/code>&lt;/pre>&lt;/div>&lt;figure>&lt;img src="./posts/images/broken-sh-diff.png"/>
&lt;/figure>
&lt;/li>
&lt;/ul>
&lt;h1 id="future-work">Future work&lt;/h1>
&lt;!-- raw HTML omitted -->
&lt;!-- raw HTML omitted -->
&lt;ul>
&lt;li>
&lt;p>Integration with ofBorg: I have been thinking about integrating this with
&lt;a href="https://github.com/nixos/ofBorg">ofBorg&lt;/a>. The idea is that reviewers,
submitters and commiters will make better informed decisions if they know
what the potential security impact of a proposed change is.&lt;/p>
&lt;p>I imagine having a PR flagged as security critical could draw more attention
to it. This is especially relevant when individual contributors try to fix
security-relevant changes but they do not have any means to draw attention to
it. They basically disappear in the flood of PRs that are hitting Nixpkgs
constantly.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Paper trail: We frequently get questions about our patching policies, why
something wasn&amp;rsquo;t fixed yet, etc. Having some kind of paper trail where we
(try to) document findings would help users, developers and any interested
party figure out why a specific issue wasn&amp;rsquo;t fixed on NixOS.&lt;/p>
&lt;p>For instance, there are often issues in a sort of undefined state. That might
be due to projects being dead, nobody having written a fix, the scope of
exploitation being too narrow or any other reason and a combination of these.
We should be able to document findings (and solutions, comments, …) about an
issue in a way that allows for automated analysis.&lt;/p>
&lt;p>More than once have I started working through the entire list of open issues.
Sometimes I am running into the very same issues I saw six months ago, but
totally forgot about. I tried adding a way to take notes on open and
resolved issues with the &lt;code>notes&lt;/code> subcommand but haven&amp;rsquo;t made any (or much)
progress.&lt;/p>
&lt;p>The tricky part is to get a proper structure that is editable manually and
that can be versioned with something like Git. I have my opinions on the
topic and over the years there have been several discussions in
&lt;a href="ircs://irc.freenode.org/#nixos-security">#nixos-security&lt;/a>. One of the
(other) long term goals in this regard is being able to produce NixOS
Security Advisories (again). But that is probably material for a dedicated
blog entry…&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="to-sum-it-up">To sum it up&amp;hellip;&lt;/h1>
&lt;p>Have I managed everything I was initially targeting? No, but most of it.&lt;/p>
&lt;p>broken.sh is already quite usable and provides value as is. In the worst case
it just provides me with a list of issues that I should go investigate when
bored.&lt;/p>
&lt;p>For the most part of a year I haven&amp;rsquo;t really changed much of the code. It just
runs unattended without causing me any extra work. That is really nice and I am
actually proud of it. When all of this started I neither really understood Nix
nor Rust. More senior Rust developers are probably disgusted by some of the
code that I produced but that is fine by me. It was my first &amp;ldquo;real&amp;rdquo; rust
project and I would probably do things differently these days.&lt;/p>
&lt;p>There is plenty of room for improvement. I just reached a point where fighting
this alone doesn&amp;rsquo;t feel fruitful anymore. While working on broken.sh I went
from many Nixpkgs PRs a day to just a few a week. Being exposed to the sheer
volume of Nixpkgs' issues is tiresome. I often end up diving into some rabbit
hole and lose track of time. By the time I am done fixing it, it&amp;rsquo;s well past
bed time and my evening is gone.&lt;/p>
&lt;p>I occasionally receive feedback that encourages me to continue the work. That
is what made me consider writing this blog post. Conversations are lossy and
aren&amp;rsquo;t easily replicated, so here is an overview of my project, in the hope
that it is, too, useful to you.&lt;/p>
&lt;p>If you have a neat idea or some matter that you want to discuss,
please do not hesitate to contact me (mail, IRC).&lt;/p>
&lt;hr>
&lt;p>Many thanks to Ana, Felix and Florian for reviewing and proof-reading this
post. 💗&lt;/p>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>The name &lt;code>broken.sh&lt;/code> is just a stupid pun on how broken everything is.
Not really a statement about how broken NixOS is. Just a nice domain that I
did come up with at the time. :)&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description></item><item><title>SaltStack, Ansible, Puppet, … how can we share data between servers?</title><link>https://andreas.rammhold.de/posts/saltstack-ansible-puppet-how-can-we-share-data/</link><pubDate>Sat, 22 Apr 2017 16:50:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/saltstack-ansible-puppet-how-can-we-share-data/</guid><description>&lt;p>WARNING: This is still a draft somewhat..&lt;/p>
&lt;p>Lately I&amp;rsquo;ve been depressed by the lack of automation in my life or really in
the projects I take part in.&lt;/p>
&lt;p>Most of my stuff is not hosted on AWS, Azure, Digitalocean or other big &amp;ldquo;cloud&amp;rdquo;
players. That is due to multiple reasons:&lt;/p>
&lt;ul>
&lt;li>We may have hardware, rack, power and network capacity available which is
basically for free.&lt;/li>
&lt;li>We might need physical connectivity to the machine - e.g. connecting to a
local WiFi mesh network, a beamer, some audio equipment and so on…&lt;/li>
&lt;li>We want to remain in control of our data. That means we are not going to
trust some random internet business.&lt;/li>
&lt;li>It is cheaper, even if you&amp;rsquo;ve to pay for the servers that are running 24/7.
This might not be true for a website that sells boat trips on the Atlantic
Ocean and gets high traffic spikes every 2nd Sunday from April to December
but for our scenarios it works quite well.&lt;/li>
&lt;/ul>
&lt;p>At the office I&amp;rsquo;ve been using puppet more then six years and I&amp;rsquo;m quite happy
with it. In the local Freifunk-Community (&lt;a href="https://darmstadt.freifunk.net">https://darmstadt.freifunk.net&lt;/a>) we
are using SaltStack. For my personal infrastructure (E-Mail, Webhosting, random
docker containers, pastebin, dn42, …) I&amp;rsquo;m using Ansible (&lt;a href="https://ansible.com">https://ansible.com&lt;/a>).&lt;/p>
&lt;p>Neither of those deployments are very trivial, they are probably just the
average project for either of those tools.&lt;/p>
&lt;p>A few weeks back I and a few others started writing SaltStack states for an
IRC-Network that we are running. If you think about automating the deployment
of IRCd&amp;rsquo;s it is rather easier. Create a VM, install your favorite flavor of
linux, install build dependencies for the IRCd of your choice and
generate the configuration. That&amp;rsquo;s basically it.&lt;/p>
&lt;p>We&amp;rsquo;ve been using TLS in our network for about 10 years now. (Both user and
server2server communication). Since it wasn&amp;rsquo;t and still is not desirable to buy
wildcard certificates or share certificates between servers we are running our
own CA (including OCSP service etc.). With the switch to automated server
configuration we also want to deploy LetsEncrypt - actually LetsEncrypt
encouraged us to start using some kind of automation since humans aren&amp;rsquo;t
reliable and having to jupe a server every 3 months because the admin still
didn&amp;rsquo;t configure a LetsEncrypt cronjob is not what we are after.&lt;/p>
&lt;p>So I started out writing some states and a Vagrantfile
(&lt;a href="https://gist.github.com/andir/2188da38904557a58cd7df29fd277275">https://gist.github.com/andir/2188da38904557a58cd7df29fd277275&lt;/a>) for easier
testing of the whole stack. After tackling a few issues with Vagrant and
different &lt;code>boxes&lt;/code> (as vagrant calls VM images) everything looked fine.&lt;/p>
&lt;p>We basically have a zoo of VMs on our laptops wich simulate the production
network. At least so we thought.&lt;/p>
&lt;p>For the links between the IRCDs we are using TLS. So far we did exchange
fingerprints, manually add them to the &lt;code>ircd.conf&lt;/code>, &lt;code>/quote rehash&lt;/code> and off you
go.&lt;/p>
&lt;p>When you automate the deployment you also automate the configuration of links
between your servers. While you can configure &lt;code>send_password&lt;/code> and
&lt;code>receive_password&lt;/code> since what I do know when that isn&amp;rsquo;t what we want to rely on
when sending chat messages around the globe. So we still need to exchange
certificate fingerprints. One might say that &lt;code>TLSA&lt;/code> records are made for that
but I wonder why I can&amp;rsquo;t use the already verified connection between my Salt
Master and the Salt Minion. If you query DNS for TLSA records it requires your
DNS to be up, DNSSEC to work, your local resolver to have a copy of the root
dns zone or an upstream resolver that (hopefully) verifies DNSSEC - unlike
OpenDNS.&lt;/p>
&lt;p>Since we are also running our own DNS Servers adding them as a dependency isn&amp;rsquo;t
really a great idea. We might be recovering from the death of some parts of the
infrastructure. The only reasonable approach to me is to use the authenticated
relationship from minion and master to exchange public keys, fingerprints etc.&lt;/p>
&lt;p>While I&amp;rsquo;m currently focusing on exchanging certificate fingerprints and/or
public keys this applies to a bunch of scenarios you might come acress when you
decide to drop all shared secrets and generate them on your servers. A few of
those could be:&lt;/p>
&lt;ul>
&lt;li>Generating and exchanging TSIG keys for nsupdate between your DNS Servers
and minions.&lt;/li>
&lt;li>Communicating a shared secret for database access, borg backups, …&lt;/li>
&lt;li>Distributing SSHFPs between your servers (local verification, publishing
them to DNS as SSHFP records,…)&lt;/li>
&lt;li>Trust bootstrapping (after minion key verification) for any kind of internal
secret database&lt;/li>
&lt;/ul>
&lt;p>With SaltStack you&amp;rsquo;ve a few options to transfer data from a minion to the master / other minions:&lt;/p>
&lt;ul>
&lt;li>use the Mine to export data&lt;/li>
&lt;li>use grains&lt;/li>
&lt;li>use pillars (with pre-generated certificates, keys, …) -&amp;gt; not the desired solution&lt;/li>
&lt;li>trigger events with custom context&lt;/li>
&lt;li>use a vault&lt;/li>
&lt;/ul>
&lt;p>In puppet you could use exported ressources, in Ansible you could write to some
kind of custom api without much overhead or query/configure the other side
on-demand since a single run isn&amp;rsquo;t restricted to a single server.&lt;/p>
&lt;p>Puppet has solved the issue for many years - people tend to not like puppet for
various reasons. Maybe because it feels like programming?&lt;/p>
&lt;h1 id="using-the-salt-mine">Using the Salt Mine&lt;/h1>
&lt;p>The Salt Mine is a handy construct when it comes to exporting data from a node
as long as that data only exists as some kind of list or with a very low
cardinality. Also it only makes sense if the data is mostly static and doesn&amp;rsquo;t
change. Changes to the exported data (e.g. another information, removal of
information, …) require changes to the minion configuration. This isn&amp;rsquo;t really
practical if you are trying to write modular states where not everything is
hard-wired with another state.&lt;/p>
&lt;p>For reference this is how you would export information about a certificate:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">mine_functions&lt;/span>:
&lt;span style="color:#f92672">my_awesome_certificate&lt;/span>:
- &lt;span style="color:#f92672">mine_function&lt;/span>: &lt;span style="color:#ae81ff">tls.cert_info&lt;/span>
- &lt;span style="color:#ae81ff">/etc/letsencrypt/live/.../cert.pem&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This has to be deployed on each minion and you probably also have to restart the minion when changeing the file.&lt;/p>
&lt;p>More information on the salt mine can be found at &lt;a href="https://docs.saltstack.com/en/latest/topics/mine/">https://docs.saltstack.com/en/latest/topics/mine/&lt;/a>.&lt;/p>
&lt;h1 id="trying-to-export-data-via-grains">Trying to export data via Grains&lt;/h1>
&lt;p>I&amp;rsquo;m not going to get into the details or provide a example configuration here.
I&amp;rsquo;ve written a bunch of grains and they just work. The limitation is that you
can&amp;rsquo;t really attribute them. So you can&amp;rsquo;t tell your custom grains what to
export without changing the code or introducing custom configuration files,
adding stuff to the minion config etc.. While this approach would automatically
propagate new certificate fingerprints, public keys, … you still have to use
the Salt Mine to actually export them. Which isn&amp;rsquo;t that bad.&lt;/p>
&lt;p>The lack of configuration options for custom grains kills this approach for me.&lt;/p>
&lt;h1 id="pillars">Pillars&lt;/h1>
&lt;p>LOLNOPE. Using pillars would require manual collection of fingerprints or (even
worse) central management of all the certificates.&lt;/p>
&lt;p>This simply doesn&amp;rsquo;t work for me.&lt;/p>
&lt;h1 id="using-a-custom-event">Using a custom event&lt;/h1>
&lt;p>This is what seems to be a promising but ephemeral approach to the issue.&lt;/p>
&lt;p>The basic idea is:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Create the public-/private key pair on the minion.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>On changes to that fail (creation, key rollover, new certificate, …) execute
a script with the filename as argument&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The (bash) script then extracts the information we want from the file.
(using &lt;code>openssl&lt;/code> or other command line tools) and publishes those
information using something like&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">salt-call event.send my/custom/event/certificate-changed &lt;span style="color:#e6db74">&amp;#39;{&amp;#34;certfp&amp;#34;: &amp;#34;ABCDEF01234567890…&amp;#34;}&amp;#39;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>In order to use the &amp;ldquo;published&amp;rdquo; fingerprint other minions must be up, running
and listening to those events. Otherwise the information is lost and nobody
cares.&lt;/p>
&lt;p>This approach only works if we figure out how to store the fingerprints - after
receiving an event.&lt;/p>
&lt;p>It basically sucks as bad as the others but we might be able to configure the
links after randomly restarting salt-minions and running &lt;code>salt '*' state.apply&lt;/code> …&lt;/p>
&lt;h1 id="using-a-vault">Using a vault&lt;/h1>
&lt;p>At the time of this writing this seems to be a valid option. I&amp;rsquo;ve not tried it
yet. There will be an update on this soon (tm).&lt;/p>
&lt;p>Reading of the vault seems to be rather easy. You can also write to it but only
using infromation that is availbe during state-rendering. So I&amp;rsquo;m not sure what
the benefit is. One could probably combine this with the event approach to
store the public keys of the minions in the vault by listening to key events on
the master.&lt;/p>
&lt;h1 id="conclusion">Conclusion&lt;/h1>
&lt;p>This world sucks. We need better tools :/&lt;/p></description></item><item><title>Juniper MCD decided to coredump on commit and rollback</title><link>https://andreas.rammhold.de/posts/juniper-config-recovery-mcd-coredump/</link><pubDate>Tue, 08 Nov 2016 15:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/juniper-config-recovery-mcd-coredump/</guid><description>&lt;p>On a Juniper EX3300 a colleague of mine entered an invalid statement:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> interface-range some_ports &lt;span style="color:#f92672">{&lt;/span>
member ge0/0/2;
member-range ge-0/0/0 to ge-0/0/1;
&lt;span style="color:#f92672">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>member ge0/0/2&lt;/code> is missing a &lt;code>-&lt;/code> between &lt;code>ge&lt;/code> and &lt;code>0/0/2&lt;/code>. Juniper (for whatever reason) accepted the input but &lt;code>mcd&lt;/code> decided to segfault when asked to delete or rollback the configuration.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback"> pid 75982 (mgd), uid 0: exited on signal 11 (core dumped)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Recovering from that case is actually not that hard. You just know the right command(s) ;-)&lt;/p>
&lt;p>You can load an older configuration via the &lt;code>load override &amp;lt;config file&amp;gt;&lt;/code> command. I did also try the &lt;code>load replace &amp;lt;config file&amp;gt;&lt;/code> command but that also segfaulted..&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">
andi@foo# load override ?
Possible completions:
&amp;lt;filename&amp;gt; Filename &lt;span style="color:#f92672">(&lt;/span>URL, local, remote, or floppy&lt;span style="color:#f92672">)&lt;/span>
db/ Last changed: Oct &lt;span style="color:#ae81ff">27&lt;/span> 10:44:35
juniper.conf+.gz Size: 6000, Last changed: Nov &lt;span style="color:#ae81ff">08&lt;/span> 15:12:55
juniper.conf.1.gz Size: 5913, Last changed: Jun &lt;span style="color:#ae81ff">30&lt;/span> 13:36:52
juniper.conf.2.gz Size: 5881, Last changed: Jun &lt;span style="color:#ae81ff">30&lt;/span> 12:59:25
juniper.conf.3.gz Size: 5280, Last changed: Jun &lt;span style="color:#ae81ff">30&lt;/span> 12:54:02
&lt;span style="color:#f92672">[&lt;/span>...&lt;span style="color:#f92672">]&lt;/span>
&lt;span style="color:#f92672">{&lt;/span>master:0&lt;span style="color:#f92672">}[&lt;/span>edit&lt;span style="color:#f92672">]&lt;/span>
andi@foo# load override juniper.conf+.gz
load complete
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In my case the &lt;code>juniper.conf+.gz&lt;/code> was the desired config file. I recommend inspecting those files before loading them (they are stored in &lt;code>/config/&lt;/code>).&lt;/p></description></item><item><title>Using VRFs with linux and systemd-networkd</title><link>https://andreas.rammhold.de/posts/linux-ip-vrf-systemd-networkd/</link><pubDate>Sun, 09 Oct 2016 13:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/linux-ip-vrf-systemd-networkd/</guid><description>&lt;p>While working on a systemd-networkd patch to implement (at least basic) VRF
interfaces I did write :doc:&lt;code>my other post &amp;lt;linux-ip-vrf&amp;gt;&lt;/code>. This post should
give you a brief example on how you can create a VRF with systemd-networkd.&lt;/p>
&lt;p>At this point it really only created the interfaces and enslaves potential
customer interfaces to a given VRF.&lt;/p>
&lt;p>You still have to implement all the &lt;code>ip rule&lt;/code>-stuff yourself. For example a
&lt;code>systemd.unit&lt;/code> file might be the right approach which is executed/started after
the network is &amp;ldquo;up&amp;rdquo;.&lt;/p>
&lt;p>First you&amp;rsquo;ve to create the systemd.netdev &lt;code>vrf-customer1.netdev&lt;/code> file:&lt;/p>
&lt;script type="application/javascript" src="https://gist.github.com/andir/146803a9343e04fffabc8e7105dff3cd.js">&lt;/script>
&lt;p>After restarting &lt;code>systemd-networkd&lt;/code> with &lt;code>systemctl restart systemd-networkd&lt;/code>
you should see the corresponding interface:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">$ ip -d link show dev vrf-customer1
9: vrf-customer1: &amp;lt;NOARP,MASTER&amp;gt; mtu &lt;span style="color:#ae81ff">1500&lt;/span> qdisc noop state DOWN mode DEFAULT group default qlen &lt;span style="color:#ae81ff">1000&lt;/span>
link/ether 02:74:c7:e1:de:64 brd ff:ff:ff:ff:ff:ff promiscuity &lt;span style="color:#ae81ff">0&lt;/span>
vrf table &lt;span style="color:#ae81ff">42&lt;/span> addrgenmode eui64 numtxqueues &lt;span style="color:#ae81ff">1&lt;/span> numrxqueues &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note the last line which states &lt;code>vrf table 42&lt;/code>.&lt;/p>
&lt;p>To add an interface to the VRF you&amp;rsquo;ll have to modify/create the corresponding
.network file. This is how the file &lt;code>/etc/systemd/network/enp0s31f6.network&lt;/code>
would look on my notebook:&lt;/p>
&lt;script type="application/javascript" src="https://gist.github.com/andir/ee155492e3af2f83df39b0808fda5718.js">&lt;/script>
&lt;p>Restarting &lt;code>systemd-networkd&lt;/code> again and checking the status using &lt;code>ip -d link&lt;/code> gives us:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">$ip -d link show dev enp0s31f6
3: enp0s31f6: &amp;lt;NO-CARRIER,BROADCAST,MULTICAST,UP&amp;gt; mtu &lt;span style="color:#ae81ff">1500&lt;/span> qdisc fq_codel master vrf-customer1 state DOWN mode DEFAULT group default qlen &lt;span style="color:#ae81ff">1000&lt;/span>
link/ether 50:7b:9d:cf:34:dc brd ff:ff:ff:ff:ff:ff promiscuity &lt;span style="color:#ae81ff">0&lt;/span>
vrf_slave table &lt;span style="color:#ae81ff">42&lt;/span> addrgenmode eui64 numtxqueues &lt;span style="color:#ae81ff">1&lt;/span> numrxqueues &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Again note the last line which states &lt;code>vrf_slave table 42&lt;/code>. Also in the first
line you can see that it belongs to the VRF &lt;code>vrf-customer&lt;/code>.&lt;/p>
&lt;p>And that is all for now. You still have to add the &lt;code>ip rule&lt;/code> commands in some
way or another (there is no support in systemd-networkd yet and I did not have
a good idea without inventing &lt;code>ip rule&lt;/code> management in systemd).&lt;/p></description></item><item><title>Using VRFs with linux</title><link>https://andreas.rammhold.de/posts/linux-ip-vrf/</link><pubDate>Tue, 14 Jun 2016 17:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/linux-ip-vrf/</guid><description>&lt;p>Ever since I&amp;rsquo;ve heard about VRF support (or VRF-lite like it is called in Documentation/network/vrf.txt) I wanted to start tinkering with it.
Since the topic is currently only covered in the previously mentioned linux kernel documentation I thought it would be a good idea to post some notes.&lt;/p>
&lt;p>It basically boils down to adding an VRF interface and creating two &lt;code>ip rule&lt;/code>-entries.&lt;/p>
&lt;p>I&amp;rsquo;m using a local VM with ArchLinux since the VRF feature seems to require a rather recent kernel. My experience with kernels below version 4.6 weren&amp;rsquo;t that great.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">$ ip -br link &lt;span style="color:#75715e"># this is where we are start off&lt;/span>
lo UNKNOWN 00:00:00:00:00:00 &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt;
ens3 DOWN 52:54:00:12:34:56 &amp;lt;BROADCAST,MULTICAST&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now are adding a new interface named &lt;code>vrf-customer1&lt;/code> with the table &lt;code>customer1&lt;/code> assigned to it.
The table parameter is used to place routes from your devices within the VRF into the right routing table&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">$ ip link add vrf-customer1 type vrf table customer1
$ ip -d link show vrf-customer1 &lt;span style="color:#75715e"># verify that the interface indeed exists and has the correct table assigned to it&lt;/span>
4: vrf-customer1: &amp;lt;NOARP,MASTER&amp;gt; mtu &lt;span style="color:#ae81ff">1500&lt;/span> qdisc noop state DOWN mode DEFAULT group default qlen &lt;span style="color:#ae81ff">1000&lt;/span>
link/ether ca:22:59:ba:05:da brd ff:ff:ff:ff:ff:ff promiscuity &lt;span style="color:#ae81ff">0&lt;/span>
vrf table &lt;span style="color:#ae81ff">100&lt;/span> addrgenmode eui64
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next: redirect the traffic from and to the vrf to the customer1 table and verify the rules are indeed as expected:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">$ ip -4 rule add oif vrf-customer1 lookup customer1
$ ip -4 rule add iif vrf-customer1 lookup customer1
$ ip -6 rule add oif vrf-customer1 lookup customer1
$ ip -6 rule add iif vrf-customer1 lookup customer1
$ ip -4 rule
0: from all lookup local
32764: from all iif vrf-customer1 lookup customer1
32765: from all oif vrf-customer1 lookup customer1
32766: from all lookup main
32767: from all lookup default
$ ip -6 rule
0: from all lookup local
32764: from all oif vrf-customer1 lookup customer1
32765: from all iif vrf-customer1 lookup customer1
32766: from all lookup main
&lt;/code>&lt;/pre>&lt;/div>&lt;p>To make any use of our VRF we will have to add an device to it. In my case I&amp;rsquo;ll add the only available &amp;ldquo;physical&amp;rdquo; device &lt;code>ens3&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">$ ip link set ens3 master vrf-customer1
$ &lt;span style="color:#75715e"># verify the interface is indeed a member of the VRF&lt;/span>
$ ip -br link show master vrf-customer1
ens3 DOWN 52:54:00:12:34:56 &amp;lt;BROADCAST,MULTICAST&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now that we&amp;rsquo;ve an interface to receive send send packets with it we should consider adding an IP-Address to it. Since IPv6 is enabled per default we don&amp;rsquo;t need to configure a LL-Address for that protocol.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> $ &lt;span style="color:#75715e"># add an IP to the interface&lt;/span>
$ ip addr add 10.0.0.1/24 dev ens3
$ ip route show table customer1
local 10.0.0.1 dev ens3 proto kernel scope host src 10.0.0.1
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Seeing a route like that might confuse the average linux user. Those routes usually exist within the local table which you can check via &lt;code>ip route show table local&lt;/code>&lt;/p>
&lt;p>The route to the /24 we&amp;rsquo;ve added is still missing from the interface. Why is that?
You&amp;rsquo;ll have to change the state of the interface to &amp;ldquo;UP&amp;rdquo;:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> $ ip link set ens3 up
$ ip route show table customer1
broadcast 10.0.0.0 dev ens3 proto kernel scope link src 10.0.0.1
10.0.0.0/24 dev ens3 proto kernel scope link src 10.0.0.1
local 10.0.0.1 dev ens3 proto kernel scope host src 10.0.0.1
broadcast 10.0.0.255 dev ens3 proto kernel scope link src 10.0.0.1
$ ip -6 route show table customer1
local fe80::5054:ff:fe12:3456 dev lo proto none metric &lt;span style="color:#ae81ff">0&lt;/span> pref medium
fe80::/64 dev ens3 proto kernel metric &lt;span style="color:#ae81ff">256&lt;/span> pref medium
ff00::/8 dev vrf-customer1 metric &lt;span style="color:#ae81ff">256&lt;/span> pref medium
ff00::/8 dev ens3 metric &lt;span style="color:#ae81ff">256&lt;/span> pref medium
&lt;/code>&lt;/pre>&lt;/div>&lt;p>suddenly routes \o/&lt;/p></description></item><item><title>Using multiple client classes with ISC DHCPd</title><link>https://andreas.rammhold.de/posts/isc-dhcpd-multiple-classes/</link><pubDate>Mon, 23 May 2016 11:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/posts/isc-dhcpd-multiple-classes/</guid><description>&lt;p>Since the internet is lacking examples of how to use multiple classes with a single pool here is one:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash"> class &lt;span style="color:#e6db74">&amp;#34;mac-filtered-clients&amp;#34;&lt;/span>
&lt;span style="color:#f92672">{&lt;/span>
match binary-to-ascii &lt;span style="color:#f92672">(&lt;/span>16, 8, &lt;span style="color:#e6db74">&amp;#34;:&amp;#34;&lt;/span>, substring&lt;span style="color:#f92672">(&lt;/span>hardware, 1, 6&lt;span style="color:#f92672">))&lt;/span>;
&lt;span style="color:#f92672">}&lt;/span>
subclass &lt;span style="color:#e6db74">&amp;#34;mac-filtered-clients&amp;#34;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;50:7b:00:00:00:00&amp;#34;&lt;/span>; &lt;span style="color:#75715e"># some cool host!&lt;/span>
class &lt;span style="color:#e6db74">&amp;#34;J-client&amp;#34;&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
spawn with option agent.circuit-id;
match &lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#f92672">(&lt;/span>substring&lt;span style="color:#f92672">(&lt;/span>option agent.circuit-id, 30, 9&lt;span style="color:#f92672">)&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;foo-bar&amp;#34;&lt;/span>&lt;span style="color:#f92672">)&lt;/span>;
lease limit 1;
&lt;span style="color:#f92672">}&lt;/span>
subnet 192.168.0.0 255.255.0.0 &lt;span style="color:#f92672">{&lt;/span>
pool &lt;span style="color:#f92672">{&lt;/span>
range 192.168.0.10 192.168.0.150;
allow members of &lt;span style="color:#e6db74">&amp;#34;J-client&amp;#34;&lt;/span>;
allow members of &lt;span style="color:#e6db74">&amp;#34;mac-filtered-clients&amp;#34;&lt;/span>;
&lt;span style="color:#f92672">}&lt;/span>
&lt;span style="color:#f92672">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This isn&amp;rsquo;t very special compared to a setup with just a single class but it can be confusing since debugging classes is a pita.. One pitfall I did run into was using the byte representation of the mac-addresses (without the quotes) and using &lt;code>match hardware;&lt;/code>. The example above works for me (tm).&lt;/p></description></item><item><title>Postgresql-tmpfs with systemd.socket-activation for local (ephemeral) data during development</title><link>https://andreas.rammhold.de/posts/postgresql-tmpfs-with-sytemdsocket-activation-for-local-ephemeral-data-during-development/</link><pubDate>Fri, 22 Apr 2016 09:50:09 +0000</pubDate><guid>https://andreas.rammhold.de/posts/postgresql-tmpfs-with-sytemdsocket-activation-for-local-ephemeral-data-during-development/</guid><description>&lt;p>During development of database related stuff you commonly run into the &amp;ldquo;issue&amp;rdquo; (or non-issue depending on your taste) of running a local database server - or multiple of those.&lt;/p>
&lt;p>In my case I have to run a local postgresql server on my notebook. I asked myself: I&amp;rsquo;m not always developing on that piece of software, and I do not always require or want a local postgresql server. What can I do about that?!?&lt;/p>
&lt;p>On top of that using my precious SSD to store data I am going to delete anyway souds like a waste (or money). In my development environment I can and want to safely wipe the data often. Also most of the database load comes from running test cases anyway. That stuff doesn&amp;rsquo;t need to end up on my (slow, compared to RAM) disk. Using a tmpfs for that kind of stuff sounds much saner to me.&lt;/p>
&lt;p>The part of running a repetitive clean database setup sounded like the use case for a container based thing. These days docker is pretty &amp;ldquo;hot&amp;rdquo; and it solves the issue of distributing re-useable images. There is an official postgresql image on docker hub for various versions of postgresql. I&amp;rsquo;ve simply build a new image based on that. It is available on docker hub (&lt;a href="https://hub.docker.com/r/andir/postgresql-tmpfs/">https://hub.docker.com/r/andir/postgresql-tmpfs/&lt;/a>) or if you prefer to build it on your own you can download the Dockerfile on GitHub (&lt;a href="https://github.com/andir/postgresql-tmpfs)">https://github.com/andir/postgresql-tmpfs)&lt;/a>.&lt;/p>
&lt;p>Now that we are past the introductional blabla here are the systemd unit files I&amp;rsquo;m using to achieve this:&lt;/p>
&lt;script type="application/javascript" src="https://gist.github.com/andir/d8307bcead6d83945db462698163ff40.js">&lt;/script>
&lt;p>You can either put those unit files in &lt;code>/etc/systemd/system&lt;/code> or install them as systemd-user units in &lt;code>~/.config/systemd/user&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">systemctl daemon-reload
systemctl enable postgresql-docker.socket
&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you try to connect to the postgresql server (&lt;code>nc 127.0.0.1 5432&lt;/code>) you can observe the container while it is starting (&lt;code>journalctl -f&lt;/code>).&lt;/p>
&lt;p>The default username, password and database name is &lt;code>postgres&lt;/code>. You can change that by modifying the startup arguments of the docker container. Those are documented at &lt;a href="https://hub.docker.com/_/postgres/">https://hub.docker.com/_/postgres/&lt;/a>.&lt;/p>
&lt;p>Happy data trashing \o/&lt;/p>
&lt;p>P.S.: If you&amp;rsquo;ve an idea on how to stop the service after x minutes of inactivity please let me know. Stopping the service manually isn&amp;rsquo;t really what I&amp;rsquo;m after.&lt;/p></description></item><item><title>About</title><link>https://andreas.rammhold.de/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://andreas.rammhold.de/about/</guid><description>&lt;p>I like hacking on software projects, taking pictures or just listening to
music. These days I spent my days (and most evenings) with
&lt;a href="https://nixos.org">Nix&lt;/a> releated topics.&lt;/p>
&lt;p>Some of my projects:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/nix-community/npmlock2nix">npmlock2nix&lt;/a> npm-lockfile.json based Node build tooling for Nix&lt;/li>
&lt;li>&lt;a href="https://broken.sh">broken.sh&lt;/a> NixOS and Nixpkgs vulnerability database&lt;/li>
&lt;li>&lt;a href="https://ipv6.watch">ipv6.watch&lt;/a> IPv6 deployment status on some (random) public websites&lt;/li>
&lt;li>&lt;a href="https://glaskugel.io">glaskugel.io&lt;/a> Searx instance that tunnels all search traffic through tor&lt;/li>
&lt;li>&lt;a href="https://darmstadt.io">darmstadt.io&lt;/a> Public transit map of my home town (Darmstadt)&lt;/li>
&lt;/ul>
&lt;p>(Incomplete, probably forgot a few)&lt;/p>
&lt;h1 id="contact">Contact&lt;/h1>
&lt;p>You can reach me via&lt;/p>
&lt;ul>
&lt;li>IRC (nick: &lt;code>andi-&lt;/code>, networks: libera, hackint,
…),&lt;/li>
&lt;li>XMPP (&lt;code>andi@kack.it&lt;/code>),&lt;/li>
&lt;li>Matrix (&lt;code>@andi:kack.it&lt;/code>) or&lt;/li>
&lt;li>email (&lt;code>andreas (@) rammhold.de&lt;/code>).&lt;/li>
&lt;/ul></description></item></channel></rss>