<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Analog</title>
    <link>https://a.custura.eu/index.xml</link>
    <description>Recent content on Analog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sat, 29 Aug 2020 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://a.custura.eu/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>/pubs</title>
      <link>https://a.custura.eu/pubs/</link>
      <pubDate>Sat, 29 Aug 2020 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/pubs/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Impact of Acknowledgements using IETF QUIC on Satellite Performance.&lt;/strong&gt; &lt;em&gt;Ana Custura, Tom Jones, Gorry Fairhurst&lt;/em&gt;. ASMS/SPSC 2020: 1-8
&lt;a href=&#34;../pub/ASMS2020_ACKSatelliteQUIC.pdf&#34;&gt;&lt;i class=&#34;fa fa-download symbol-downloads&#34; &gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rethinking ACKs at the Transport Layer.&lt;/strong&gt; &lt;em&gt;Ana Custura, Tom Jones, Gorry Fairhurst&lt;/em&gt;. FIT 2020: 731-736
&lt;a href=&#34;../pub/FIT2020_ACKsTransport.pdf&#34;&gt;&lt;i class=&#34;fa fa-download symbol-downloads&#34; &gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measuring mobile performance in the Tor network with OnionPerf.&lt;/strong&gt; &lt;em&gt;Ana Custura, Iain Learmonth, Gorry Fairhurst&lt;/em&gt;. MNM 2019: 233-238
&lt;a href=&#34;../pub/MNM2019_OnionPerf.pdf&#34;&gt;&lt;i class=&#34;fa fa-download symbol-downloads&#34; &gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exploring Usable Path MTU in the Internet.&lt;/strong&gt; &lt;em&gt;Ana Custura, Gorry Fairhurst, Iain Learmonth&lt;/em&gt;. Traffic Measurement Analysis 2018: 1-8
&lt;a href=&#34;../pub/TMA2018_ExploringPathMTU.pdf&#34;&gt;&lt;i class=&#34;fa fa-download symbol-downloads&#34; &gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experience: Implications of Roaming in Europe.&lt;/strong&gt; &lt;em&gt;Anna Maria Mandalari, Andra Lutu, Ana Custura, Ali Safari Khatouni, Özgü Alay, Marcelo Bagnulo, Vaibhav Bajpai, Anna Brunström, Jörg Ott, Marco Mellia, Gorry Fairhurst&lt;/em&gt;. MobiCom 2018: 179-189
&lt;a href=&#34;../pub/MobiCom2018_RoamingEurope.pdf&#34;&gt;&lt;i class=&#34;fa fa-download symbol-downloads&#34; &gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exploring DSCP modification pathologies in the Internet.&lt;/strong&gt; &lt;em&gt;Ana Custura, Raffaello Secchi, Gorry Fairhurst&lt;/em&gt;. Comput. Commun. 127: 86-94 (2018)
&lt;a href=&#34;../pub/ACM_CC2018_ExploringDSCP.pdf&#34;&gt;&lt;i class=&#34;fa fa-download symbol-downloads&#34; &gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exploring DSCP modification pathologies in mobile edge networks.&lt;/strong&gt; &lt;em&gt;Ana Custura, Andre Venne, Gorry Fairhurst&lt;/em&gt;. MNM 2017: 1-6
&lt;a href=&#34;../pub/MNM2017_DSCPmobile.pdf&#34;&gt;&lt;i class=&#34;fa fa-download symbol-downloads&#34; &gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>For sale</title>
      <link>https://a.custura.eu/radio/</link>
      <pubDate>Sat, 29 Aug 2020 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/radio/</guid>
      <description>

&lt;h1 id=&#34;vx-8e-vhf-amateur-handheld-transciever-for-sale&#34;&gt;VX-8E VHF Amateur Handheld Transciever for sale&lt;/h1&gt;


&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/1.jpg/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/radio/1.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;


&lt;ul&gt;
&lt;li&gt;TX on 70cm, 2M and 6M.&lt;/li&gt;
&lt;li&gt;Built-in packet modem 1200/9600bd with APRS support&lt;/li&gt;
&lt;li&gt;Comes with mounted GPS antenna, charger, 3100mAh battery, extra clip and original battery pack&lt;/li&gt;
&lt;li&gt;5W power output&lt;/li&gt;
&lt;li&gt;Modes: TX FM, RX FM/AM/WFM&lt;/li&gt;
&lt;li&gt;Full specs: &lt;a href=&#34;http://rigpix.com/yaesu/vx8e.htm&#34;&gt;http://rigpix.com/yaesu/vx8e.htm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Asking price for radio, accessories and postage is £180. Contact ana@netstat.org.uk.&lt;/p&gt;

&lt;p&gt;
&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/2.jpg/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/radio/2.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;


&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/3.jpg/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/radio/3.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;


&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/4.jpg/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/radio/4.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;


&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/5.jpg/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/radio/5.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;
&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>/about</title>
      <link>https://a.custura.eu/about/</link>
      <pubDate>Sat, 23 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/about/</guid>
      <description>&lt;p&gt;I do Internet research and sometimes interesting things with computers.&lt;/p&gt;

&lt;p&gt;My interests include network security, network measurements and transparency,
privacy and digital freedom, amateur radio and electronics.
I am also  a fan of Free Software and
contribute to &lt;a href=&#34;https://qa.debian.org/developer.php?login=ana@netstat.org.uk&#34;&gt;Debian&lt;/a&gt; and &lt;a href=&#34;https://www.torproject.org/&#34;&gt;Tor Project&lt;/a&gt; in my spare time.&lt;/p&gt;

&lt;p&gt;You can drop me a GPG-encrypted email at &lt;code&gt;ana@netstat.org.uk&lt;/code&gt;, my fingerprint is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;85E7 AAB8 C29D F383 4940 5DC0 6F8D E44D 59D7 DBCC&lt;/code&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A quick look at the QUIC software ecosystem</title>
      <link>https://a.custura.eu/post/nginx-quic/</link>
      <pubDate>Tue, 16 Mar 2021 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/nginx-quic/</guid>
      <description>

&lt;p&gt;As far as transport protocols go, &lt;a href=&#34;https://tools.ietf.org/html/rfc793&#34;&gt;TCP&lt;/a&gt; is the most used today, and when it comes to TCP implementations, there are as many TCP stacks as there are networked operating systems. This includes everything from Windows PCs to Android Phones and Cisco routers.
In most cases, an update to TCP requires a firmware change or OS update - and all endpoints, routers and network appliances may need to deploy a feature for it to work.&lt;/p&gt;

&lt;p&gt;Not the case with &lt;a href=&#34;https://www.ietf.org/archive/id/draft-ietf-quic-transport-34.txt&#34;&gt;QUIC&lt;/a&gt;, an encrypted transport layer protocol which runs on top of UDP.
As QUIC implementations can live in userspace, development can happen separate from the OS. This means control over transport features shifts away from network vendors and operating system implementors to application service providers - enterprises who run the web, like Google and Facebook.&lt;/p&gt;

&lt;p&gt;The IETF standards for HTTP/3 and QUIC version 1 are &lt;a href=&#34;https://datatracker.ietf.org/doc/draft-ietf-quic-transport/&#34;&gt;about to be published by the IETF&lt;/a&gt;, and the software ecosystem to go with them is still emerging.
Many implementations are still under development, and while some provide servers and clients for the QUIC transport, there are very few production-ready out-of-the-box HTTP/3 servers.&lt;/p&gt;

&lt;h2 id=&#34;who-s-using-what&#34;&gt;Who&amp;rsquo;s using what?&lt;/h2&gt;

&lt;p&gt;Serving a website using HTTP3/QUIC needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an SSL library with TLS 1.3 and &lt;a href=&#34;https://tools.ietf.org/html/draft-ietf-quic-tls-34&#34;&gt;QUIC crypto support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;a QUIC implementation&lt;/li&gt;
&lt;li&gt;an HTTP/3-enabled webserver&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It&amp;rsquo;s March 2021 at the time of writing. There are several open-source implementations of QUIC, in various stages of readiness.
Here&amp;rsquo;s a diagram of all the QUICs, their TLS library dependencies, and who implements and uses them. Those who run their variant
in production are highlighted in orange:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://a.custura.eu/images/quic-eco.svg&#34; alt=&#34;QUIC Software Ecosystem. This diagram is not exhaustive.&#34; /&gt;&lt;/p&gt;

&lt;p&gt;This diagram is not exhaustive, the IETF QUIC working group maintains its own list, including non-open-source &lt;a href=&#34;https://github.com/quicwg/base-drafts/wiki/Implementations&#34;&gt;implementations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All the implementations rely on some sort of custom TLS library: the most popular appears to be &lt;a href=&#34;https://boringssl.googlesource.com/boringssl/&#34;&gt;BoringSSL&lt;/a&gt;, Google&amp;rsquo;s fork of &lt;a href=&#34;https://www.openssl.org/&#34;&gt;OpenSSL&lt;/a&gt;.
Other implementations take the approach of developing their own TLS 1.3 libraries (like &lt;a href=&#34;https://github.com/h2o/picotls&#34;&gt;PicoTLS&lt;/a&gt;), which depend on OpenSSL.
It doesn&amp;rsquo;t look like &lt;a href=&#34;https://gnutls.org/&#34;&gt;gnuTLS&lt;/a&gt; is making an appearance.&lt;/p&gt;

&lt;p&gt;Google have been &lt;a href=&#34;https://blog.chromium.org/2020/10/chrome-is-deploying-http3-and-ietf-quic.html&#34;&gt;using QUIC in production for a few years&lt;/a&gt; (and have switched to IETF QUIC), unsurprisingly as they originated the protocol.&lt;/p&gt;

&lt;p&gt;Facebook allegedly &lt;a href=&#34;https://engineering.fb.com/2020/10/21/networking-traffic/how-facebook-is-bringing-quic-to-billions/&#34;&gt;use QUIC for more than 75% of their traffic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.fastly.com/blog/state-of-quic-and-http3-2020&#34;&gt;Fastly&lt;/a&gt; and &lt;a href=&#34;https://blog.cloudflare.com/http3-the-past-present-and-future/&#34;&gt;Cloudflare&lt;/a&gt; now allow their customers to enable QUIC for their websites.&lt;/p&gt;

&lt;p&gt;Akamai have been using their own implementation &lt;a href=&#34;https://myakamai.force.com/customers/s/article/FAQ-QUIC-Native-Platform-Support-for-Media-Delivery-Products?language=en_US&#34;&gt;since 2016&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Client-side, there is support for QUIC in &lt;a href=&#34;https://www.zdnet.com/article/cloudflare-google-chrome-and-firefox-add-http3-support/&#34;&gt;major browsers&lt;/a&gt;, &lt;a href=&#34;https://blog.cloudflare.com/how-to-test-http-3-and-quic-with-firefox-nightly/&#34;&gt;including Firefox nightly&lt;/a&gt; since last year - although it&amp;rsquo;s not enabled by default.&lt;/p&gt;

&lt;p&gt;With so many implementations, perhaps you&amp;rsquo;re wondering if they all talk to each other?  Well, the QUIC Working Group
also runs &lt;a href=&#34;https://interop.seemann.io/&#34;&gt;interoperability testing of the various implementations&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&#34;what-about-us&#34;&gt;What about us?&lt;/h2&gt;

&lt;p&gt;If you&amp;rsquo;re not a big corporation and want to serve your website over QUIC, then right now you have a few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://hg.nginx.org/nginx-quic/file/tip/README&#34;&gt;nginx-quic&lt;/a&gt; - according to their README, code is experimental and still under development&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/h2o/h2o&#34;&gt;h2o&lt;/a&gt;- also provides &amp;lsquo;experimental&amp;rsquo; HTTP/3 support&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cwiki.apache.org/confluence/display/TS/QUIC&#34;&gt;apache with QUIC support&lt;/a&gt;- also under development&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openlitespeed.org/&#34;&gt;openlitespeed&lt;/a&gt; - appears to be the only production-ready option&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run an HTTP/3 server out of the box on any  OS, first the custom TLS libraries need to make their way into their respective distribution.
On top of this, webservers like Apache and NGINX also need to integrate support for QUIC/HTTP3 in a main release.
Any other QUIC and HTTP/3 implementations would also need to be packaged for major server distributions.&lt;br /&gt;
In my experience with Debian packaging, this is only likely to happen after the software is mature enough, by which point TLS dependencies will have already been packaged.&lt;/p&gt;

&lt;p&gt;For now, everything needs to be built from source.&lt;/p&gt;

&lt;p&gt;You can of course host your website with Cloudflare or Fastly and simply enable QUIC - but where&amp;rsquo;s the fun in that?&lt;/p&gt;

&lt;h2 id=&#34;my-experience-with-nginx-quic&#34;&gt;My experience with nginx-quic&lt;/h2&gt;

&lt;p&gt;I&amp;rsquo;ve deployed nginx-quic on a Raspberry Pi.
This was made more complicated due to hitting a bug in gcc 8.3 when building BoringSSL and needing to upgrade the Pi from &lt;code&gt;buster&lt;/code&gt; to &lt;code&gt;bullseye&lt;/code&gt;, the soon-to-be Debian Stable. After upgrading, BoringSSL built as per instructions in their repo.&lt;/p&gt;

&lt;p&gt;Nginx-quic provides a guide for building and configuring the server.
I configured the server as per instructions, and tested it with Firefox versions 78 and 85, and Chrome version 88 on both Linux and MacOS.&lt;/p&gt;

&lt;p&gt;The mechanism to signal a client the presence of an HTTP/3 server is the use of the &lt;code&gt;alt-svc&lt;/code&gt; header, which tells the client which QUIC versions are support and which port to use.
In theory, the second time a resource is requested it will use HTTP/3.
For my server, this worked&amp;hellip; eventually, using version 85, after much page reloading, cache clearing and browser restarting. The logs show about 20 HTTP/1.1 attempts until HTTP/3 was finally used.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://a.custura.eu/images/http3.png&#34; alt=&#34;Success!&#34; /&gt;&lt;/p&gt;

&lt;p&gt;I have not managed to get Firefox version 78 and the latest Chrome to do the same, despite enabling QUIC support.&lt;/p&gt;

&lt;h1 id=&#34;final-words&#34;&gt;Final words&lt;/h1&gt;

&lt;p&gt;On one hand, controlling software at both endpoints of a network path (like Google controls its own servers and your browser!) means innovation in the QUIC transport can happen really fast.&lt;/p&gt;

&lt;p&gt;But right now, each of my browsers comes with a different implementation of QUIC. If I want &lt;a href=&#34;https://github.com/curl/curl/blob/master/docs/HTTP3.md&#34;&gt;cURL with HTTP/3&lt;/a&gt; support I&amp;rsquo;ll likely need to install yet another QUIC library. What happens when more applications use it?
All this seems wasteful, if you consider all applications on an endpoint share a TCP stack.&lt;/p&gt;

&lt;hr /&gt;
</description>
    </item>
    
    <item>
      <title>Firefox modding with containers and SOCKS proxies</title>
      <link>https://a.custura.eu/post/autossh/</link>
      <pubDate>Thu, 24 Sep 2020 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/autossh/</guid>
      <description>

&lt;p&gt;Here&amp;rsquo;s a guide to my lazy setup for running multiple Firefox tabs in the same
session over different networks using the magic of SOCKS.&lt;/p&gt;

&lt;p&gt;The use-case is that I sometimes want to access a web app or page which is only
accessible via a specific network (i.e., my work network or &lt;a href=&#34;torproject.org&#34;&gt;Tor&lt;/a&gt;), but I most
definitely don&amp;rsquo;t want the rest of my browsing traffic going through there.&lt;/p&gt;

&lt;p&gt;The general idea is to use long-running SSH tunnels to provide one or more
SOCKS5 proxies that can be used by Firefox (or your browser of choice). &lt;a href=&#34;https://tools.ietf.org/html/rfc1928&#34;&gt;SOCKS&lt;/a&gt; is
a protocol that allows applications to request connections through a proxy
server. Applications, such as Firefox, must be configured to use it.&lt;/p&gt;

&lt;p&gt;Generally, to do this manually, you&amp;rsquo;d first SSH with dynamic forwarding into a host on the desired network:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ssh -D1080 user@host
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&amp;hellip;and now a SOCKS proxy on localhost port 1080 is ready to forward connections
to the remote host. Tor also provides a SOCKS proxy that can be used in
much the same way by default on port 9050. This does not conflict with Tor Browser,
which runs its own Tor daemon listening on port 9051, separate from the system Tor.&lt;/p&gt;

&lt;p&gt;So, to use the proxy in a browser, the browser&amp;rsquo;s network settings should be changed to resemble something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://a.custura.eu/images/socks.png&#34; alt=&#34;Firefox settings&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Firefox also has a checkbox for proxying DNS requests through the same connection.&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s a good idea to proxy your DNS requests because 1) the remote DNS resolver might know names of resources you can&amp;rsquo;t access
locally and 2) due to the prevalence of CDNs in the Internet, the IP addresses
obtained this way will often correspond to servers physically closer to the
tunnel endpoint, speeding up connections.&lt;/p&gt;

&lt;p&gt;These settings could be saved under a separate Firefox profile that can be
fired up whenever the SSH connection is active.  Any browser requests will be
forwarded to the network of the host you&amp;rsquo;re SSHed into.&lt;/p&gt;

&lt;p&gt;Now, this is an easy substitute for a VPN, but still requires launching a new
SSH connection and browser instance every time you want to browse via the
remote network. Plus, multiple networks mean multiple profiles or multiple SSH
connections which is a pain to manage.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/&#34;&gt;Firefox containers&lt;/a&gt; and &lt;a href=&#34;https://linux.die.net/man/1/autossh&#34;&gt;autossh&lt;/a&gt;. The first is an extension that allows you
to keep website data, cookies, and cache separate between tabs and websites by assigning them to different containers.&lt;/p&gt;

&lt;p&gt;The second is a way to maintain an SSH tunnel indefinitely.
The way to glue them together is &lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/container-proxy/&#34;&gt;Container Proxy&lt;/a&gt;, another Firefox extension that allows per-container proxy settings.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s how it works:&lt;/p&gt;

&lt;h4 id=&#34;autossh-and-tor&#34;&gt;&lt;code&gt;Autossh&lt;/code&gt; and Tor&lt;/h4&gt;

&lt;p&gt;This is a wrapper around &lt;code&gt;ssh&lt;/code&gt; to keep tunnels open indefinitely in the background. It can use any SSH option or config.
For simplicity, I have the following config specified for my proxy host in &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Host pxhost
    Hostname pxhost.example.com
    ServerAliveInterval 30
    ServerAliveCountMax 3
    DynamicForward 1080
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This command will run &lt;code&gt;autossh&lt;/code&gt; in the background, forever keeping the
connection alive.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;autossh -M 0 -f -N pxhost 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To persist this on reboot, I use a &lt;code&gt;systemd&lt;/code&gt; service file for Linux and a &lt;code&gt;@reboot&lt;/code&gt; cronjob for macOS.&lt;/p&gt;

&lt;p&gt;I also have Tor configured to run at startup, allowing me to use it alongside other connections. Tor can run as a service on distros using &lt;code&gt;systemd&lt;/code&gt;.
On macOS, I modified the &lt;code&gt;.torrc&lt;/code&gt; file in my home directory to include &lt;code&gt;RunAsDaemon 1&lt;/code&gt;, and just running &lt;code&gt;tor&lt;/code&gt; with no options on the command line starts
the SOCKS proxy.&lt;/p&gt;

&lt;h4 id=&#34;firefox-multi-account-containers&#34;&gt;Firefox Multi-Account Containers&lt;/h4&gt;

&lt;p&gt;The extension can be found &lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/&#34;&gt;in the official Firefox store&lt;/a&gt;.
I have three containers: a Direct container for day-to-day browsing without a proxy, a Work container for accessing some infrastructure at work via an SSH connection into my work computer,
and a Tor container for looking at &lt;code&gt;.onion&lt;/code&gt; addresses or other web pages over Tor:&lt;/p&gt;

&lt;p&gt;&lt;img alt=&#34;Firefox Containers&#34; src=&#34;https://a.custura.eu/images/containers.png&#34; style=&#34;width: 20em; margin: 0 auto; display: block;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Tabs opened in each container are colour coded, and easy to keep track of.
&lt;p style=color:red;&#34;&gt;
An important note about the Tor container: using Tor as a proxy and not using
Tor Browser does not provide anonymity, because any other browsers will leak
client information allowing 3rd parties to identify users. I do this mostly for
convenience and sometimes to avoid eavesdropping from my ISP. However, if you
want anonymity, USE TOR BROWSER!
&lt;/p&gt;&lt;/p&gt;

&lt;h4 id=&#34;container-proxy&#34;&gt;Container Proxy&lt;/h4&gt;

&lt;p&gt;Proxies for containers are not supported natively in the official Firefox
extension. At the moment, another extension is required to make the containers
use the tunnels. While not checked by Mozilla, this is open source and the code is auditable at
&lt;a href=&#34;https://github.com/bekh6ex/firefox-container-proxy&#34;&gt;https://github.com/bekh6ex/firefox-container-proxy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It lets you configure and test the proxies with DuckDuckGo, and assign them to containers:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://a.custura.eu/images/cproxy.png&#34; alt=&#34;Container proxy setup&#34; /&gt;&lt;/p&gt;

&lt;p&gt;If the tunnels are set up to persist on reboot, and your Firefox profile is not entirely erased with the latest update, this is how it looks/works:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://a.custura.eu/images/containerexample.png&#34; alt=&#34;Example container&#34; /&gt;&lt;/p&gt;

&lt;p&gt;That&amp;rsquo;s it. Containers are cool. Use more containers, before Mozilla dies off.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Ansible x OpenBSD Web Deployment</title>
      <link>https://a.custura.eu/post/cats-on-the-internet/</link>
      <pubDate>Fri, 10 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/cats-on-the-internet/</guid>
      <description>

&lt;p&gt;I&amp;rsquo;ve recently switched most of my home and away infrastructure from Linux to various flavours of BSD.
This blog post documents an attempt at deploying multiple static websites in the fastest way possible, on &lt;a href=&#34;https://www.openbsd.org/&#34;&gt;OpenBSD&lt;/a&gt;, with &lt;a href=&#34;https://www.ansible.com/&#34;&gt;Ansible&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It covers automating multiple &lt;a href=&#34;https://letsencrypt.org/&#34;&gt;LetsEncrypt&lt;/a&gt;-enabled static websites, configured with &lt;a href=&#34;https://support.torproject.org/onionservices/&#34;&gt;onion services&lt;/a&gt; for access through the &lt;a href=&#34;https://www.torproject.org/&#34;&gt;Tor network&lt;/a&gt;, using OpenBSD&amp;rsquo;s brilliant &lt;code&gt;httpd&lt;/code&gt;.
As a bonus, the content of these websites is automatically generated from cat pictures.&lt;/p&gt;

&lt;p&gt;The requirements for this are a public IP address, ability to make DNS records, and of course&amp;hellip; some static websites to deploy.&lt;/p&gt;

&lt;h2 id=&#34;basics&#34;&gt;Basics&lt;/h2&gt;

&lt;p&gt;At the heart of the Ansible configuration for this project is the group of websites to deploy, which are defined as variables in the hosts file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[www_sites]
site1.example.net
site1.example.com
site2.example.net
site2.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Throughtout the deployment, this group is iterated over.&lt;/p&gt;

&lt;h2 id=&#34;generate-the-websites&#34;&gt;Generate the websites&lt;/h2&gt;

&lt;p&gt;First, to deploy multiple websites, one must have multiple websites available - I made a few, out of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pictures of several cat friends&lt;/li&gt;
&lt;li&gt;a short bio for each cat friend&lt;/li&gt;
&lt;li&gt;a Jinja2 HTML template for the html index&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My website vars group looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[www_sites]
lion.kitty.institute
leppy.kitty.institute
fox.kitty.institute
jinxy.kitty.institute
kitty.kitty.institute
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Directories for each site are iteratively created with an Ansible task:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: create vhost directories
  file:
    path: &amp;quot;/var/www/vhosts/{{ item }}&amp;quot;
    state: directory
    owner: www
  with_items: &amp;quot;{{ groups[&#39;www_sites&#39;] }}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The website content, in our case a generated &lt;code&gt;index.html&lt;/code&gt; file, is also created
by an Ansible task. With each iteration, a variable named &amp;lsquo;vhost&amp;rsquo;, which corresponds to the name of each site,
is passed to the &lt;code&gt;index.html.j2&lt;/code&gt; template:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: create mock site content
  template:
    src: &amp;quot;index.html.j2&amp;quot;
    dest: &amp;quot;/var/www/vhosts/{{ item }}/index.html&amp;quot;
    owner: www
  with_items: &amp;quot;{{ groups[&#39;www_sites&#39;] }}&amp;quot;
  vars: 
    vhost: &amp;quot;{{ item }}&amp;quot; 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The template is simple html, and uses the &amp;lsquo;vhost&amp;rsquo; variable to load the correct bio and picture files.
The bios and pictures files are stored in the main Ansible directory, to be used by the template:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;h1&amp;gt;{{ vhost }}&amp;lt;/h1&amp;gt;
&amp;lt;hr&amp;gt;
&amp;lt;p&amp;gt;&amp;quot;{{ lookup(&#39;file&#39;,&#39;kitty_bios/&#39; + vhost) }}&amp;quot;&amp;lt;/p&amp;gt;
&amp;lt;img src= &amp;quot;{{  vhost }}.jpg&amp;quot; width=&amp;quot;500 px&amp;quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The resulting index files are installed in their own directory, ready to be served.&lt;/p&gt;

&lt;h2 id=&#34;deploy-the-websites&#34;&gt;Deploy the websites&lt;/h2&gt;

&lt;p&gt;To serve the new websites, a configuration file for httpd must be created.
You&amp;rsquo;ve guessed it - this is another template: &lt;code&gt;httpd.conf.j2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The example configuration file shipped with OpenBSD is a wonderful reference here,
and we want to keep the part that redirects port 80 to port 443 and allows
LetsEncrypt verification for ACME challenges, for all the sites (and, aditionally, for your host):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;server &amp;quot;{{ inventory_hostname }}&amp;quot; {
        listen on * port 80
        location &amp;quot;/.well-known/acme-challenge/*&amp;quot; {
                root &amp;quot;/acme&amp;quot;
                request strip 2
        }
        location * {
                block return 302 &amp;quot;https://$HTTP_HOST$REQUEST_URI&amp;quot;
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, a loop is added to make configuration blocks for all the servers in the &lt;code&gt;www_sites&lt;/code&gt; group:
Eventually, all the websites should listen on port 443 and are expected to have TLS certificates -
these will be generated in the next step.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{% for vhost in groups[&#39;www_sites&#39;] %}
server &amp;quot;{{ vhost }}&amp;quot; {
        listen on * tls port 443
        tls {
                certificate &amp;quot;/etc/ssl/{{ vhost }}.fullchain.pem&amp;quot;
                key &amp;quot;/etc/ssl/private/{{ vhost }}.key&amp;quot;
        }
        location &amp;quot;/.well-known/acme-challenge/*&amp;quot; {
                root &amp;quot;/acme&amp;quot;
                request strip 2
        }
        location * {
                root &amp;quot;/vhosts/{{ vhost }}&amp;quot;
        }
}
{% endfor %}

&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;letsencrypt-the-websites&#34;&gt;Letsencrypt the websites&lt;/h2&gt;

&lt;p&gt;Ok, time to fill &lt;code&gt;/etc/ssl&lt;/code&gt; with certificates for these websites.
As a cautionary note, &lt;code&gt;httpd&lt;/code&gt; must be started before you attempt to request the certificates. This was&amp;hellip;the hardest issue to debug in the entire setup.&lt;/p&gt;

&lt;p&gt;So, throw in there:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: enable and start httpd
  service:
    name: httpd
    enabled: yes
    state: started
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For the ACME setup, a configuration file must first be created and installed. Yes, there&amp;rsquo;s a template for that.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: install acme-client.conf
  template:
    src: &amp;quot;acme-client.conf&amp;quot;
    dest: &amp;quot;/etc/acme-client.conf&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The template is based on the default example configuration included with OpenBSD&amp;rsquo;s &lt;code&gt;acme-client&lt;/code&gt;, wrapped in a for loop:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;authority letsencrypt {
        api url &amp;quot;https://acme-v02.api.letsencrypt.org/directory&amp;quot;
        account key &amp;quot;/etc/acme/letsencrypt-privkey.pem&amp;quot;
}

{% for d in groups[&#39;www_sites&#39;] %}
domain &amp;quot;{{ d }}&amp;quot; {
        domain key &amp;quot;/etc/ssl/private/{{ d }}.key&amp;quot;
        domain full chain certificate &amp;quot;/etc/ssl/{{ d }}.fullchain.pem&amp;quot;
        sign with letsencrypt
}
{% endfor %}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For the ACME run, ensure the names match up those used earlier for the &lt;code&gt;httpd&lt;/code&gt; configuration file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: initial acme-client run
  command: &amp;quot;/usr/sbin/acme-client {{ item }}&amp;quot;
  args:
    creates: &amp;quot;/etc/ssl/{{ item }}.fullchain.pem&amp;quot;
  with_items: &amp;quot;{{ groups[&#39;www_sites&#39;] }}&amp;quot;
  notify:
  - reload_httpd
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A task to renew certs indefinitely via cron jobs finishes the job nicely:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: renew certificates via root crontab
  cron:
    name: &amp;quot;acme-client renew {{ item }}&amp;quot;
    minute: &amp;quot;0&amp;quot;
    job: &amp;quot;sleep $((RANDOM \\% 2048)) &amp;amp;&amp;amp; acme-client {{ item }} &amp;amp;&amp;amp; rcctl reload httpd&amp;quot;
    user: root
  with_items: &amp;quot;{{ groups[&#39;www_sites&#39;] }}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To test, go to your website, which should both redirect to https, and should have a valid cert. In this case, &lt;a href=&#34;https://fox.kitty.institute&#34;&gt;https://fox.kitty.institute&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&#34;onion-the-websites&#34;&gt;Onion the websites&lt;/h2&gt;

&lt;p&gt;The cats now must be made available to those under oppresive regimes or avoiding censorship.
An onion address does just that.&lt;/p&gt;

&lt;p&gt;Tor must be installed, and the appropriate configuration to enable onion services must be enabled.
The &lt;code&gt;torrc&lt;/code&gt; file is another template - which basically instructs Tor to create an onion service for each site and store the onion service files in a separate site folder:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Log notice syslog
RunAsDaemon 1
DataDirectory /var/tor
User _tor

{% for domain in groups[&#39;www_sites&#39;] %}
HiddenServiceDir /var/tor/{{ domain }}/
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 443 127.0.0.1:443
{% endfor %}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To install tor, and the configuration file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: install tor
  openbsd_pkg:
    name: [&#39;tor&#39;]
    state: present

- name: install torrc
  template:
    src: &amp;quot;torrc&amp;quot;
    dest: &amp;quot;/etc/tor/torrc&amp;quot;
    owner: root
    group: wheel
    mode: 0644
  register: torrc
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note how copying the &lt;code&gt;torrc&lt;/code&gt; template registers a variable. This is
used later to reload tor if any changes were made to the configuration.&lt;/p&gt;

&lt;p&gt;I should point out that this will generate random onion
addresses. If you want a custom onion address you can mine it and replace the
files in each directory manually.&lt;/p&gt;

&lt;p&gt;Remember: the machine running the
service must be able to read the private key, so to reduce attack surface
the safest way to generate onion addresses is on the very same machine.
Software to mine vanity onion addresses can be found &lt;a href=&#34;https://github.com/cathugger/mkp224o&#34;&gt;here&lt;/a&gt;.
When copying these, the owners and permissions of the original files need to be kept
for tor to run.&lt;/p&gt;

&lt;p&gt;My final onion addresses look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;meow2ecfmjschzktpwnaufh5m5fop3emrmm2mb62gvawovpiyk7jxdqd.onion
meow3pdf65knffidkkypdgtvohispjpzw6omcyoellwythv64vxo5dqd.onion
meow4u5lkpndb562ble3ityac2l3gm47wegtqp72mq2mfqmved7mnhad.onion
meow5w6vxagd7ipzvmb2h54quuzgwkanu2i3zjnb7qloakmgy3nmrgid.onion
meow6onx3grwas4k2i7lb6iecgyk6fkrypcnsatr2tri6kunv36zksid.onion
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
&lt;p&gt;Try these in Tor browser!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The step above is entirely optional. However, after copying the torrc
configuration file, tor must be running and set to run at startup:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: ensure tor is enabled and started
  service:
    name: tor
    enabled: yes
    state: started

- name: reload tor
  service:
    name: tor
    state: reloaded
  when: torrc.changed
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The final step is integrating these tasks in with the rest of the
configuration, and telling &lt;code&gt;httpd&lt;/code&gt;  what to do when it receives requests on the
onion addresses. The onion address must be known before installing the &lt;code&gt;httpd&lt;/code&gt;
template - if they are generated by tor then fetching them manually would be a
pain.  Tor stores the onion address in a file called hostname in &lt;code&gt;/var/tor/&lt;/code&gt;.  A
task can be set up to retrieve these with Ansible:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: retrieve onion hostnames
  fetch:
    src: &amp;quot;/var/tor/{{ item }}/hostname&amp;quot;
    dest: &amp;quot;files/onion_hostnames/{{ item }}&amp;quot;
    flat: yes
  with_items: &amp;quot;{{ groups[&#39;www_sites&#39;] }}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the files are in place ready to be retrieved by other tasks in Ansible. The &lt;code&gt;httpd&lt;/code&gt; template created earlier can now be modified to use the onion addresses:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{% for vhost in groups[&#39;www_sites&#39;] %}
server {{ lookup(&#39;file&#39;, &#39;onion_hostnames/&#39; + vhost) }} {
        listen on * port 80
        location * {
                root &amp;quot;/vhosts/{{ vhost }}&amp;quot;
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will set up listeners on port 80 for each onion, and serve the website
content created earlier. You can enable https for these addresses, but as the
server name certificate won&amp;rsquo;t match the onion name, the user will be presented
with a warning. Note the new alias line included in the template:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;server &amp;quot;{{ vhost }}&amp;quot; {
        listen on * tls port 443
        alias {{ lookup(&#39;file&#39;, &#39;onion_hostnames/&#39; + vhost) }}
        tls {
                certificate &amp;quot;/etc/ssl/{{ vhost }}.fullchain.pem&amp;quot;
                key &amp;quot;/etc/ssl/private/{{ vhost }}.key&amp;quot;
        }
        location &amp;quot;/.well-known/acme-challenge/*&amp;quot; {
                root &amp;quot;/acme&amp;quot;
                request strip 2
        }
        location * {
                root &amp;quot;/vhosts/{{ vhost }}&amp;quot;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Lots of things were omitted here for brevity, but the full template is on &lt;a href=&#34;https://github.com/ana-cc/ansible-openbsd-www&#34;&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To finish this off, a picture of a cat friend:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://a.custura.eu/images/kitty.kitty.institute.jpg&#34; alt=&#34;Kitty&#34; /&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Packet Radio Fun</title>
      <link>https://a.custura.eu/post/packets/</link>
      <pubDate>Thu, 02 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/packets/</guid>
      <description>

&lt;p&gt;Packet radio has been around since the 70s, and is used extensively by radio
amateurs. APRS (Automatic Packet Reporting System) allows amateurs to send
real-time beacons which may include location or other types of information.&lt;/p&gt;

&lt;p&gt;Alongside stations, the APRS infrastructure includes digipeaters and IGates.
APRS packets may be picked up by local digipeaters (repeaters of digital
packets), and then repeated to reach further stations. An IGate
relays received packets to the Internet where a &lt;a href=&#34;aprs.fi&#34;&gt;unified map of all APRS objects&lt;/a&gt; can be displayed.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve tried to do APRS packet radio over the past several days with mixed results.
Here is what I found.&lt;/p&gt;

&lt;h3 id=&#34;required-equipment&#34;&gt;Required equipment&lt;/h3&gt;

&lt;p&gt;The standard APRS frequency for sending messages is 144.800 MHz in Europe, in the 2
metre band.  Any 2M radio that can receive and decode APRS messages will work.
It&amp;rsquo;s the most fun using a handheld device and receive messages as you walk
through a new area.  Unfortunately, handheld radios that have a built-in TNC
are very expensive - but there is an alternative. If all you have is a cheap 2M radio,
it can be used with a &lt;a href=&#34;http://www.mobilinkd.com/&#34;&gt;compact bluetooth TNC device&lt;/a&gt; and an &lt;a href=&#34;https://aprsdroid.org/&#34;&gt;APRS application&lt;/a&gt; on your
phone for the same capabilities.&lt;/p&gt;

&lt;p&gt;If doing this stationary, there are more alternatives - as the TNC bit can be done in software,
with applications such as &lt;a href=&#34;https://github.com/wb2osz/direwolf&#34;&gt;Direwolf&lt;/a&gt;, and all you require
is a sound card and any radio.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve used an &lt;a href=&#34;http://www.dolstra.nl/Ham-radio/Yaesu/Handheld/VX-8DE/VX-8DE.htm&#34;&gt;Yaesu VX-8DE&lt;/a&gt;, which is a handheld device with a built-in TNC.&lt;/p&gt;

&lt;h3 id=&#34;sending-packets&#34;&gt;Sending packets&lt;/h3&gt;

&lt;p&gt;It&amp;rsquo;s harder than you think.&lt;/p&gt;

&lt;h4 id=&#34;problem-1-trains&#34;&gt;Problem #1: Trains&lt;/h4&gt;

&lt;p&gt;When travelling by trains, I was only able to send packets for maybe &lt;sup&gt;1&lt;/sup&gt;&amp;frasl;&lt;sub&gt;5&lt;/sub&gt; of the
total journey time, due to not having GPS coordinates to build packets with.
Turns out that the inside of a train is NOT the best environment for acquiring
a GPS lock.&lt;/p&gt;

&lt;p&gt;Even with the GPS fired up and sending a beacon every minute, only about 10% of
all the packets sent were picked up by any digipeater. Turns out the inside of
a train is NOT the best environment for RF propagation either.
I was digipeated once approaching Leipzig, as the train was stopping:&lt;/p&gt;


&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/almost-in-leipzig1.jpg&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/almost-in-leipzig1.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;


&lt;p&gt;Trains are still amazing despite this slight glitch. And using a bluetooth TNC should eliminate
the GPS problem, as phones are generally better at acquiring GPS data (they can use a broader range of satellites than the proprietary Yaesu GPS antenna for one).&lt;/p&gt;

&lt;h4 id=&#34;problem-2-other-users&#34;&gt;Problem #2: Other users&lt;/h4&gt;

&lt;p&gt;There can be too many APRS packets.&lt;/p&gt;

&lt;p&gt;For example, Cambridge is very active APRS-wise. There
were many stations around, some even within direct messaging distance. However,
trying to send a single APRS message took several attempts due to
collisions.  Timing the message so that it sends between the regular beacons of
other users helps, but rarely first try, and spamming messages to get something
through only makes the problem worse in a crowded spectrum.&lt;/p&gt;

&lt;p&gt;As good practice for those out there thinking of running stationary or unattended beacons
I recommend sending updates at least three minutes apart.&lt;/p&gt;

&lt;h4 id=&#34;problem-3-cloudy-skies-rubber-ducks&#34;&gt;Problem #3: Cloudy skies &amp;amp; rubber ducks&lt;/h4&gt;

&lt;p&gt;Several satellites have amateur radio capabilities, and &lt;a href=&#34;https://www.2m0sql.com/2018/07/23/android-satellite-tracking-applications/&#34;&gt;there are apps&lt;/a&gt; to easily track satellite passes in your location.&lt;/p&gt;

&lt;p&gt;Cambridge is also in a great spot for &lt;a href=&#34;https://www.nasa.gov/mission_pages/station/overview/index.html&#34;&gt;ISS&lt;/a&gt; passes, some with really a nice elevation above the horizon.
The ISS has a digipeater on board. This extends the limited
local 2M perspective to space and back anywhere on the continent.  I spent too
much time in muddy fields trying to get digipeated by the ISS, but with no
success.  However, I did receive stations from Austria (OE6PWE), Sweden
(SM3XLY), Italy (IK1COA), Netherlands (PE1NTN), France (F6FUJ), Germany
(DB1DT), Russia (UA1WBM) and Space itself (RS0ISS):&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://a.custura.eu/images/iss.jpg&#34; alt=&#34;SPACE&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Sending failed possibly due to a combination of the bad
rubber duck antenna of my handheld, sky conditions, maybe competing with
other stations.&lt;/p&gt;

&lt;p&gt;On my to-do list is to build a Yagi, and try again, as per this &lt;a href=&#34;https://www.ariss.org/uploads/1/9/6/8/19681527/k9jkm_2012_symposium_ver2.pdf&#34;&gt;nice start-up guide&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>LibreELEC Streaming Camera</title>
      <link>https://a.custura.eu/post/kodi-security-camera/</link>
      <pubDate>Tue, 12 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/kodi-security-camera/</guid>
      <description>

&lt;p&gt;This is a blogpost about a Raspberry Pi camera setup on LibreELEC. It includes a step by step tutorial.&lt;/p&gt;

&lt;p&gt;If you have the very specific use case of needing to run LibreELEC on a Raspberry Pi to play media while also
streaming a Pi camera, look no further. This setup has been running for the past year or so in my house. I&amp;rsquo;ve
rebuilt the Docker container for it recently, which is the &lt;em&gt;perfect&lt;/em&gt; excuse for a blogpost.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/LibreELEC/LibreELEC.tv&#34;&gt;LibreELEC&lt;/a&gt; is a Linux distribution
for running media center software &lt;a href=&#34;https://kodi.tv/about&#34;&gt;Kodi&lt;/a&gt;. It will stream
films, music, TV shows from your NAS to your TV and has several &lt;a href=&#34;https://wiki.libreelec.tv/add-ons&#34;&gt;awesome add-ons&lt;/a&gt;.
LibreELEC has no add-on for streaming from a camera. Perhaps because the drivers
needed for USB cameras are not actually even compiled into the image for the RPi2.
I&amp;rsquo;ve tried this setup using a USB camera and had to build a custom LibreELEC image.&lt;/p&gt;

&lt;p&gt;However, there is support for streaming from RPi cameras and there is a
Docker add-on, which can be used to run a simple camera streaming app in a
container. The app I&amp;rsquo;ve chosen after some trial and error of which gruelling
details I will spare you is &lt;a href=&#34;https://github.com/jacksonliam/mjpg-streamer&#34;&gt;mjpeg-streamer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The container uses the current Raspbian stable image, &lt;code&gt;stretch&lt;/code&gt;, on top of which it builds mjpeg-streamer.
It then uses an entrypoint script to capture a still image per second from the
Pi camera, which the software then turns into a www stream on port 8080.&lt;/p&gt;

&lt;p&gt;You can get the container &lt;a href=&#34;https://github.com/ana-cc/dockerstuffs/tree/master/mjpeg-streamer-rpi&#34;&gt;here&lt;/a&gt;.
For a step by step tutorial on how to deploy this, read on.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&#34;step-by-step-tutorial&#34;&gt;Step by step tutorial&lt;/h3&gt;

&lt;h5 id=&#34;ingredients&#34;&gt;Ingredients:&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pi v2 or v3&lt;/li&gt;
&lt;li&gt;Micro SD card (&amp;gt;2GB) and SD card adapter&lt;/li&gt;
&lt;li&gt;Compatible Raspberry Pi camera module&lt;/li&gt;
&lt;li&gt;Monitor and keyboard&lt;/li&gt;
&lt;li&gt;Wired connection or wireless dongle if using a RPi 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;step-1-download&#34;&gt;Step 1: Download&lt;/h4&gt;

&lt;p&gt;Download the official LibreELEC image for the Raspberry Pi from &lt;a href=&#34;https://libreelec.tv/downloads/&#34;&gt;the official website&lt;/a&gt;. Scroll to &amp;lsquo;Direct Downloads&amp;rsquo;, select &amp;lsquo;Raspberry Pi v2 and Raspberry Pi v3&amp;rsquo; and click the filename link on the page.
The filename looks like &lt;code&gt;LibreELEC-RPi2.arm-8.2.5.img.gz&lt;/code&gt; at the moment of writing this blog post, yours will most likely be newer.&lt;/p&gt;

&lt;h4 id=&#34;step-2-put-image-on-sd-card&#34;&gt;Step 2: Put image on SD card&lt;/h4&gt;

&lt;p&gt;I use an SD card adapter and the in-built card reader in my laptop. On Linux, after plugging the SD card in, command &lt;code&gt;sudo dmesg&lt;/code&gt; should display the name newly inserted device, similar to the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ana@sinopia:~|⇒  sudo dmesg
[...]
[70540.504869] mmc0: new high speed SDHC card at address aaaa
[70540.585060] mmcblk0: mmc0:aaaa SB16G 14.8 GiB (ro)
[70540.590225] mmcblk0: p1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The identifier for SD cards on Linux usually looks like &lt;code&gt;mmcblk&lt;/code&gt;. Careful to copy your image on the right device, &lt;a href=&#34;https://www.foxk.it/blog/disk-destroyer/&#34;&gt;and not on your local hard drive&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ana@cyan:~|⇒  gunzip LibreELEC-RPi2.arm-8.2.5.img.gz
ana@cyan:~|⇒  sudo dd if=LibreELEC-RPi2.arm-8.2.5.img of=/dev/mmcblk0
549+0 records in
549+0 records out
575668224 bytes (576 MB, 549 MiB) copied, 50.8254 s, 11.3 MB/s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This may take a minute or two. Once dd has finished, load up your MicroSD card in your Raspberry Pi.&lt;/p&gt;

&lt;h4 id=&#34;step-3-configure-libreelec&#34;&gt;Step 3: Configure LibreELEC&lt;/h4&gt;

&lt;p&gt;Plug a monitor and a keyboard in and turn on your
RPi. LibreELEC starts an autoconfiguration wizard when it first boots.  This
will guide you through setting up location, timezone and most importantly, a
network connection needed for the next step. Hit &amp;lsquo;Next&amp;rsquo; and follow the wizard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One you connect to a network, the interface will display your IP address. Make sure to take a note of it.&lt;/li&gt;
&lt;li&gt;Under the initial &amp;lsquo;Sharing and Remote Access&amp;rsquo; screen make sure you enable the SSH service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For security purposes, we will disable SSH after setting up docker as the root password is hardcoded in the image.&lt;/p&gt;

&lt;h4 id=&#34;step-4-download-the-docker-add-on&#34;&gt;Step 4: Download the Docker Add-on&lt;/h4&gt;

&lt;p&gt;From the left hand-side menu, navigate to &lt;code&gt;Add-ons -&amp;gt; Install from repository -&amp;gt; LibreELEC Add-ons -&amp;gt; Services -&amp;gt; Docker&lt;/code&gt; and then select &lt;code&gt;Install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The interface will notify you once this has installed.&lt;/p&gt;

&lt;h4 id=&#34;step-5-build-the-docker-container&#34;&gt;Step 5: Build the Docker container&lt;/h4&gt;

&lt;p&gt;In order to build and run the docker container, you need to ssh as root into your Pi, using the IP address from Step 3.
In this example, my IP is &lt;code&gt;192.168.0.156&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ana@cyan:~|⇒  ssh root@172.22.152.253
root@172.22.152.253&#39;s password
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At the password prompt, type &amp;lsquo;libreelec&amp;rsquo;, the LibreELEC default and hardcoded password.&lt;/p&gt;

&lt;p&gt;Next, make a directory and download the Dockerfile and starting script.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;LibreELEC:~ # mkdir mpeg
LibreELEC:~ # cd mpeg
LibreELEC:~/mpeg # wget https://raw.githubusercontent.com/ana-cc/dockerstuffs/master/mjpeg-streamer-rpi/Dockerfile
Connecting to raw.githubusercontent.com (151.101.60.133:443)
Dockerfile           100% |************************************************************************************************************************|   917   0:00:00 ETA
LibreELEC:~/mpeg # wget https://raw.githubusercontent.com/ana-cc/dockerstuffs/master/mjpeg-streamer-rpi/stream_from_pi.sh
Connecting to github.com (140.82.118.3:443)
stream_from_pi.sh    100% |************************************************************************************************************************| 55534   0:00:00 ETA
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Inspect and (optionally) edit the stream script. The script uses command &lt;code&gt;raspistill&lt;/code&gt; to capture an image per second from the
Pi camera, which mjpeg-streamer then turns into a www stream on port 8080.
You can vary the resolution, quality and time apart these images are taken with the &lt;code&gt;-w&lt;/code&gt; &lt;code&gt;-h&lt;/code&gt; and &lt;code&gt;sleep&lt;/code&gt; values. Here is the script by default:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;until raspistill --nopreview -vf -hf -w 640 -h 480 -q 5 -o /tmp/stream/pic.jpg -tl 100 -t 9999999 -th 0:0:0 &amp;amp; LD_LIBRARY_PATH=/usr/local/lib mjpg_streamer -i &amp;quot;input_file.so -f /tmp/stream -n pic.jpg&amp;quot; -o &amp;quot;output_http.so -w /usr/local/www&amp;quot;; do
	sleep 1
done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, build the container and give it an easy to remember tag:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;docker build -t mjpeg_container .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This should take a while, at the end your output should be similar to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[...]
Step 12/13 : COPY stream_from_pi.sh /
 ---&amp;gt; 2299b11e7696
Removing intermediate container de4317561efe
Step 13/13 : ENTRYPOINT /bin/bash /stream_from_pi.sh
 ---&amp;gt; Running in deff3a4ebe15
 ---&amp;gt; b5f669ccd45e
Removing intermediate container deff3a4ebe15
Successfully built b5f669ccd45e
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;step-6-run-the-docker-container&#34;&gt;Step 6: Run the docker container&lt;/h4&gt;

&lt;p&gt;Now for the fun part: running and testing the newly-built container.&lt;/p&gt;

&lt;p&gt;We want the Docker process to map the default streaming port to port 8081 of
our RPi host (Kodi&amp;rsquo;s own web interface occupies port 8080 by default):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; docker run -d --privileged -p 8081:8080 mjpeg_container
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And now the LibreELEC box will present the stream on port 8081. In a browser of choice, navigate to your IP address on port 8081,
for example &lt;code&gt;https//192.168.0.156:8081&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should see a welcome page like the following:

&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/mj.png/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/mj.png&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;

Your stream can be viewed under the stream tab.&lt;/p&gt;

&lt;h4 id=&#34;step-8-clean-up&#34;&gt;Step 8: Clean up&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Worth adding an &lt;code&gt;@reboot&lt;/code&gt; crontab to run the docker command if your box is subject to, uh, a lot
of reboots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before ending your ssh session:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;LibreELEC:~ # crontab -e
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make sure the file contains the following line:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@reboot docker run -d --privileged -p 8081:8080 mjpeg_container
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This tells the box to run the docker container at every reboot so that you don&amp;rsquo;t have to start it manually.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finally, disable SSH by going to &lt;code&gt;Settings -&amp;gt; Services&lt;/code&gt; in your Kodi navigation.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;This concludes the tutorial. Happy streaming!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Tor Relay Map</title>
      <link>https://a.custura.eu/post/relay-map/</link>
      <pubDate>Fri, 01 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/relay-map/</guid>
      <description>

&lt;p&gt;&lt;a href=&#34;https://a.custura.eu/relay-map.html&#34;&gt;Here is a map&lt;/a&gt;
that displays &lt;a href=&#34;https://www.torproject.org/&#34;&gt;Tor&lt;/a&gt; relays!&lt;/p&gt;

&lt;p&gt;The map also supports searching for the full or partial nickname, IP address fingerprint of a Tor relay.
You can also search for relays in
specific countries or ASes (&lt;code&gt;country:us&lt;/code&gt;,  &lt;code&gt;as:3&lt;/code&gt;), or with specific
flags (&lt;code&gt;flag:exit&lt;/code&gt; or &lt;code&gt;flag:authority&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To display all running relays, the search string is &lt;code&gt;running:true&lt;/code&gt;. An empty search string will plot all the relays.&lt;/p&gt;

&lt;p&gt;The Tor relay information is queried from &lt;a href=&#34;https://metrics.torproject.org/onionoo.htm&#34;&gt;Onionoo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This was written a while ago for the only purpose of trying out &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript&#34;&gt;JS&lt;/a&gt; and in the
future, this will have other open source
nodes added to it, including things like BSD mirrors, Bitcoin nodes etc. For now, the source can
be found &lt;a href=&#34;https://github.com/ana-cc/relay-map&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are several icons used to display the relays, their meanings can be found &lt;a href=&#34;https://people.torproject.org/~irl/icons/&#34;&gt;here&lt;/a&gt;. A legend is still on the TODO list.&lt;/p&gt;

&lt;h5 id=&#34;full-page-version-relay-map-html&#34;&gt;&lt;a href=&#34;https://a.custura.eu/relay-map.html&#34;&gt;Full-page version.&lt;/a&gt;&lt;/h5&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
   &lt;head&gt;
      &lt;link rel=&#34;stylesheet&#34; href=&#34;https://a.custura.eu/css/leaflet.css&#34;/&gt;

&lt;link rel=&#34;stylesheet&#34; href=&#34;https://a.custura.eu/css/font-awesome.min.css&#34;&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;https://a.custura.eu/css/font-logos.css&#34;&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;https://a.custura.eu/css/bootstrap.min.css&#34;&gt;
      &lt;script src=&#34;https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js&#34;&gt;&lt;/script&gt;
      &lt;script src=&#34;https://a.custura.eu/js/leaflet.js&#34;&gt;&lt;/script&gt;
      &lt;link rel=&#34;stylesheet&#34; href=&#34;https://a.custura.eu/css/MarkerCluster.css&#34;/&gt;
      &lt;link rel=&#34;stylesheet&#34; href=&#34;https://a.custura.eu/css/MarkerCluster.map.css&#34;/&gt;
      &lt;script src=&#34;https://a.custura.eu/js/leaflet.markercluster.js&#34;&gt;&lt;/script&gt;
      &lt;script src=&#34;https://a.custura.eu/js/leaflet.markercluster-src.js&#34;&gt;&lt;/script&gt;
      &lt;link rel=&#34;stylesheet&#34; href=&#34;https://a.custura.eu/css/easy-button.css&#34;&gt;
      &lt;script src=&#34;https://a.custura.eu/js/easy-button.js&#34;&gt;&lt;/script&gt;
   &lt;/head&gt;
   &lt;body&gt;

   &lt;/section&gt;
   &lt;div id=&#34;content&#34; class=&#34;container-fluid&#34;, style=&#34; display:block&#34;&gt;
            &lt;div id=&#34;controls&#34; style =&#34;padding-bottom:10px&#34;&gt;&lt;/div&gt;
      &lt;input title=&#34;Custom query text, e.g &#39;as=3&#39; or &#39;search=freeBogatov&#39;&#34;  id=&#34;qsrc&#34; placeholder=&#34;country:de&#34;&gt;
      &lt;label title=&#34;Custom query&#34; class=&#34;btn btn-primary&#34; onclick=&#39;markers($(&#34;#qsrc&#34;).val().trim())&#39;&gt;Custom Query&lt;/label&gt;
      &lt;label class=&#34;btn btn-danger&#34; title=&#34;Clear custom query&#34; onclick=&#34;clear_custom()&#34;&gt;&lt;i class=&#34;fa fa-times&#34;&gt;&lt;/i&gt;&lt;/label&gt;
      &lt;p id=&#34;info&#34;&gt;&lt;/p&gt;
      &lt;div id=&#34;unimap&#34; style=&#34;border:2px solid #a8a8a8; border-radius: 3px;&#34; &gt;&lt;/div&gt;
   &lt;/div&gt;

  &lt;script&gt;
  
 String.prototype.capitalize = function() {
      return this.charAt(0).toUpperCase() + this.slice(1);
  }
 var l_custom = new L.MarkerClusterGroup({
          chunkedLoading: true,
          chunkInterval: 350,
          iconCreateFunction: function(cluster) {
              return L.divIcon({
                  iconSize: [40, 40],
                  className: &#34;guard&#34;,
                  html: &#39;&lt;div class=&#34;inner-div&#34;&gt;&#39; + cluster.getChildCount() + &#39;&lt;/div&gt;&#39;
              });
          }
      });


  FLAGS = [&#39;guard&#39;, &#39;exit&#39;, &#39;authority&#39;];
  ICONS = [&#39;guard&#39;, &#39;exit&#39;, &#39;authority&#39;, &#39;exitfast&#39;, &#39;exitstable&#39;, &#39;guardstable&#39;, &#39;guardfast&#39;, &#39;fast&#39;, &#39;stable&#39;, &#39;relay&#39;, &#39;notrunning&#39;];
  ATTRIBUTES = [&#39;fast&#39;,&#39;stable&#39;];
  $.each(ICONS, function(i, d) { 
      window[d] = new L.Icon({
          iconUrl: &#34;/flags/&#34; + d + &#34;.png&#34;,
          iconSize: [31, 30]
      });
  });
  
  
  $(&#34;#unimap&#34;).height($(window).height());
  
  var mymap = L.map(&#39;unimap&#39;, {
      center: [+40, 0],
      zoomSnap: 0.5,
      zoomDelta: 0.5,
      zoom: 4 
  });
  
  L.tileLayer(&#39;https://{s}.tile.osm.org/{z}/{x}/{y}.png&#39;, {
      attribution: &#39;&amp;copy; &lt;a href=&#34;https://osm.org/copyright&#34;&gt;OpenStreetMap&lt;/a&gt; contributors&#39;
  }).addTo(mymap);
  
  L.easyButton(&#39;&lt;i class=&#34;fa fa-undo&#34;&gt;&lt;/i&gt;&#39;, function(btn, map) {
      map.setView([+20, 0], 2.5);
  }).addTo(mymap);
  
  
  function markers(query) {
      var myIcon = &#34;m_guard&#34;; 
      if (query != &#34;&#34;) {
        query = &#34;https://onionoo.torproject.org/details?search=&#34; + query + &#34;&amp;fields=nickname,platform,observed_bandwidth,latitude,longitude,flags,fingerprint,recommended_version,exit_probability,guard_probability,running&#34;;}
      else {
        query = &#34;https://onionoo.torproject.org/details?&amp;fields=nickname,platform,observed_bandwidth,latitude,longitude,flags,fingerprint,recommended_version,exit_probability,guard_probability,running&#34;;}
      
        mymap.removeLayer(l_custom);
        l_custom.clearLayers();
        $.getJSON(query)
            .done(function(data) {
                $(&#39;#info&#39;).html( &#34;The query returned &#34; + data.relays.length + &#34; relays.&#34;);
                var markerList = [];
                $.each(data.relays, function(d, e) {
                    if (e[&#34;latitude&#34;] != null) {
                        marker_string = get_marker(e);
                         var marker = L.marker([e[&#34;latitude&#34;], e[&#34;longitude&#34;]], {
                            icon: window[marker_string]
                        });
                        marker.bindPopup(
                            &#34;&lt;b&gt;Nickname:&lt;/b&gt; &#34; + e[&#34;nickname&#34;] +
                            &#34;&lt;br/&gt;&lt;b&gt;Platform:&lt;/b&gt; &#34; + e[&#34;platform&#34;] +
                            &#34;&lt;br/&gt;&lt;b&gt;Observed BW:&lt;/b&gt; &#34; + (e[&#34;observed_bandwidth&#34;] / (1024 * 8)).toFixed(2) + &#34; Kb/s&#34; +
                            &#34;&lt;br/&gt;&lt;b&gt;Up-to-date:&lt;/b&gt; &#34; + e[&#34;recommended_version&#34;] +
                            
                            &#34;&lt;br/&gt;&lt;b&gt;&lt;a href=\&#34;https://atlas.torproject.org/#details/&#34; + e[&#34;fingerprint&#34;] + &#34;\&#34;&gt;Atlas link&lt;/a&gt;&lt;/b&gt;&#34;
                        )
                        markerList.push(marker);
                    }
                });
                l_custom.addLayers(markerList);
                mymap.addLayer(l_custom);
            })
            .fail(function() {
                $(&#39;#info&#39;).html(&#34;The query failed, please check your syntax.&#34;);
            });
  }
  
  function clear_custom() {
      mymap.removeLayer(l_custom);
      l_custom.clearLayers();
  }

  function get_marker(e) {
    f = e[&#34;flags&#34;];
    var marker_string = &#34;&#34;;

    $.each(FLAGS, function(i, flag) {
       if($.inArray(flag.capitalize(), f) != -1) {
        marker_string += flag;
        }
    });
    if (marker_string == &#34;guardexit&#34;) {
      if (e[&#34;exit_probability&#34;] &lt; e[&#34;guard_probablity&#34;]) {
        marker_string = &#34;guard&#34;;}
      else {
        marker_string = &#34;exit&#34;;}
    }

    $.each(ATTRIBUTES, function(i, attr) {
    if($.inArray(attr.capitalize(), f) != -1) {
       marker_string += attr;
       return false;
    }
    }); 

    if (marker_string == &#34;authoritystable&#34;) {
        marker_string = &#34;authority&#34;;}
    if (e[&#34;running&#34;] == false) { 
      marker_string = &#34;notrunning&#34;;}
    if (marker_string == &#34;&#34;) {
        marker_string = &#34;relay&#34;;}
 return marker_string; 
 } 
markers(&#34;country:de&#34;)

  &lt;/script&gt;
 &lt;/body&gt;
&lt;/html&gt;

</description>
    </item>
    
    <item>
      <title>Zenburn Theme for the terminal</title>
      <link>https://a.custura.eu/post/zenburn-for-your-terminal/</link>
      <pubDate>Wed, 23 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/zenburn-for-your-terminal/</guid>
      <description>&lt;p&gt;So today I glanced down to my &lt;a href=&#34;https://i3wm.org/i3bar/&#34;&gt;i3 status bar&lt;/a&gt;, and thought &lt;em&gt;man those colours are fugly&lt;/em&gt;.
By default, these are white, fully saturated neon green and red on black:&lt;/p&gt;

&lt;p&gt;
&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/i3bar.jpg/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/i3bar.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;

&lt;em&gt;&amp;lsquo;5 minutes is enough to configure the colors for this, right?&amp;rsquo;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Some research and 2 hours down the rabbit-hole later, and everything from my text editor to the status bar is Zenburn-themed.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/jnurmine/Zenburn&#34;&gt;Zenburn&lt;/a&gt; is a low-contrast theme meant to ensure your eyes do not fall off
when you&amp;rsquo;re having that mexican-standoff with whatever project you happen to be &lt;a href=&#34;https://www.vim.org/&#34;&gt;Vimming&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But most of us spend a lot of time in text editors &lt;em&gt;and&lt;/em&gt; on the command line, and Zenburn is a theme for Vim only.
This useful and beautiful theme does not have an &lt;code&gt;.Xresources&lt;/code&gt;
file listed on the official page of &lt;a href=&#34;http://kippura.org/zenburnpage/&#34;&gt;Zenburn things&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As I&amp;rsquo;m a fan of cohesion, &lt;a href=&#34;https://gist.github.com/ana-cc/db86c09cd2c5788ba8e23a963a5d0a37&#34;&gt;this now exists&lt;/a&gt;, this blog post will explain how.&lt;/p&gt;

&lt;p&gt;First, I extracted the colors from the original &lt;code&gt;zenburn.vim&lt;/code&gt; using simple regex:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;matches = re.findall(r&#39;#(?:[0-9a-fA-F]{3}){1,2}&#39;, line)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I then used &lt;code&gt;yattag&lt;/code&gt;, a Python library for creating HTML, and displayed all the unique colors
from the file. It looks like this:&lt;/p&gt;

&lt;div &gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#c0bed1;&#34;&gt;#c0bed1&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#bc6c4c;&#34;&gt;#bc6c4c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#80d4aa;&#34;&gt;#80d4aa&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfdfbf;&#34;&gt;#dfdfbf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#4f4f4f;&#34;&gt;#4f4f4f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2b2b2b;&#34;&gt;#2b2b2b&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#bc8cbc;&#34;&gt;#bc8cbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#d0d0a0;&#34;&gt;#d0d0a0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#e89393;&#34;&gt;#e89393&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#82a282;&#34;&gt;#82a282&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#304a3d;&#34;&gt;#304a3d&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#444444;&#34;&gt;#444444&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#8f8f8f;&#34;&gt;#8f8f8f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2e3330;&#34;&gt;#2e3330&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#242424;&#34;&gt;#242424&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f8f893;&#34;&gt;#f8f893&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#41363c;&#34;&gt;#41363c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#88b090;&#34;&gt;#88b090&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f0f0c0;&#34;&gt;#f0f0c0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dccdcc;&#34;&gt;#dccdcc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#121212;&#34;&gt;#121212&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#313c36;&#34;&gt;#313c36&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#efef8f;&#34;&gt;#efef8f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#bc6c9c;&#34;&gt;#bc6c9c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2f2f2f;&#34;&gt;#2f2f2f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#464646;&#34;&gt;#464646&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#353535;&#34;&gt;#353535&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ecbcbc;&#34;&gt;#ecbcbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#71d3b4;&#34;&gt;#71d3b4&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dcdccc;&#34;&gt;#dcdccc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#7cac7c;&#34;&gt;#7cac7c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#222222;&#34;&gt;#222222&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#93b3a3;&#34;&gt;#93b3a3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#5b605e;&#34;&gt;#5b605e&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfdfdf;&#34;&gt;#dfdfdf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#000d18;&#34;&gt;#000d18&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#0f0f0f;&#34;&gt;#0f0f0f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#353a37;&#34;&gt;#353a37&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ffd7a7;&#34;&gt;#ffd7a7&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dc8c6c;&#34;&gt;#dc8c6c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dca3a3;&#34;&gt;#dca3a3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#383838;&#34;&gt;#383838&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#1f1f1f;&#34;&gt;#1f1f1f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#b2b2a0;&#34;&gt;#b2b2a0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#b6bf98;&#34;&gt;#b6bf98&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#e3ceab;&#34;&gt;#e3ceab&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#688060;&#34;&gt;#688060&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#709080;&#34;&gt;#709080&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#cc9393;&#34;&gt;#cc9393&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#434343;&#34;&gt;#434343&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfaf8f;&#34;&gt;#dfaf8f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#3a3a39;&#34;&gt;#3a3a39&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#acd0b3;&#34;&gt;#acd0b3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2c2e2e;&#34;&gt;#2c2e2e&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#bca3a3;&#34;&gt;#bca3a3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#efefaf;&#34;&gt;#efefaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#545a4f;&#34;&gt;#545a4f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#343434;&#34;&gt;#343434&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2c302d;&#34;&gt;#2c302d&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#000000;&#34;&gt;#000000&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#a0afa0;&#34;&gt;#a0afa0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#313633;&#34;&gt;#313633&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#6c6c9c;&#34;&gt;#6c6c9c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#8c8cbc;&#34;&gt;#8c8cbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#9fafaf;&#34;&gt;#9fafaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#efefef;&#34;&gt;#efefef&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#d0d0b8;&#34;&gt;#d0d0b8&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#9ccc9c;&#34;&gt;#9ccc9c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#8faf9f;&#34;&gt;#8faf9f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#3f3f3f;&#34;&gt;#3f3f3f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ffffe0;&#34;&gt;#ffffe0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ffffff;&#34;&gt;#ffffff&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#284f28;&#34;&gt;#284f28&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#664040;&#34;&gt;#664040&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ccd990;&#34;&gt;#ccd990&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#c3bf9f;&#34;&gt;#c3bf9f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfcfaf;&#34;&gt;#dfcfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#e37170;&#34;&gt;#e37170&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#1c1c1b;&#34;&gt;#1c1c1b&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#efdcbc;&#34;&gt;#efdcbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ccccbc;&#34;&gt;#ccccbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#233323;&#34;&gt;#233323&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#333333;&#34;&gt;#333333&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f18c96;&#34;&gt;#f18c96&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#404040;&#34;&gt;#404040&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#161616;&#34;&gt;#161616&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#262626;&#34;&gt;#262626&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f0f0b0;&#34;&gt;#f0f0b0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#cfcfaf;&#34;&gt;#cfcfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f0dfaf;&#34;&gt;#f0dfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#385f38;&#34;&gt;#385f38&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#040404;&#34;&gt;#040404&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#9ece9e;&#34;&gt;#9ece9e&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#cbecd0;&#34;&gt;#cbecd0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#434443;&#34;&gt;#434443&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfe4cf;&#34;&gt;#dfe4cf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#3f4040;&#34;&gt;#3f4040&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ccdc90;&#34;&gt;#ccdc90&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#3d3535;&#34;&gt;#3d3535&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#101010;&#34;&gt;#101010&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f0efd0;&#34;&gt;#f0efd0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ffcfaf;&#34;&gt;#ffcfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#7f9f7f;&#34;&gt;#7f9f7f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2e2e2e;&#34;&gt;#2e2e2e&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#181818;&#34;&gt;#181818&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#9f9f9f;&#34;&gt;#9f9f9f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#8cd0d3;&#34;&gt;#8cd0d3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#cfbfaf;&#34;&gt;#cfbfaf&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To get the colors to make a bit of sense, some sorting was in order.
I spent some time reading this &lt;a href=&#34;https://www.alanzucconi.com/2015/09/30/colour-sorting/&#34;&gt;excellent article about why sorting colours is a pain&lt;/a&gt;.
I then did some quick RGB to HSV conversion and sorting, and voilà:&lt;/p&gt;

&lt;div &gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#000000;&#34;&gt;#000000&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#040404;&#34;&gt;#040404&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#0f0f0f;&#34;&gt;#0f0f0f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#101010;&#34;&gt;#101010&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#121212;&#34;&gt;#121212&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#161616;&#34;&gt;#161616&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#181818;&#34;&gt;#181818&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#1f1f1f;&#34;&gt;#1f1f1f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#222222;&#34;&gt;#222222&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#242424;&#34;&gt;#242424&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#262626;&#34;&gt;#262626&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2b2b2b;&#34;&gt;#2b2b2b&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2e2e2e;&#34;&gt;#2e2e2e&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2f2f2f;&#34;&gt;#2f2f2f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#333333;&#34;&gt;#333333&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#343434;&#34;&gt;#343434&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#353535;&#34;&gt;#353535&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#383838;&#34;&gt;#383838&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#3f3f3f;&#34;&gt;#3f3f3f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#404040;&#34;&gt;#404040&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#434343;&#34;&gt;#434343&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#444444;&#34;&gt;#444444&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#464646;&#34;&gt;#464646&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#4f4f4f;&#34;&gt;#4f4f4f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#8f8f8f;&#34;&gt;#8f8f8f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#9f9f9f;&#34;&gt;#9f9f9f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfdfdf;&#34;&gt;#dfdfdf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#efefef;&#34;&gt;#efefef&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ffffff;&#34;&gt;#ffffff&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#3d3535;&#34;&gt;#3d3535&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#bca3a3;&#34;&gt;#bca3a3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ecbcbc;&#34;&gt;#ecbcbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dca3a3;&#34;&gt;#dca3a3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#cc9393;&#34;&gt;#cc9393&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#e89393;&#34;&gt;#e89393&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#664040;&#34;&gt;#664040&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#e37170;&#34;&gt;#e37170&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dccdcc;&#34;&gt;#dccdcc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#bc6c4c;&#34;&gt;#bc6c4c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dc8c6c;&#34;&gt;#dc8c6c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ffcfaf;&#34;&gt;#ffcfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfaf8f;&#34;&gt;#dfaf8f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#cfbfaf;&#34;&gt;#cfbfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ffd7a7;&#34;&gt;#ffd7a7&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#e3ceab;&#34;&gt;#e3ceab&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#efdcbc;&#34;&gt;#efdcbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfcfaf;&#34;&gt;#dfcfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f0dfaf;&#34;&gt;#f0dfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#c3bf9f;&#34;&gt;#c3bf9f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f0efd0;&#34;&gt;#f0efd0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#3a3a39;&#34;&gt;#3a3a39&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#1c1c1b;&#34;&gt;#1c1c1b&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dcdccc;&#34;&gt;#dcdccc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ccccbc;&#34;&gt;#ccccbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#b2b2a0;&#34;&gt;#b2b2a0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#d0d0b8;&#34;&gt;#d0d0b8&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ffffe0;&#34;&gt;#ffffe0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfdfbf;&#34;&gt;#dfdfbf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#cfcfaf;&#34;&gt;#cfcfaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f0f0c0;&#34;&gt;#f0f0c0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#d0d0a0;&#34;&gt;#d0d0a0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f0f0b0;&#34;&gt;#f0f0b0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#efefaf;&#34;&gt;#efefaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#efef8f;&#34;&gt;#efef8f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f8f893;&#34;&gt;#f8f893&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ccd990;&#34;&gt;#ccd990&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#ccdc90;&#34;&gt;#ccdc90&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#b6bf98;&#34;&gt;#b6bf98&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#dfe4cf;&#34;&gt;#dfe4cf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#545a4f;&#34;&gt;#545a4f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#688060;&#34;&gt;#688060&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#434443;&#34;&gt;#434443&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#a0afa0;&#34;&gt;#a0afa0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#82a282;&#34;&gt;#82a282&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#7f9f7f;&#34;&gt;#7f9f7f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#9ece9e;&#34;&gt;#9ece9e&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#9ccc9c;&#34;&gt;#9ccc9c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#7cac7c;&#34;&gt;#7cac7c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#233323;&#34;&gt;#233323&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#385f38;&#34;&gt;#385f38&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#284f28;&#34;&gt;#284f28&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#cbecd0;&#34;&gt;#cbecd0&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#acd0b3;&#34;&gt;#acd0b3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#88b090;&#34;&gt;#88b090&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2c302d;&#34;&gt;#2c302d&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2e3330;&#34;&gt;#2e3330&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#353a37;&#34;&gt;#353a37&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#313633;&#34;&gt;#313633&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#313c36;&#34;&gt;#313c36&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#93b3a3;&#34;&gt;#93b3a3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#8faf9f;&#34;&gt;#8faf9f&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#709080;&#34;&gt;#709080&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#304a3d;&#34;&gt;#304a3d&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#80d4aa;&#34;&gt;#80d4aa&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#5b605e;&#34;&gt;#5b605e&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#71d3b4;&#34;&gt;#71d3b4&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#3f4040;&#34;&gt;#3f4040&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#2c2e2e;&#34;&gt;#2c2e2e&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#9fafaf;&#34;&gt;#9fafaf&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#8cd0d3;&#34;&gt;#8cd0d3&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#000d18;&#34;&gt;#000d18&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#8c8cbc;&#34;&gt;#8c8cbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#6c6c9c;&#34;&gt;#6c6c9c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#c0bed1;&#34;&gt;#c0bed1&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#bc8cbc;&#34;&gt;#bc8cbc&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#bc6c9c;&#34;&gt;#bc6c9c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#41363c;&#34;&gt;#41363c&lt;/div&gt;&lt;div
style=&#34;display:inline-block;width:90px;height:40px;background-color:#f18c96;&#34;&gt;#f18c96&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This looks better and one can easily see what colors there are.&lt;/p&gt;

&lt;p&gt;The next stop was &lt;a href=&#34;https://terminal.sexy&#34;&gt;terminal.sexy&lt;/a&gt;, which is an amazing
terminal theme tool which allows you to vizualize and export your work easily.
Here&amp;rsquo;s our theme:

&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/terminal.png/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/terminal.png&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;
&lt;/p&gt;

&lt;p&gt;And here&amp;rsquo;s how it looks in the terminal:&lt;/p&gt;


&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/peek.png/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/peek.png&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;


&lt;p&gt;The i3 window colors, borders and status bar have now also been changed to match:&lt;/p&gt;


&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/i3bar-nice.jpg/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/i3bar-nice.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;


&lt;p&gt;And for completeness, &lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/zenburn-lc/&#34;&gt;here&amp;rsquo;s a Firefox theme&lt;/a&gt; to match, because we can:

&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/firefox.jpg/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/firefox.jpg&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;
&lt;/p&gt;

&lt;p&gt;Starting the new year ready to sysadmin for hours on end in an ill-lit room, happy hacking!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A tiny Nagios plugin to check DNSSEC RRSIG expiry</title>
      <link>https://a.custura.eu/post/check-RRSIG-nagios/</link>
      <pubDate>Wed, 14 Nov 2018 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/check-RRSIG-nagios/</guid>
      <description>&lt;p&gt;I have used both &lt;a href=&#34;https://icinga.com/products/icinga-2/&#34;&gt;Icinga&lt;/a&gt; and &lt;a href=&#34;https://www.nagios.org/&#34;&gt;Nagios&lt;/a&gt; to monitor a variety of infrastructures,
either at work, at home and anywhere in between.  These share a variety of very
useful &lt;a href=&#34;https://www.nagios.org/downloads/nagios-plugins/&#34;&gt;plugins&lt;/a&gt; for monitoring lots and lots of standard stuff, from basic ping
checks to filesystem mounts.  Occasionally I had to write the odd script to
monitor something ridiculously niche, like &lt;a href=&#34;https://tools.ietf.org/html/rfc2281&#34;&gt;split-brain HSRP status between pairs of Cisco routers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This one is a plugin for checking RRSIG expiry dates
on DNSSEC records. There are other plugins out there that do exactly this, but
I could not find anything that would not depend on a ton of other packages to
do it (libnet-dns-sec-perl anyone?).  So I decided to write something that has
minimal footprint, ended up with a python wrapper around dig. Of course, this
depends on dig (which should come with the plugin installation), as well as
python. The python part is optional if you rewrite this in bash.&lt;/p&gt;

&lt;p&gt;With regards to RRSIGs and DNSSEC:&lt;/p&gt;

&lt;p&gt;RRSIGs = signature records for a zone which
contain a cryptographic signature used for &lt;a href=&#34;http://www.rfc-editor.org/rfc/rfc4034.txt&#34;&gt;validating a DNSSEC response&lt;/a&gt;.
When you sign a zone, the signature records expire after 30 days. There are
recommendations for resigning a zone once a day. This is why the plugin default is
to warn if the signature expires in less than 29 days (of course warning and
critical thresholds can be changed with command line arguments -w and -c; the server to check against can also be specified with -s):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;~./check_dns_rrsig.py cloudflare.com
Signature expires in 1 days
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/ana-cc/nagios_plugins/blob/master/check_dns_rrsig.py&#34;&gt;Here is the plugin!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is very, very basic and will work if all you want is something to tell you
when an RRSIG expires, without any other overhead.&lt;/p&gt;

&lt;p&gt;Happy monitoring!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Unbound on a fresh FreeBSD install</title>
      <link>https://a.custura.eu/post/freebsd-unbound-issue/</link>
      <pubDate>Mon, 12 Nov 2018 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/freebsd-unbound-issue/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recenty encountered a weird problem where unbound would not work on a
fresh FreeBSD install on an APU3 board. Online research lead to finding a bunch
of posts complaining about this, but no leads. As it turns out, my problem was
the clock.&lt;/p&gt;

&lt;p&gt;But first things first. Installing FreeBSD on an APU board is very easy, at the boot shell enter the following commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;set boot_serial=YES
&amp;gt;set comconsole_speed=115200
&amp;gt;set console=comconsole
#this delays the boot by 10s to allow the USB controller to come up
&amp;gt;kern.cam.boot_delay=&amp;quot;10000&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course, remembering to drop into a shell at the end of the installation, and edit
&lt;code&gt;/boot/loader.conf&lt;/code&gt; so that we can use it over the console afterwards:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;boot_serial=&amp;quot;YES&amp;quot;
comconsole_speed=&amp;quot;115200&amp;quot;
console=&amp;quot;comconsole&amp;quot;
kern.cam.boot_delay=&amp;quot;10000&amp;quot;
amdtemp_load=&amp;quot;YES&amp;quot; #this loads the driver for the temperature sensor embedded in the CPU. It is equivalent to the km driver in OpenBSD. Yay sensors!
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, the APU board does not have an on-board clock. I did not set the time/date
during installation (lazy), as NTP was going to be set up anyway.  So, at this
stage in &lt;code&gt;/etc/ntp.conf&lt;/code&gt;, I replaced &lt;code&gt;pool 0.freebsd.pool.ntp.org iburst&lt;/code&gt; with the
name of my local NTP server.&lt;/p&gt;

&lt;p&gt;Reboot after successful installation, cue unbound not working. General DNS
failure. As unbound is DNSSEC enabled, run:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;unbound-anchor
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This &lt;a href=&#34;https://linux.die.net/man/8/unbound-anchor&#34;&gt;sets up the root trust anchor for DNSSEC validation&lt;/a&gt;, which was part of the solution to this issue.&lt;/p&gt;

&lt;p&gt;But unbound was still not working. Do not underestimate actually checking the results of any commands run:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cat /var/unbound/root.key
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The file contained no valid key, thus solving the mistery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DNS lookups do not work as there is no valid key here, due to our system clock being, well, off by a few years.&lt;/li&gt;
&lt;li&gt;And of course NTP does not sync the date&amp;hellip; as we gave it a hostname, which needs to be looked up in DNS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A quick &lt;code&gt;ntpdate &amp;lt;ip-address&amp;gt;&lt;/code&gt; followed by running &lt;code&gt;unbound-anchor&lt;/code&gt; again solves the issue.&lt;/p&gt;

&lt;p&gt;So frustrating.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Installing Debian via serial console</title>
      <link>https://a.custura.eu/post/Debian-via-serial-console/</link>
      <pubDate>Tue, 20 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/Debian-via-serial-console/</guid>
      <description>&lt;p&gt;I was recently tasked with installing an &lt;a href=&#34;https://pcengines.ch/apu2.htm&#34;&gt;apu2d&lt;/a&gt; board, a small board with enough performance and NICs to be used for network appliances - in my case, a simple gateway for a point-to-point link.&lt;/p&gt;

&lt;p&gt;This required me to do a Debian install first. The APU board has no
video outputs and is accesed with a null modem cable. I&amp;rsquo;ve never done an
install on the serial port and as soon as I selected an option from the
Debian GNU/Linux installer boot menu I was greeted with the following
error:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Undefined video mode number: 314 
Press &amp;lt;ENTER&amp;gt; to see video modes available, &amp;lt;SPACE&amp;gt; to continue, or wait 30 sec
Mode:  Resolution:  Type:
0 F00    80x25      CGA/MDA/HGC
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Selecting a mode resulted in the system freezing: the kernel is presumably
expecting a video output/keyboard input and crashes not finding
any. The system needs to be told it&amp;rsquo;s being installed via serial console, by
starting the kernel with some &lt;a href=&#34;https://wiki.archlinux.org/index.php/Kernel_parameters&#34;&gt;additional options&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pressing &amp;lsquo;TAB&amp;rsquo; allows you to edit the kernel parameters:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;gt; /install.amd/vmlinuz vga=788 initrd=/install.amd/initrd.gz --- quiet&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Changed the above to:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;gt; /install.amd/vmlinuz vga=off initrd=/install.amd/initrd.gz --- quiet console=ttyS0,115200n8&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&amp;hellip;where ttyS0 is the default serial device, and 115200 is the baud rate
required by the APU board. The &amp;lsquo;n&amp;rsquo; stands for &lt;a href=&#34;https://en.wikipedia.org/wiki/Serial_port#Settings&#34;&gt;parity&lt;/a&gt; &amp;lsquo;none&amp;rsquo; and the 8 is the
number of data bits.&lt;/p&gt;

&lt;p&gt;Optionally, vga can be changed to &lt;code&gt;vga=off&lt;/code&gt;, which makes the video
mode error go away, although the installer should start fine without it.&lt;/p&gt;

&lt;p&gt;The installation process is identical to the regular one, with an oldschool
looking installer:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://a.custura.eu/images/i1.png&#34; alt=&#34;Installer 1&#34; /&gt;
&lt;img src=&#34;https://a.custura.eu/images/i2.png&#34; alt=&#34;Installer 3&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Anyway, I wonder why the initial bootloader screen appears - is there
something my bootloader does to redirect console output to the serial console,
or is there a chip on board that has some video functionality?
For the latter, the CPU is my first suspect, although this is a riddle for another blogpost.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>IPv6 MTU issues</title>
      <link>https://a.custura.eu/post/mtu-issues/</link>
      <pubDate>Sun, 08 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/mtu-issues/</guid>
      <description>

&lt;p&gt;I have a &lt;a href=&#34;https://tunelbroker.net&#34;&gt;Hurricane Electric IPv6 tunnel&lt;/a&gt; that runs on my home &lt;a href=&#34;https://www.cisco.com/c/en/us/support/routers/887va-integrated-services-router-isr/model.html&#34;&gt;Cisco 887VA&lt;/a&gt;.
After setting the tunnel up, I was having issues connecting to IPv6-enabled
websites, which would never load consistently. If your first thought was &amp;ldquo;MTU blackholing&amp;rdquo;, then you weren&amp;rsquo;t far off. But it&amp;rsquo;s a bit more complicated than that.&lt;/p&gt;

&lt;p&gt;For those who don&amp;rsquo;t know, &lt;a href=&#34;https://en.wikipedia.org/wiki/Maximum_transmission_unit&#34;&gt;MTU&lt;/a&gt; refers to the maximum
size of packet that can be transmitted on a network for a specific protocol.
To work the value out, systems
use &lt;a href=&#34;https://en.wikipedia.org/wiki/Path_MTU_Discovery&#34;&gt;Path MTU Discovery&lt;/a&gt;.
For IPv6, PMTUD relies on &lt;em&gt;ICMPv6 type 2 - Packet too big&lt;/em&gt; messages
arriving from hosts in the path that have smaller MTUs.
When the sender receives a &lt;em&gt;Packet too big&lt;/em&gt; message, it adjusts its
own MTU size to reflect this, and packets larger than this value then get
fragmented.&lt;/p&gt;

&lt;p&gt;In real life, as ICMP/v6 is (sadly) &lt;a href=&#34;http://www.znep.com/~marcs/mtu/&#34;&gt;often blocked&lt;/a&gt; in the Internet,
you need to manually set the MTU for tunnels. In my case, this was already correctly
set to account for PPPoE and 6to4 encapsulation.&lt;/p&gt;

&lt;p&gt;Instead, the issue had to do with setting the &lt;a href=&#34;https://tools.ietf.org/html/rfc879&#34;&gt;TCP Maximum Segment Size&lt;/a&gt;, the largest possible size of a packet
payload which does not include the IP and TCP headers.&lt;/p&gt;

&lt;p&gt;Setting the &lt;a href=&#34;https://blog.thousandeyes.com/troubleshooting-path-mtu-tcp-mss-problems/&#34;&gt;TCP MSS&lt;/a&gt; ensures that the data payload of a
packet will fit within a desired MTU. For IPv4, it is usually set to be 40
bytes lower than the MTU value, as the IP + TCP headers are 40 bytes in
total. To set it, I was able to use the &lt;code&gt;ip tcp adjust-mss&lt;/code&gt; &lt;a href=&#34;https://www.cisco.com/c/en/us/td/docs/ios/12_2sb/12_2sba/feature/guide/sb_admss.pdf&#34;&gt;option&lt;/a&gt; on the
Cisco box. There was no equivalent command for IPv6.&lt;/p&gt;

&lt;p&gt;Researching the issue
revealed the version I was running at the time, 15.1(0), had no way of
adjusting TCP segment sizes for IPv6. My tunnel was doomed to behave inconsistently
unless I upgraded the firmware.&lt;/p&gt;

&lt;p&gt;The newer versions of IOS can do it. But it&amp;rsquo;s not what
you think: In versions since 15.2(4)M, the command &lt;code&gt;ip tcp adjust-mss&lt;/code&gt; applies
to both IPv4 and IPv6. Although they have&amp;hellip; different header sizes.
*sigh*&lt;/p&gt;

&lt;p&gt;The
tunnel started working after upgrading the firmware and making the change. Doing
so also introduced 20 bytes of overhead for all IPv4 TCP connections.&lt;/p&gt;

&lt;p&gt;But at least IPv6 works now.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&#34;epilogue&#34;&gt;Epilogue&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;It&amp;rsquo;s just gone past 8 PM.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Time to relax, to unwind, to eat some dinner and
watch some Netflix. But something&amp;rsquo;s wrong. Netflix refuses to play. It&amp;rsquo;s
complaining I&amp;rsquo;m using a proxy&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It appears that my London-based Hurricane Electric assigned IPv6 block is
geo-located in the US&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;With another sigh, I disable IPv6 in the browser, quietly
weeping in my bowl of &lt;del&gt;mac and cheese&lt;/del&gt; porridge&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;THE END&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>monroe-cli 1.0.1 released!</title>
      <link>https://a.custura.eu/post/monroe-cli-1/</link>
      <pubDate>Tue, 03 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>https://a.custura.eu/post/monroe-cli-1/</guid>
      <description>

&lt;p&gt;Mobile broadbands are a big part of Internet today, and the ubiquity of mobile devices
shapes the way we get online. The mobile broadband
infrastructure provides its own challenges, from to &lt;a href=&#34;https://en.wikipedia.org/wiki/5G&#34;&gt;heterogeneous access technologies&lt;/a&gt; to &lt;a href=&#34;http://www.cs.ucr.edu/~zhiyunq/pub/sigcomm11_netpiculet.pdf&#34;&gt;traffic shapers and firewalls&lt;/a&gt; buried under layers of NAT.&lt;/p&gt;

&lt;p&gt;The MONROE project, &lt;a href=&#34;https://monroe-system.eu&#34;&gt;Measuring Mobile Broadband Networks in Europe&lt;/a&gt;, provides a
distributed infrastructure for experimenting on mobile broadband networks. At
the time of writing, the platform comprises over 300 measurement servers, or
nodes, in Sweden, Norway, UK, Greece, Spain and Italy.&lt;/p&gt;

&lt;p&gt;All of the nodes within the MONROE platform are made available to researchers
on an Experiment-as-a-Service basis, via a scheduling interface with
client-side authentication, which looks like this:&lt;/p&gt;


&lt;figure &gt;
    &lt;a href=&#34;https://a.custura.eu/images/sched.png/&#34;&gt;
        &lt;img src=&#34;https://a.custura.eu/images/sched.png&#34; /&gt;
    &lt;/a&gt;
    
&lt;/figure&gt;


&lt;p&gt;The MONROE scheduler is feature rich, allowing users to customize experiments
in a variety of ways.  The experiments run on nodes in the platform in Docker
containers.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;monroe-cli&lt;/code&gt; provides both a library and a command-line tool for interacting with
the scheduler, without the point and click required by the web front end. It
supports all the features of the scheduler, such as experiment recurrence
parameters, country selection, start time, and optional experiment parameters.  It also
allows users check node availability and view, cancel and delete experiments,
as well as download experiment results.&lt;/p&gt;

&lt;p&gt;Over the last few days I&amp;rsquo;ve worked on monroe-cli version 1.0.1. This version
follows the changes in the &lt;a href=&#34;https://github.com/MONROE-PROJECT/Scheduler&#34;&gt;API&lt;/a&gt;,
and implements new features. Here are the highlights:&lt;/p&gt;

&lt;h3 id=&#34;support-for-node-models-and-number-of-network-interfaces&#34;&gt;Support for node models and number of network interfaces&lt;/h3&gt;

&lt;p&gt;As the project has moved on
to the second generation of MONROE nodes featuring &lt;a href=&#34;https://www.pcengines.ch/apu.htm&#34;&gt;apu2d&lt;/a&gt;
boards, both the library and the CLI now support setting up experiments on either new models
of nodes or nodes with a specific number of mobile operators.
These options can be set as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;monroe create &amp;lt;experiment&amp;gt; --new --ifcount 2&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&#34;library-support-for-low-priority-queue-experiments-lpq&#34;&gt;Library support for low priority queue experiments (LPQ)&lt;/h3&gt;

&lt;p&gt;The library now supports low-priority queue experiments, a new feature
which allows users to submit experiments without a given start time. These are deployed on-the-fly
when the selected nodes are up and free for enough time to run an experiment.
Usage example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from monroe.core import *
s = Scheduler(&#39;clientcert.pem&#39;,&#39;clientkey.pem&#39;)
exp = s.new_experiment()
exp.start(-1)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In order to use this feature in the CLI, the &lt;code&gt;&#39;--start&#39;&lt;/code&gt; argument should be set to &lt;code&gt;-1&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&#34;cleaner-command-line-client-with-better-default-options&#34;&gt;Cleaner command line client with better default options&lt;/h3&gt;

&lt;p&gt;The initial command line syntax had a few unnecessary optional arguments. The
default node type when creating an experiment is now &amp;lsquo;testing&amp;rsquo;, which is what
most researchers are expected to use. Also, creating an experiment now submits it by
default, instead of relying on &lt;code&gt;--submit&lt;/code&gt;. When downloading or
deleting experiments, the experiment ID is now a positional argument:
&lt;code&gt;monroe delete --exp &amp;lt;exp-id&amp;gt;&lt;/code&gt; becomes &lt;code&gt;monroe delete &amp;lt;exp-id&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same applies for setting up client certificates, which can now be done
using command &lt;code&gt;monroe setup &amp;lt;client-cert.pkcs12&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Perhaps the most important syntax change is the &lt;code&gt;--script&lt;/code&gt; argument, which indicates
the experiment&amp;rsquo;s Docker container, which is now a positional argument as well:
&lt;code&gt;monroe create acustura/monroe&lt;/code&gt; will submit a new experiment using Docker container &lt;code&gt;acustura/monroe&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&#34;documentation&#34;&gt;Documentation&lt;/h3&gt;

&lt;p&gt;Instructions on how to install the tool, and usage examples for the library and
the CLI have been added to the project, hopefully making it more accessible to
everyone.&lt;/p&gt;

&lt;h3 id=&#34;issues&#34;&gt;Issues&lt;/h3&gt;

&lt;p&gt;The one outstanding issue is that, currently, the client certificates required to authenticate are md5 signed.&lt;/p&gt;

&lt;p&gt;This means it is impossible to use &lt;code&gt;python-requests&lt;/code&gt;, which relies on OpenSSL, and
instead the library uses to not-so-graceful calls to &lt;code&gt;wget&lt;/code&gt;.  OpenSSL disabled support for
certificates signed with md5 as of version 1.1.0, so the CLI tool only works (that
I know of) on Debian machines, where &lt;code&gt;wget&lt;/code&gt; is compiled against GnuTLS.&lt;/p&gt;

&lt;p&gt;If you want to check, here&amp;rsquo;s how:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ldd /usr/bin/wget | egrep &amp;quot;(tls|ssl)&amp;quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As md5 is phased out from digital certificate signing, this is a temporary
workaround.  Here&amp;rsquo;s a &lt;a href=&#34;http://www.win.tue.nl/hashclash/rogue-ca/&#34;&gt;paper from 2008 (!)&lt;/a&gt; that points out md5 is harmful.
&lt;a href=&#34;https://www.mitls.org/pages/attacks/SLOTH&#34;&gt;More recent research&lt;/a&gt; describes
various attacks involving RSA-MD5 signatures, pointing out that keeping legacy
crypto in current protocols weakens them.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;If you&amp;rsquo;d like to check out monroe-cli, visit the &lt;a href=&#34;https://github.com/ana-cc/monroe-cli&#34;&gt;github page&lt;/a&gt;. The work is funded under EU Horizon
2020, agreement number 644399 . This support does not imply endorsement.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>