<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>The Grumpy Troll: The Grumpy Troll</title>
    <link>https://bridge.grumpy-troll.org/</link>
    <language>en-us</language>
    <author>Phil Pennock</author>
    <rights>Copyright &#xA9; 2010-2025 Phil Pennock.  All Rights Reserved.</rights>
    <updated>2024-02-23 21:30:00 -0500 -0500</updated>
    
    <item>
      <title>(Go)Hugo Generated Text File</title>
      <link>https://bridge.grumpy-troll.org/2024/02/hugo-generated-text-file/</link>
      <pubDate>Fri, 23 Feb 2024 21:30:00 -0500</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2024/02/hugo-generated-text-file/</guid>
      <description>&lt;p&gt;I migrated this blog to Hugo in 2013.
At the time, I wrote features for Hugo to support the migration.
For a while, I knew every feature of the software.
Those days are long past.
Much of what I knew, I have forgotten.&lt;/p&gt;
&lt;p&gt;I almost entirely just have post content, setup in a pattern which predates
the themes and so forth.  I have some static files, of course.  But when it
came to setting up a plain-text file outside of the blog area, but with the
content being generated with Go &lt;code&gt;text/template&lt;/code&gt;, I found it surprisingly hard
to find clear explicit guidance on how to do this.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s comparatively simple, once you know how.
And I belatedly realised that this makes it an appropriate topic for the blog.
How meta!&lt;/p&gt;
&lt;p&gt;You might use this for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;robots.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;humans.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;security.txt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and anything else which has to live at explicit paths.&lt;/p&gt;
&lt;p&gt;Except I don&amp;rsquo;t use &lt;code&gt;humans.txt&lt;/code&gt;; and &lt;code&gt;security.txt&lt;/code&gt; needs to be PGP-signed so
is static data where it exists; and &amp;hellip; hrm, there must be something other
than &lt;code&gt;robots.txt&lt;/code&gt;, right?  Anyway, moving swiftly on.&lt;/p&gt;
&lt;h3 id=&#34;motivation-robotstxt&#34;&gt;Motivation: &lt;code&gt;robots.txt&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;robots.txt&lt;/code&gt; file is perhaps the oldest fixed-format fixed-URL &amp;ldquo;standard&amp;rdquo; site pages of the World Wide Web.
It far predates &lt;code&gt;/.well-known/&lt;/code&gt;.
There are multiple parsers for it, as every crawler, of every search engine, can implement their own rules.
And the search engines change their rules over time.&lt;/p&gt;
&lt;p&gt;In particular, if someone wants to crawl what you&amp;rsquo;ve written, and you have a
way to tell them not to, then &amp;ldquo;little mistakes&amp;rdquo; or &amp;ldquo;oh we changed that&amp;rdquo;
updates which result in them being able to claim with a straight face that you
didn&amp;rsquo;t tell them &lt;em&gt;properly&lt;/em&gt;?  That&amp;rsquo;s a situation to be avoided.&lt;/p&gt;
&lt;p&gt;Thus for &lt;code&gt;robots.txt&lt;/code&gt; it pays to be proactively defensive and use the simplest possible served format.
No assumptions of batching rules.  No fanciness.&lt;/p&gt;
&lt;p&gt;But the moment you start writing the same text over and over again, typos will creep in.
I mean, my spieling is alwais purfect so it must be the auto-corruption of the virtual keyboard, right?&lt;/p&gt;
&lt;h3 id=&#34;hugo-textfile-setup&#34;&gt;Hugo Textfile Setup&lt;/h3&gt;
&lt;p&gt;I only noticed when writing this blog-post, but Hugo actually has a pre-registered output format explicitly for &lt;code&gt;robots.txt&lt;/code&gt;.
But only for that one specific text file.
I want this setup for any text file I might choose to generate.
So I&amp;rsquo;m sticking with what I configured because I can use it with more files in future.&lt;/p&gt;
&lt;p&gt;We &lt;em&gt;need&lt;/em&gt; one configuration change to be able to serve arbitrary text files, and then two files inside the tree for each file served.
For &lt;code&gt;robots.txt&lt;/code&gt; and auto-generation, I also added a &amp;ldquo;data file&amp;rdquo; to read as a data source.&lt;/p&gt;
&lt;p&gt;I use a YAML configuration file, so in my blog&amp;rsquo;s git repository&amp;rsquo;s root directory I have &lt;code&gt;config.yaml&lt;/code&gt;.
&lt;em&gt;(edit/2025-01: Newer versions of Hugo encourage a migration to &lt;code&gt;hugo.yaml&lt;/code&gt; instead)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In that, I have multiple &lt;code&gt;outputFormats&lt;/code&gt; but only one of interest here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;contentdir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;layoutdir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;layouts&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;publishdir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;public&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;outputFormats&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;txt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mediaType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;text/plain&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isPlainText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those first three directories are (or used to be?) standard, and will be covered
below.&lt;/p&gt;
&lt;p&gt;This &lt;code&gt;outputFormats&lt;/code&gt; entry says:
when the page metadata sets the &lt;code&gt;outputs&lt;/code&gt; list to contain &lt;code&gt;txt&lt;/code&gt;,
we want to serve it as &lt;code&gt;text/plain&lt;/code&gt;,
and we want the Hugo engine to use the Golang &lt;code&gt;text/template&lt;/code&gt; parser instead of the &lt;code&gt;html/template&lt;/code&gt; parser.&lt;/p&gt;
&lt;p&gt;Then to generate the target file, we need a stub markdown page and the logic in the layout page.&lt;/p&gt;
&lt;p&gt;Because &lt;code&gt;contentdir:&lt;/code&gt; is &lt;code&gt;&amp;quot;content&amp;quot;&lt;/code&gt;, my stub markdown page is: &lt;strong&gt;&lt;code&gt;content/robots.md&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nn&#34;&gt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Robots&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;robots.txt&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;page&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;robots&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;outputs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;txt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;I don&amp;rsquo;t think the &lt;code&gt;title&lt;/code&gt; really matters here, but it&amp;rsquo;s standard and available to templates.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;url:&lt;/code&gt; says &amp;ldquo;where to put this inside the &lt;code&gt;public/&lt;/code&gt; directory (the &lt;code&gt;publishdir:&lt;/code&gt; directory),
or when using &lt;code&gt;hugo serve&lt;/code&gt; it is more directly just the URL&lt;/li&gt;
&lt;li&gt;The types are used for grouping pages together, eg &lt;code&gt;blog&lt;/code&gt; or &lt;code&gt;post&lt;/code&gt; and is usually the first directory inside &lt;code&gt;content/&lt;/code&gt;;
since we&amp;rsquo;re not inside a directory, I&amp;rsquo;m just manually assigning something generic and &lt;code&gt;&amp;quot;page&amp;quot;&lt;/code&gt; is in fact what the default should be outside a directory.
So this is making an implicit value explicit.  You could skip it.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;layout:&lt;/code&gt; is important and is what ties into the next file, below.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;outputs:&lt;/code&gt; list says which output renderers to use.
&lt;ul&gt;
&lt;li&gt;If we had multiple here, they would all need to exist, but since we define one URL which is not a directory, only one will be used.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;url:&lt;/code&gt; did not have an extension (or ended in a &lt;code&gt;/&lt;/code&gt;) then the URL
would create a directory and files with basename &lt;code&gt;index&lt;/code&gt; would be created, each with an extension corresponding to the outputs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;There is no &amp;ldquo;content&amp;rdquo; after the front-matter.  This stub is pure front-matter.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we can write the template!&lt;/p&gt;
&lt;p&gt;The stub says that the page &lt;code&gt;robots.txt&lt;/code&gt; should use the &lt;code&gt;robots&lt;/code&gt; layout.
The &lt;code&gt;config.yaml&lt;/code&gt; says &lt;code&gt;layoutdir: &amp;quot;layouts&amp;quot;&lt;/code&gt;.
We are not doing anything special with additional rendering views on the same content (layouts) so we use the layout named &lt;code&gt;_default&lt;/code&gt;.
The output is &lt;code&gt;txt&lt;/code&gt; so we use that as our extension.&lt;/p&gt;
&lt;p&gt;So we create: &lt;strong&gt;&lt;code&gt;layouts/_default/robots.txt&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;&lt;code&gt;{{ layoutdir }}/_default/{{ chosen_layout_name }}.{{ output_extension}}&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And in &lt;em&gt;that&lt;/em&gt; file we can write Golang &lt;code&gt;text/template&lt;/code&gt; directives to construct
a repetitive file with the most unambiguous rendering we can come up with.&lt;/p&gt;
&lt;h3 id=&#34;my-robotstxt-template-and-data&#34;&gt;My &lt;code&gt;robots.txt&lt;/code&gt; template and data&lt;/h3&gt;
&lt;p&gt;I want highly structured input.
I&amp;rsquo;m already using YAML, as penance for my sins.
Hugo lets you put YAML, JSON, TOML, or XML (for those with even more sins than I)
in files in the &lt;code&gt;data/&lt;/code&gt; directory.
The loaded and parsed content of these files can then be accessed in templates via the &lt;code&gt;.Site.Data&lt;/code&gt; dictionary.&lt;/p&gt;
&lt;p&gt;After reading some documents such as
&lt;a href=&#34;https://www.eff.org/deeplinks/2023/12/no-robotstxt-how-ask-chatgpt-and-google-bard-not-use-your-website-training&#34;&gt;https://www.eff.org/deeplinks/2023/12/no-robotstxt-how-ask-chatgpt-and-google-bard-not-use-your-website-training&lt;/a&gt;
and
&lt;a href=&#34;https://github.com/samber/the-great-gpt-firewall&#34;&gt;https://github.com/samber/the-great-gpt-firewall&lt;/a&gt;
I had decided to go ahead and explicitly deny permission to AI systems from using my blog in future.&lt;/p&gt;
&lt;p&gt;I really truly want to make sure that those companies training AI by ignoring
copyright have no excuses, thus the simplified &lt;code&gt;robots.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So I created a file &lt;code&gt;data/ai_bots.yaml&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;GPTBot&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;OpenAI&amp;#39;s web crawler: GPT3.5, GPT4, ChatGPT&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ChatGPT-User&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ChatGPT plugins&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Google-Extended&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Google&amp;#39;s web crawler: Bard, VertexAI, Gemini&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;anthropic-ai&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Claude&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;CCBot&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Common Crawl&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the &lt;code&gt;description&lt;/code&gt; is for me and is not currently used, but
conceivably could be in other uses of this file.  Also, I&amp;rsquo;m torn on
&lt;code&gt;ChatGPT-User&lt;/code&gt; being included, since that&amp;rsquo;s for AI agents reaching out and
performing specific actions.&lt;/p&gt;
&lt;p&gt;But ultimately this is a blog and I have copyright over the content, it is not
an API which an AI could reasonably be interacting with, only plagiarising from.&lt;/p&gt;
&lt;p&gt;Then I created &lt;code&gt;layouts/_default/robots.txt&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# robots.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{ range $bot := $.Site.Data.ai_bots }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: {{ $bot.ua }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disallow: /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{- end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: *
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Allow: /
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;details&gt;
&lt;summary&gt;The resulting generated page is not much longer.&lt;/summary&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# robots.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: GPTBot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disallow: /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: ChatGPT-User
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disallow: /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: Google-Extended
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disallow: /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: anthropic-ai
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disallow: /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: CCBot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disallow: /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: *
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Allow: /
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;But that&amp;rsquo;s not the point.
I now have structure, consistency, and confidence that if I make a typo it
will be repeated everywhere so that I can&amp;rsquo;t help but notice it, instead of
quietly sabotaging subsets of my efforts to exclude AI bots.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Configuration Objects</title>
      <link>https://bridge.grumpy-troll.org/2024/01/configuration-objects/</link>
      <pubDate>Mon, 22 Jan 2024 01:45:00 -0500</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2024/01/configuration-objects/</guid>
      <description>&lt;p&gt;I&amp;rsquo;m going to pick holes in an approach which I think is actively harmful to
our industry and the maintainability of the systems we write and manage.
The problem is one of approach, and nothing is unfixable.&lt;/p&gt;
&lt;p&gt;The way is which YAML is used and abused is symptomatic of flaws in how we approach systems design and configuration.
This is not the fault of YAML, as whichever language we use would suffer the same fate.&lt;/p&gt;
&lt;p&gt;It is reasonable to not want to tie the configuration of a system to one language.&lt;/p&gt;
&lt;p&gt;It is reasonable to not want to force the use of interpreters for various engines deep into your stack;
to not want to make it impossible to add new flexibility later unless either some language maintainers agree with you,
or you want to take on maintaining a language interpreter yourself.&lt;/p&gt;
&lt;p&gt;When you choose to use a structured data format as the only input,
if the format is human editable then the danger is people try layering as little as possible onto that format at first,
and instead of getting a well designed data generator, you accrete generations of hacks.&lt;/p&gt;
&lt;h3 id=&#34;kubernetes&#34;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;For instance, it&amp;rsquo;s reasonable for Kubernetes to pick something like YAML as the object interchange,
but &lt;em&gt;only&lt;/em&gt; if you demonstrate from the beginning that YAML is only an object representation layer;
one with schemas available, and other good things;
and demonstrate this with examples of how to do things otherwise.&lt;/p&gt;
&lt;p&gt;Most Kubernetes admins should touch a tiny bit of YAML for initial bootstrap.
Thereafter, they should know it as &amp;ldquo;that format the state snapshots and backups are written in&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;But because a human editable representation was used, with the supplied tools not geared for taking other formats, people stick with YAML.
We see anchors and references, and then Helm becoming a Go &lt;code&gt;text/template&lt;/code&gt; wrapper with distribution mechanisms,
and incomprehensible messes as supplied Helm Charts either try to be all things to all people with a bazillion configuration options,
or say &amp;ldquo;learn to write patch objects, then you can change anything, we merge those in&amp;rdquo; and suddenly most admins &lt;em&gt;can&amp;rsquo;t&lt;/em&gt; tune their charts.&lt;/p&gt;
&lt;p&gt;If Protobufs had been used to represent the Kubernetes API objects,
it would have been less approachable &lt;em&gt;but&lt;/em&gt; it would have been much more obvious that the
configuration objects should be merely output artifacts from the configuration management for a set of clusters,
not the native language for humans to edit.&lt;/p&gt;
&lt;p&gt;I hope that in the future we look back on people using YAML with text
templating layers on top of it with the same cautious respect with which we
look at people writing raw Sendmail configs and bemoaning the use of macros by
newcomers who aren&amp;rsquo;t real mail administrators until they do.
I have a suspicion that attempts to nudge the Kubernetes culture away from
YAML will meet just that sort of reaction.&lt;/p&gt;
&lt;p&gt;And this is not specific to Kubernetes.
I see it with many things using YAML.
While YAML might have its problems (or &lt;code&gt;NO&lt;/code&gt; problems),
it&amp;rsquo;s broadly a decent human-editable way to represent some structured data.&lt;/p&gt;
&lt;h3 id=&#34;oci-image-references&#34;&gt;OCI Image References&lt;/h3&gt;
&lt;p&gt;People write references to OCI images and write &lt;code&gt;:latest&lt;/code&gt;,
and then they get told off because you don’t know what a given machine will see at any point in time.
It becomes a shibboleth, to tell people that &lt;code&gt;:latest&lt;/code&gt; is wrong, rather than fixing the problem, and define the in-group of service admins who &amp;ldquo;understand why you should never use &lt;code&gt;:latest&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The problem is that the configuration describing the overall desired state is being used
as the same configuration for telling the machines what to run &lt;strong&gt;now&lt;/strong&gt; when
they see the configuration.&lt;/p&gt;
&lt;p&gt;Loosely, the human using &lt;code&gt;:latest&lt;/code&gt; probably &lt;em&gt;really does want&lt;/em&gt; a given deploy to grab the latest,
but to have it be consistent across the deploy,
and subject to &lt;em&gt;being able to&lt;/em&gt; switch to a pinned version if needed,
and perhaps &lt;code&gt;:latest&lt;/code&gt; being interpreted as &amp;ldquo;the latest to pass our malware scanners, not the latest upstream&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The build step for deployment configurations &lt;em&gt;might&lt;/em&gt; be written in the same configuration language
as the &amp;ldquo;desired state when we do deploys&amp;rdquo; configurations,
but it is semantically different.
&lt;strong&gt;There needs to be a translation layer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Taking a desired state should resolve OCI image tags to checksums and lock down specific versions, without the human needing to care.
They should know that they can see what it was &lt;em&gt;for a given release/deploy&lt;/em&gt; but not be forced to manually maintain the lower layer configuration.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Configuration to describe what we want is a higher level of abstraction than configuration to be interpreted by individual machines during a deploy.
Using the exact same files and models for both is an anti-pattern.&lt;/p&gt;
&lt;p&gt;The runtime deploy configuration objects should have a schema which doesn&amp;rsquo;t allow for the human mutable labels which drift, perhaps mid-deploy.
The human edited objects should have LSP integrations letting you see how a label might be resolved now, and why,
or switching to &lt;strong&gt;how it was resolved, for a given past deploy&lt;/strong&gt;, referencing a history of the generated configurations.&lt;/p&gt;
&lt;p&gt;We should probably be using JSON or Protobufs or CBOR or &amp;ldquo;something&amp;rdquo; for the machine configurations.
Just about anything will do, except ASN.1 or XML (I have my prejudices, you have yours).
We don&amp;rsquo;t want comments there, or encouraging editing except in emergencies.&lt;/p&gt;
&lt;p&gt;YAML might be a fair simple representation as a starting-point for human editing,
but as long as there are solid schemas for the backend target, it should not be forced.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;other-posts&#34;&gt;Other posts&lt;/h3&gt;
&lt;p&gt;Readers have pointed me towards the following posts which are relevant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://leebriggs.co.uk/blog/2019/02/07/why-are-we-templating-yaml&#34;&gt;https://leebriggs.co.uk/blog/2019/02/07/why-are-we-templating-yaml&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Suggests &lt;a href=&#34;https://jsonnet.org/&#34;&gt;Jsonnet&lt;/a&gt; as a solution for the human layer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Updates (excluding other posts links):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Updated 2023-01-23 with YAML reference in intro for clarity and clarify the shibboleth&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Tailscale and Docker Remote</title>
      <link>https://bridge.grumpy-troll.org/2024/01/tailscale-docker-remote/</link>
      <pubDate>Sun, 14 Jan 2024 22:45:00 -0500</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2024/01/tailscale-docker-remote/</guid>
      <description>&lt;p&gt;I recently wanted a way for my team to run throw-away containers on a remote
box, for development and testing.  I did not want Kubernetes or a lot of
overhead, I just wanted the ability for people to throw containers on the box
and run them.  The only access control needed was &amp;ldquo;is in the list of people
allowed to talk to the container-runner service&amp;rdquo;: if you can talk to it, you
can run containers.  In host-networking mode, privileged, whatever.&lt;/p&gt;
&lt;p&gt;There are all sorts of fleet orchestration systems for running containers
across many nodes, but really I was after nothing more than &amp;ldquo;docker, but
remote&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Given a choice of Docker, how to secure the communications to the Docker API?
This is not about &amp;ldquo;the apps run by Docker&amp;rdquo;, as some of those might be public,
some not.
Each app will have its own stance, although we do want good defaults and
support for different models.
My concern was how to talk securely to Docker itself.&lt;/p&gt;
&lt;p&gt;At home I do this using my personal Certificate Authority, but we&amp;rsquo;ve been
strenuously resisting needing to run our own CA at work.  Let&amp;rsquo;s Encrypt
manages almost everything we need, and CertManager inside K8S manages ad-hoc
CAs with a limited audience as needed.&lt;/p&gt;
&lt;p&gt;And then I remembered Tailscale and
&lt;a href=&#34;https://boinkor.net/2023/07/tsnsrv-or-easily-accessing-services-on-your-tailscale-network/&#34;&gt;Andreas Fuchs&amp;rsquo;s tsnsrv&lt;/a&gt;
front-end proxy: connect a machine to Tailscale, run a proxy service, and use
Tailscale as the access control.  There are even HTTP headers added to the
proxied request, identifying the origin.  Roughly.  Exceptions apply with
multiple authentication mechanisms allowed, etc; the ID presented is not
absolutely trusted.  Real access control should be done with ACLs in the
Tailnet, and I do that, but it&amp;rsquo;s a nice extra.&lt;/p&gt;
&lt;p&gt;The proxy appears on your Tailnet as a new &amp;ldquo;machine&amp;rdquo;, with its own hostname.&lt;/p&gt;
&lt;p&gt;So, how hard could it be?&lt;/p&gt;
&lt;p&gt;Turns out, not very hard.  There was a fair bit of Ansible to write for our
dependencies, and a lot of debugging to get Docker working, but conceptually
it&amp;rsquo;s simple enough.  For a few days, it was more complicated, until I realised
that I could remove TLS from the picture.  The original deployment was a
single-evening task.&lt;/p&gt;
&lt;p&gt;This blog-post will include an example systemd service file, but none of the
rest of the Ansible we use.&lt;/p&gt;
&lt;h3 id=&#34;no-tls&#34;&gt;No TLS??&lt;/h3&gt;
&lt;p&gt;Tailscale is built around WireGuard as the network encryption layer.
WireGuard provides both encryption (ChaCha20) &lt;em&gt;and&lt;/em&gt; integrity protection
(Poly1305) between nodes on the network.&lt;/p&gt;
&lt;p&gt;Tailscale tracks your identity, according to various identity providers, and
lets you build ACLs inside Tailscale.&lt;/p&gt;
&lt;p&gt;So TLS provides no real additional benefit here.  It&amp;rsquo;s vaguely nice to have as
a doubled layer and to reduce possible questioning from auditors.&lt;/p&gt;
&lt;p&gt;Against that, the Docker CLI tool will only use TLS to talk to a remote
service if configured with a TLS client key and cert.  It doesn&amp;rsquo;t need to be
verifiable by the peer!  At first I just reused the existing key/cert from my
home CA, and that was sufficient.  A snake-oil cert (self-signed) would work
just fine, we only need to to convince Docker to speak HTTPS instead of HTTP.&lt;/p&gt;
&lt;p&gt;Setting up an additional snake-oil cert as a pre-requisite is a bunch of
needless ceremony and an obstacle to easy use.  So, just &amp;hellip; switch the proxy
to not require HTTPS and Docker is happy!  We don&amp;rsquo;t need to worry about
web-browsers which refuse &amp;ldquo;plaintext&amp;rdquo; here, we just need to know that the
traffic on the wire is protected.&lt;/p&gt;
&lt;h3 id=&#34;appearance-to-an-end-user&#34;&gt;Appearance to an end user&lt;/h3&gt;
&lt;p&gt;To use this, someone runs this setup step:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;tailnet_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;whatever-isyours&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tcp://docker-foo.&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;tailnet_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:?&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.ts.net:2375&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker context create work-foo &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --description&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;foo CloudNameHere ArchHere&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --docker&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;host=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and thereafter they can use &lt;code&gt;--context work-foo&lt;/code&gt; for the Docker CLI.  Or, more
likely, &lt;code&gt;export DOCKER_CONTEXT=work-foo&lt;/code&gt; and set that up via a &lt;em&gt;direnv(1)&lt;/em&gt; hook
to be set automatically as needed.&lt;/p&gt;
&lt;p&gt;For us, using a Linode in Chicago, that was a hostname of &lt;code&gt;docker-us-ord&lt;/code&gt; and
a context of &lt;code&gt;work-us-ord&lt;/code&gt; with a description of &lt;code&gt;&#39;us-ord Linode amd64&#39;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can find your Tailnet name at &lt;a href=&#34;https://login.tailscale.com/admin/dns&#34;&gt;https://login.tailscale.com/admin/dns&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you do need to use TLS, then it becomes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CP&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/path/to/keycert_without_suffix&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;tailnet_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;whatever-isyours&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tcp://docker-foo.&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;tailnet_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:?&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.ts.net:2376&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker context create work-foo &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --description&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;foo CloudNameHere ArchHere&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --docker&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;host=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;,cert=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$CP&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.crt,key=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$CP&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.key&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-deployment&#34;&gt;The Deployment&lt;/h2&gt;
&lt;p&gt;As a pre-requisite, you need a Tailscale account and some users.  Note that
they do have a generous free tier and you can probably get started without
paying anything.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://tailscale.com/&#34;&gt;https://tailscale.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;the-setup-once-written&#34;&gt;The setup once written&lt;/h3&gt;
&lt;p&gt;To add to a new host, you&amp;rsquo;d just add some variables to a host definition:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;vars&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;want_svc_docker&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;docker_tailsvc_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;docker-us-ord&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# Those first two are all that were really needed,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# the next two ... see below.&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tailsvc_verbose_logging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# docker_use_tsnsrv: true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and then run Ansible &amp;hellip; three times.  Alas.  Why?  Because I failed to use
pre-authentication keys.
Don&amp;rsquo;t be me.
Read &lt;a href=&#34;https://tailscale.com/kb/1085/auth-keys&#34;&gt;https://tailscale.com/kb/1085/auth-keys&lt;/a&gt; and work this into your Ansible
secrets management.  We will do this before we use this with a second host.
At least, I hope we will.&lt;/p&gt;
&lt;p&gt;You can even set the key to be pre-tagged, which will be &lt;em&gt;wonderful&lt;/em&gt; for
easing ACL setup for a given deployment role.&lt;/p&gt;
&lt;p&gt;So we&amp;rsquo;ve got a clean-up task if we decide we&amp;rsquo;re going to do more with this
approach, which I hope we will.  So far, the additional services have been run
&lt;em&gt;inside&lt;/em&gt; Docker using &lt;code&gt;$TS_AUTHKEY&lt;/code&gt; and I haven&amp;rsquo;t touched the Ansible rules.&lt;/p&gt;
&lt;h4 id=&#34;the-multiple-ansible-invocations-without-pre-auth-keys&#34;&gt;The Multiple Ansible Invocations Without Pre-Auth Keys&lt;/h4&gt;
&lt;p&gt;The first time, you run without the &lt;code&gt;docker_use_tsnsrv&lt;/code&gt; enabled.&lt;/p&gt;
&lt;p&gt;You then run, as root on the host:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# tailsvc is a Unix user, chosen to match systemd setup below&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tailscale up --operator&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tailsvc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That will give you a URL to visit.  Sign in with the authentication provider
you use, and accept the machine into the Tailnet.&lt;/p&gt;
&lt;p&gt;Then use &lt;a href=&#34;https://tailscale.com/kb/1028/key-expiry&#34;&gt;https://tailscale.com/kb/1028/key-expiry&lt;/a&gt; to disable key expiry.&lt;/p&gt;
&lt;p&gt;Then you uncomment &lt;code&gt;docker_use_tsnsrv&lt;/code&gt; and run Ansible.  This is the step
where you install the &lt;code&gt;tsnsrv&lt;/code&gt; proxy service.  It will run, but with rather
verbose logs.  This is needed because (at time of writing) the URL needed to
authenticate the proxy to the Tailnet is only printed in the verbose logs.
But you do the same authentication flow once more, and disable key expiry
again.&lt;/p&gt;
&lt;p&gt;This time you should also apply a Tag to the pseudo-machine, for use in
Tailscale ACLs.&lt;/p&gt;
&lt;p&gt;Then set &lt;code&gt;tailsvc_verbose_logging&lt;/code&gt; to false and deploy once more to clean up.&lt;/p&gt;
&lt;p&gt;This could shrink to two deploys, if the issue with the authentication URL
needing verbose logs is sorted out, and to a single deploy with pre-auth keys!&lt;/p&gt;
&lt;h3 id=&#34;the-systemd-service-file-template&#34;&gt;The systemd service file template&lt;/h3&gt;
&lt;p&gt;The following variables were needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tailscale_magicdns_domain&lt;/code&gt;: this is probably global within your deployment
scope&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tailsvc_name&lt;/code&gt;: this needs to be unique for each service using this setup&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tailsvc_plaintext&lt;/code&gt;: a bool to choose whether or not to live without the
TLS pain&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tailsvc_verbose_logging&lt;/code&gt;: because I didn&amp;rsquo;t use pre-auth keys and so needed
to be able to deploy with access to the logs to get the server auth URL.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tailscale_proxy_username&lt;/code&gt;, &lt;code&gt;tailscale_proxy_groupname&lt;/code&gt;,
&lt;code&gt;tailscale_proxy_homedir&lt;/code&gt;: for a Unix user to run the proxy, so we can
avoid running things as root.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% set logparam = &amp;#39;-tsnetVerbose=true&amp;#39; if tailsvc_verbose_logging else &amp;#39;&amp;#39; -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% if tailsvc_plaintext | bool -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% set port = &amp;#39;2375&amp;#39; -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% set listenspec %}-plaintext=true -listenAddr=:{{ port }}{% endset -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% else -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% set port = &amp;#39;2376&amp;#39; -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% set listenspec %}-listenAddr=:{{ port }}{% endset -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% endif -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{% set svcurl %}http://{{ tailsvc_name }}.{{ tailscale_magicdns_domain }}:{{ port }}{% endset -%}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[Unit]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Description=Tailscale Proxy for Docker
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;After=network-online.target docker.service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[Service]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Type=simple
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ExecStart=/usr/local/bin/tsnsrv -name {{ tailsvc_name }} {{ logparam }} {{ listenspec }} -upstreamUnixAddr /run/docker.sock {{ svcurl }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User={{ tailscale_proxy_username }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Group={{ tailscale_proxy_groupname }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# A bunch of config is written to .config/tsnet-tsnsrv/ in the homedir (/home/tailsvc)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# We do need access to the Docker control socket.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ProtectHome=tmpfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BindPaths={{ tailscale_proxy_homedir }}  /run/docker.sock
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NoNewPrivileges=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;MemoryDenyWriteExecute=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;LockPersonality=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PrivateDevices=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ProtectKernelTunables=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ProtectKernelModules=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ProtectControlGroups=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;RestrictSUIDSGID=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ProtectSystem=strict
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PrivateTmp=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[Install]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Alias=tailsvc-docker.service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;WantedBy=multi-user.target
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{# vim: set filetype=systemd.jinja : #}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;the-acls&#34;&gt;The ACLs&lt;/h3&gt;
&lt;p&gt;While tailsvc is able to present identity in HTTP headers,
Docker isn&amp;rsquo;t going to use those and there&amp;rsquo;s no ACL management available there.&lt;/p&gt;
&lt;p&gt;But visit &lt;a href=&#34;https://login.tailscale.com/admin/acls/file&#34;&gt;https://login.tailscale.com/admin/acls/file&lt;/a&gt; and,
if you&amp;rsquo;re ready to carve up your Tailnet,
then you might define a &lt;code&gt;group:myteam&lt;/code&gt; and then in the &lt;code&gt;acls[]&lt;/code&gt; list include
an item:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;group:myteam&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-icing-on-the-cake&#34;&gt;The Icing on the Cake&lt;/h2&gt;
&lt;p&gt;If you have firewall rules on the host, then you might reference the IP
address ranges used by Tailscale to bypass most protections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;100.64.0.0/10&lt;/code&gt;: RFC 6598 Shared Address Space&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fd7a:115c:a1e0::/96&lt;/code&gt;: random allocation in RFC 4193 Unique Local IPv6 Unicast Addresses&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;tailscale_tailnet_cidrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;100.64.0.0/10&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;fd7a:115c:a1e0::/96&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then you might allow ports 80 and 443 through from the world, but no other
ports, and suddenly folks can run things in Docker and expose on other ports
and those run services will only be reachable for people connected to your
Tailnet!&lt;/p&gt;
&lt;p&gt;Now, some might use that to skip authentication again and end up with a soft
chewy inside for your VPN &amp;ldquo;internal network&amp;rdquo;, but it&amp;rsquo;s on you and your
policies to keep that from happening.&lt;/p&gt;
&lt;p&gt;When running tsnet using services, those are inherently Tailscale-only; but
for developing a new public service which isn&amp;rsquo;t ready to be public quite yet,
having a toybox deployment host for containers where folks can run quick
experiments without a lot of overhead, this visibility constraint is ideal.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;feedback&#34;&gt;Feedback&lt;/h2&gt;
&lt;h3 id=&#34;acls-include-ports-use-daemonjson&#34;&gt;ACLs include ports, use daemon.json&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.petekeen.net/&#34;&gt;Pete Keen&lt;/a&gt; notes that, depending upon access
requirements, a simpler solution which might be available is to configure
Docker to listen on the tailnet IP in its &lt;code&gt;daemon.json&lt;/code&gt; file and then use ACL
tags to only grant access to the &lt;code&gt;2375&lt;/code&gt;/&lt;code&gt;2376&lt;/code&gt; ports to my team.&lt;/p&gt;
&lt;p&gt;This is great and has me wondering if I should have made different choices
about how deployments can offer service to the company.  :)&lt;/p&gt;
&lt;p&gt;This won&amp;rsquo;t work for my deployment, because our model is that ports 80 and 443
are open to the world (and apps which want access control have to provide it)
and every other port is open to the Tailnet, so simple apps can just provide
external service on a port, and anyone in the company, on our Tailnet, can
reach the app.&lt;/p&gt;
&lt;p&gt;The problem is that the
&lt;a href=&#34;https://tailscale.com/kb/1018/acls&#34;&gt;Tailscale ACL language only offers an accept action&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tailscale access rules are “default deny”, so the only possible &lt;code&gt;action&lt;/code&gt; is
&lt;code&gt;accept&lt;/code&gt;, to allow traffic that would otherwise be denied.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;If there were&lt;/strong&gt; a Deny action available, then we could switch to that and just
trust that the ACLs are well-maintained.  We would use something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Not supported by Tailscale at this time
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;group:myteam&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:22&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:2375&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:2376&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;deny&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;autogroup:member&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:22&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:2375&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:2376&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;autogroup:member&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and then have Ansible pick up the Tailscale IPs for the docker &lt;code&gt;daemon.json&lt;/code&gt;
file, and manage that file via Ansible.&lt;/p&gt;
&lt;p&gt;If we had &amp;ldquo;the 80/443 service is for internal usage only and everyone outside
the team has to go through that&amp;rdquo;, then we could still use this, and just grant
&lt;code&gt;&amp;quot;autogroup:member&amp;quot;&lt;/code&gt; access to &lt;code&gt;[&amp;quot;tag:docker:80&amp;quot;,&amp;quot;tag:docker:443&amp;quot;]&lt;/code&gt; and all
would be well.&lt;/p&gt;
&lt;p&gt;What we &lt;em&gt;could&lt;/em&gt; do is this, since Pete pointed out that ACL rules can match
port ranges too:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;group:myteam&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:22&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:2375&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker:2376&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;autogroup:member&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker-companyopen:1-21&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker-companyopen:23-2374&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tag:docker-companyopen:2377-65535&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;but by that point things are looking fragile enough that the &amp;ldquo;use &lt;code&gt;tsnsrv&lt;/code&gt; as
a proxy to present as a different machine&amp;rdquo; looks much nicer to maintain.&lt;/p&gt;
&lt;p&gt;So yes, I might revisit, but I &lt;em&gt;think&lt;/em&gt; I like the model I ended up with, of
80/443 being public-to-Internet and everything else internal.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Small Mailserver Best Current Practices</title>
      <link>https://bridge.grumpy-troll.org/2020/07/small-mailserver-bcp/</link>
      <pubDate>Fri, 24 Jul 2020 16:45:00 -0400</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2020/07/small-mailserver-bcp/</guid>
      <description>&lt;p&gt;&lt;em&gt;(This post was originally written as a reply on the mailop mailing-list, but
a friend asked me to turn it into a blog post.  I&amp;rsquo;ve edited it, mostly
adding more links to elsewhere, but there are some additions here.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Context: someone with a mail-server hosted in a German facility with a
poor reputation for handling abuse reports was asking for help on
sending email to their Gmail-using friends; they had SPF and didn&amp;rsquo;t see
the point of DKIM; they had TLS setup for their mail-server, using a
certificate from CACert.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a PDF from Google from 2006 which is still worth reading:
&lt;a href=&#34;https://research.google.com/pubs/archive/45.pdf&#34;&gt;https://research.google.com/pubs/archive/45.pdf&lt;/a&gt;, entitled
&amp;ldquo;Sender Reputation in a Large Webmail Service&amp;rdquo; by Bradley Taylor.
Anyone running a mail-server today needs to read that document.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t send much email, then the only IP-based reputation which
Google can assess you on is the reputation of your address-block, so
being in a &amp;ldquo;troublesome&amp;rdquo; hosting provider will score heavily against
you.  At that point, if not moving away, you need to try to balance out
that negative score with enough positives that any of the large
providers using reputation scoring will accept the mail.&lt;/p&gt;
&lt;p&gt;Working forward-and-reverse paired DNS is even more important for IPv6
than for IPv4; for better or worse, some of the large providers have
decided that exemptions in old standards for old behavior should not
apply when folks deploy standards which are far newer.  So you
absolutely need an &lt;code&gt;MX&lt;/code&gt; record, you must not just rely upon
address-records (&lt;code&gt;A&lt;/code&gt; and &lt;code&gt;AAAA&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;With a poor IP-based reputation, you need to see if you can score a
better domain-based reputation.  This is where DKIM comes into play:
once you can provably link a message to really be from a given domain,
then even if you don&amp;rsquo;t send much mail you can benefit from stuff like
&amp;ldquo;not on day-old-bread domain-lists&amp;rdquo;.  But having DKIM and then a DMARC
record does help (and &lt;a href=&#34;https://bridge.grumpy-troll.org/2014/04/dmarc-stance/&#34;&gt;I&amp;rsquo;m no fan of DMARC&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;For the mail-server&amp;rsquo;s TLS: for that to count in your favor instead of
being a wash, I strongly suspect that it needs to be a certificate which
senders can verify.  For those people scoring up for &amp;ldquo;better TLS&amp;rdquo;, those
senders using DANE will be happy with a TLSA record in DNSSEC for your
CACert anchor.  But the large webmail providers are Resistant to having
to deploy DNSSEC verification, so instead have pushed out an alternative
called MTA-STS.  With MTA-STS, you&amp;rsquo;re tied into &amp;ldquo;whichever subset of CAs
all the large senders you care about will trust&amp;rdquo;, and then using that CA
for the certificates both for the MTA-STS web-server and for your
mail-server.  Note that you don&amp;rsquo;t need to implement the client logic for
MTA-STS (and I think it&amp;rsquo;s antithetical to an open federated platform)
but do need to just publish the static information for those senders who
do use it. At that point, CACert is not going to cut it.  You&amp;rsquo;d need to
try Let&amp;rsquo;s Encrypt instead.&lt;/p&gt;
&lt;p&gt;The ongoing natural tendency from larger providers is to favor
supporting what the majority of their users want the majority of the
time.  With so many people using larger providers, they naturally tilt
towards stuff which works with the larger senders, and requiring more
hoops.  Those additional hoops create more work for smaller providers
and self-hosters doing thing manually.&lt;/p&gt;
&lt;p&gt;We need better automation tools around all of this.  The below will make
it clearer why.&lt;/p&gt;
&lt;p&gt;So, here is my current understanding of the best current practices here,
in reality not IETF idealism.  This includes making mandatory stuff
which some folks insist must be optional, because realistically to send
to some large providers it&amp;rsquo;s not optional.  This list includes features
to make you compatible with ongoing trends in the EU (particularly
Germany) to strongly disfavor allowing clear-text SMTP.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is broad best practices, targeting more than just Gmail.
Eg, DANE/DNSSEC will be ignored by the large webmail providers,
but will matter with some other providers.&lt;/em&gt;
[clarification added later]&lt;/p&gt;
&lt;p&gt;This assumes that you are &lt;em&gt;not&lt;/em&gt; a large sender who should also be
setting up feedback loops, learning how to &amp;ldquo;warm&amp;rdquo; IPs, considering BIMI,
postmaster tooling domain verification, etc.&lt;/p&gt;
&lt;h3 id=&#34;deliverability-fixes&#34;&gt;Deliverability fixes&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;reverse DNS with matching forward DNS; the name used should not
pattern-match anything generic and ideally would include a DNS label
of &lt;code&gt;mail&lt;/code&gt; or &lt;code&gt;mx&lt;/code&gt; or the like in it.&lt;/li&gt;
&lt;li&gt;MX record, always.&lt;/li&gt;
&lt;li&gt;Accurate SPF;
&lt;ul&gt;
&lt;li&gt;ideally not too broad; pay attention to SPF&amp;rsquo;s query limits.&lt;/li&gt;
&lt;li&gt;avoid &lt;code&gt;-all&lt;/code&gt; at the end because, with the sole exception of &amp;ldquo;this
domain never sends email&amp;rdquo; records, the larger operators have metrics to
show that using &lt;code&gt;-all&lt;/code&gt; &lt;em&gt;tends&lt;/em&gt; to be a sign of over-enthusiasm rather
than reality, so it will slightly count against you;&lt;/li&gt;
&lt;li&gt;remember to have an SPF record for your &lt;code&gt;HELO&lt;/code&gt; hostname, because
when you send a &amp;ldquo;bounce&amp;rdquo; rejection, this is the thing which will be
looked up (since there&amp;rsquo;s no domain in &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DKIM set up, with thought towards the selector namespace.
&lt;ul&gt;
&lt;li&gt;RSA2048 key is effectively a hard-requirement
&lt;ul&gt;
&lt;li&gt;DNS TXT records consist of one or more DNS strings, each of which
is limited to 255 ASCII characters.  For a key of this size, you
will end up needing two DNS strings in the zonefile.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ed25519 keys are not yet widely supported, but by now are not
likely to actively break and make things worse for you, if you
dual-sign.  This needs to be a different selector.&lt;/li&gt;
&lt;li&gt;Note that for various good reasons you should design this to be
something you routinely rotate.
&lt;ul&gt;
&lt;li&gt;Some folks use yearly, some monthly&lt;/li&gt;
&lt;li&gt;I rotate every three months.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DMARC record; see &lt;a href=&#34;https://tools.ietf.org/html/rfc7489&#34; title=&#34;Domain-based Message Authentication, Reporting, and Conformance (DMARC)&#34;&gt;RFC 7489&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;But for domains which humans send from &lt;em&gt;don&amp;rsquo;t&lt;/em&gt; use
&lt;code&gt;p=quarantine&lt;/code&gt; or &lt;code&gt;p=reject&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Do consider setting up a receiver for reports, just so that you can
see how much of a privacy breach DMARC reporting is when you send
to mailing-lists which don&amp;rsquo;t re-sign. :-/&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TLS certificate from a CA in the main trust anchor bundles;
&lt;ul&gt;
&lt;li&gt;Just use Let&amp;rsquo;s Encrypt.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MTA-STS web-server with HTTPS certificate from the same CA, and the
relevant MTA-STS txt file in place; add the DNS record when it&amp;rsquo;s up
and happy.  See &lt;a href=&#34;https://tools.ietf.org/html/rfc8461&#34; title=&#34;SMTP MTA Strict Transport Security (MTA-STS)&#34;&gt;RFC 8461&lt;/a&gt;.
&lt;ul&gt;
&lt;li&gt;See &lt;a href=&#34;https://esmtp.email/tools/mta-sts/&#34;&gt;https://esmtp.email/tools/mta-sts/&lt;/a&gt; for a testing validation tool
(with thanks to Luis Muñoz for the pointer).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;For the independent mail providers using the stuff broadly supported
in open source MTAs, you should look at DNSSEC, because the patterns
here are less susceptible to rent-seeking pressures:
&lt;ul&gt;
&lt;li&gt;DNSSEC-signed zone for your own domain
&lt;ul&gt;
&lt;li&gt;Use whichever signing algorithm CloudFlare are currently using:
this should be both current for cryptography and widely enough
supported that if it&amp;rsquo;s not supported by someone&amp;rsquo;s resolver, then
they have bigger problems than just your domain.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DNSSEC validating resolver for you to look up records of others
(consider &lt;a href=&#34;https://nlnetlabs.nl/projects/unbound/about/&#34;&gt;Unbound&lt;/a&gt; or &lt;a href=&#34;https://www.knot-resolver.cz/&#34;&gt;Knot Resolver&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;DANE records for your own domain (TLSA records in DNS)
&lt;ul&gt;
&lt;li&gt;See &lt;a href=&#34;https://tools.ietf.org/html/rfc7672&#34; title=&#34;SMTP Security via Opportunistic DNS-Based Authentication of Named Entities (DANE) Transport Layer Security (TLS)&#34;&gt;RFC 7672&lt;/a&gt; for the SMTP details.&lt;/li&gt;
&lt;li&gt;See &lt;a href=&#34;https://tools.ietf.org/html/rfc6698&#34; title=&#34;The DNS-Based Authentication of Named Entities (DANE) Transport Layer Security (TLS) Protocol: TLSA&#34;&gt;RFC 6698&lt;/a&gt; for the base spec with &lt;a href=&#34;https://tools.ietf.org/html/rfc7218&#34; title=&#34;Adding Acronyms to Simplify Conversations about DNS-Based Authentication of Named Entities (DANE)&#34;&gt;RFC 7218&lt;/a&gt; for some common
acronyms which make talking about it easier.&lt;/li&gt;
&lt;li&gt;See &lt;a href=&#34;https://tools.ietf.org/html/rfc7671&#34; title=&#34;The DNS-Based Authentication of Named Entities (DANE) Protocol: Updates and Operational Guidance&#34;&gt;RFC 7671&lt;/a&gt; for the updates and operational guidance.&lt;/li&gt;
&lt;li&gt;There are other RFCs, for SRV records and for OpenPGP, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tell your mail-server to obey DANE stuff, so that if there&amp;rsquo;s a TLSA
record in DNSSEC-verified DNS then the mail-server can disable
fallback to cleartext for delivery to MX (and ideally also then
verify the TLS connection has a cert chain which is anchored in one
of the TLSA records)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://dnsviz.net&#34;&gt;https://dnsviz.net&lt;/a&gt; is your friend&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_smtp._tls&lt;/code&gt; record so you can get reports of TLS failures sending to
you&lt;/li&gt;
&lt;li&gt;Seeing if you can get your IP onto one of the open DNS-based
allow-lists (also called &amp;ldquo;whitelists&amp;rdquo; but some folks are moving away
from that term), such as &lt;a href=&#34;https://www.dnswl.org/&#34;&gt;https://www.dnswl.org/&lt;/a&gt; or Spamhaus&amp;rsquo;s SWL.&lt;/li&gt;
&lt;li&gt;Periodically check if you appear in any DNS-based deny-lists.&lt;/li&gt;
&lt;li&gt;Make sure you&amp;rsquo;re not sending from &amp;ldquo;ISP residential address-space&amp;rdquo;; if
need be route your mail outbound via a host in better address-space
(and update SPF etc to match)&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t do sender call-out verification to SMTP servers which aren&amp;rsquo;t
yours.&lt;/li&gt;
&lt;li&gt;For your own sanity, do make sure you set up &lt;a href=&#34;https://www.fail2ban.org/&#34;&gt;fail2ban&lt;/a&gt;, or something
like it, scanning your mail-server logs, because SMTP AUTH online
cracking is widespread.  If they ever get in, your deliverability
will be negatively impacted by their spam campaign through your
mail-server.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;convenience-stuff&#34;&gt;Convenience Stuff&lt;/h3&gt;
&lt;p&gt;Outside of &amp;ldquo;Phil&amp;rsquo;s BCP&amp;rdquo; above, additional non-deliverability but
convenience options include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;DNS SRV records for submission(s)/imap(s)/pop3(s)/sieve, even if just
to say with «&lt;code&gt;0 0 0 .&lt;/code&gt;» that it&amp;rsquo;s not supported.&lt;/li&gt;
&lt;li&gt;If your communications base includes people using OpenPGP with email,
then set up WKD to publish OpenPGP keys for your domain too.
&lt;ul&gt;
&lt;li&gt;This is just a fixed schema for laying out keys for HTTPS retrieval.&lt;/li&gt;
&lt;li&gt;See the &lt;a href=&#34;https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/?include_text=1&#34; title=&#34;OpenPGP Web Key Directory&#34;&gt;WKD draft&lt;/a&gt; for details.&lt;/li&gt;
&lt;li&gt;I wrote &lt;a href=&#34;https://github.com/PennockTech/openpgpkey-control&#34;&gt;https://github.com/PennockTech/openpgpkey-control&lt;/a&gt; as a
management framework for an organization; the
&lt;code&gt;other/standalone-update-website&lt;/code&gt; script is designed to be
embeddable into an existing site-building workflow without anything
else from the repository.&lt;/li&gt;
&lt;li&gt;The GnuPG project has tooling available which manages the WKD layout as
an email-integrated workflow, for people to update their own keys.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If your communications base includes people using S/MIME then set up
SMIMEA records in your DNSSEC-signed DNS.
&lt;ul&gt;
&lt;li&gt;They look a lot like TLSA records; both are trust anchors in DNS.&lt;/li&gt;
&lt;li&gt;See &lt;a href=&#34;https://tools.ietf.org/html/rfc8162&#34; title=&#34;Using Secure DNS to Associate Certificates with Domain Names for S/MIME&#34;&gt;RFC 8162&lt;/a&gt; for details.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The moment you start specifying &amp;ldquo;must be TLS-secured&amp;rdquo; it&amp;rsquo;s worth
adding &lt;code&gt;CAA&lt;/code&gt; records into DNS, so that Certificate Authorities which
are broadly trusted will refuse to issue for your domain unless you
list them.
&lt;ul&gt;
&lt;li&gt;See &lt;a href=&#34;https://tools.ietf.org/html/rfc8659&#34; title=&#34;DNS Certification Authority Authorization (CAA) Resource Record&#34;&gt;RFC 8659&lt;/a&gt; for details&lt;/li&gt;
&lt;li&gt;The values checked by each Certificate Authority as indicating they
have permission are required to be listed in their Certification
Practice Statement, as part of the CA/Browser forum&amp;rsquo;s Baseline
Requirements.  If it&amp;rsquo;s missing, then browsers are not supposed to
be trusting that CA.&lt;/li&gt;
&lt;li&gt;For domain-validation CAs such as Let&amp;rsquo;s Encrypt, consider adding
account information to those records to tie it to your specific
account.  See &lt;a href=&#34;https://tools.ietf.org/html/rfc8657&#34; title=&#34;Certification Authority Authorization (CAA) Record Extensions for Account URI and Automatic Certificate Management Environment (ACME) Method Binding&#34;&gt;RFC 8657&lt;/a&gt; for details.&lt;/li&gt;
&lt;li&gt;Beware that at time of writing, Let&amp;rsquo;s Encrypt only honors the
&lt;code&gt;accounturi&lt;/code&gt; restriction in their staging environment, not their
production setup; this will likely change.&lt;/li&gt;
&lt;li&gt;Remember that DNS zonefiles support comments.  You&amp;rsquo;ll want them
here!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://wiki.libravatar.org/api/&#34;&gt;https://wiki.libravatar.org/api/&lt;/a&gt; for Gravatar-style images in some
MUAs (with caching for privacy reasons); this is a fixed schema HTTPS
layout and a DNS record.  I think almost nobody uses this but I don&amp;rsquo;t
have usage figures.  If you have a Claws user, perhaps?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A lot of the DNS items above can be found in a
&lt;a href=&#34;https://github.com/StackExchange/dnscontrol/blob/master/commands/test_data/example.org.zone&#34;&gt;sample DNS zonefile&lt;/a&gt;
contributed to the &lt;a href=&#34;https://stackexchange.github.io/dnscontrol/&#34;&gt;DNSControl&lt;/a&gt; project for their regression test suite,
after my production zone broke their converters.
DNS hosting platforms which try to over-simplify will make some of the
above hard.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve written this as a reply so it&amp;rsquo;s the stuff I can remember / spot
now, so I might be missing something big or &amp;ldquo;too obvious to me&amp;rdquo;.  Other
tech exists.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;addenda&#34;&gt;Addenda&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;A second reason to run a local DNS resolver is that if you are checking any
DNS-based access lists then you should not do so via public resolvers.  All
mail-server clusters or stand-alone systems really should have a dedicated
DNS resolver for their use.  Since you need the resolver to exist anyway,
you might as well make it a validating resolver and get DNSSEC verification
too, per the above.  &lt;em&gt;[2020-08-19]&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Shell Locality</title>
      <link>https://bridge.grumpy-troll.org/2020/07/shell-locality/</link>
      <pubDate>Thu, 16 Jul 2020 11:45:00 -0400</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2020/07/shell-locality/</guid>
      <description>&lt;p&gt;When a shell function declares a variable to be &lt;code&gt;local&lt;/code&gt; and then unsets it,
does the name return to being global in scope or is it still &amp;ldquo;known&amp;rdquo; to be
local, even though unset (via some kind of tombstone mechanism, perhaps)?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s test it.  Spoiler: the results vary.&lt;/p&gt;
&lt;p&gt;Note that POSIX does not provide &lt;code&gt;local&lt;/code&gt;, this is a shell extension.&lt;/p&gt;
&lt;h3 id=&#34;the-test&#34;&gt;The Test&lt;/h3&gt;
&lt;p&gt;Here is our test as a pasteable one-liner:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; s&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;: x: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$((&lt;/span&gt;i+1&lt;span class=&#34;k&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;outside&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; s&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; foo&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; s&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; x&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; s&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;inside&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;s&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;unset&lt;/span&gt; x&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;s&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;unset&lt;/span&gt; x&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; s&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; foo&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Breaking that down:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;                &lt;span class=&#34;c1&#34;&gt;# loop-counter for our &amp;#34;show&amp;#34; function so we can see the steps&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;s&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;              &lt;span class=&#34;c1&#34;&gt;# a simple show function, using POSIX $((...)) for arithmetic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;: x: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nv&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$((&lt;/span&gt;i+1&lt;span class=&#34;k&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;outside    &lt;span class=&#34;c1&#34;&gt;# our test variable is x and we give it a maskable value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;s            &lt;span class=&#34;c1&#34;&gt;# 0 -- prior state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;foo&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  s          &lt;span class=&#34;c1&#34;&gt;# 1 -- inside function, before local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; x    &lt;span class=&#34;c1&#34;&gt;# from this point on, if `local` exists, x is non-global&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  s          &lt;span class=&#34;c1&#34;&gt;# 2 -- does the act of making the variable local clear its value?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nv&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;inside   &lt;span class=&#34;c1&#34;&gt;# our &amp;#34;inside&amp;#34; value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  s          &lt;span class=&#34;c1&#34;&gt;# 3 -- confirming value is set and variables work&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;unset&lt;/span&gt; x    &lt;span class=&#34;c1&#34;&gt;# reset x ... but what is left behind?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  s          &lt;span class=&#34;c1&#34;&gt;# 4 -- should see an empty string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;unset&lt;/span&gt; x    &lt;span class=&#34;c1&#34;&gt;# unknown: does this unset the global?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  s          &lt;span class=&#34;c1&#34;&gt;# 5 -- final display inside the function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;foo      &lt;span class=&#34;c1&#34;&gt;# we need to call the function, since only zsh has anonymous functions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;s            &lt;span class=&#34;c1&#34;&gt;# 6 -- outside the function, show the global after double unset&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;the-results&#34;&gt;The Results&lt;/h3&gt;
&lt;h4 id=&#34;zsh&#34;&gt;Zsh&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3: x: inside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;local&lt;/code&gt; keyword resets the value of the variable (counter 2).&lt;/li&gt;
&lt;li&gt;The second &lt;code&gt;unset&lt;/code&gt; did not unset the global (counter 6).&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;bash-5&#34;&gt;Bash (5)&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3: x: inside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;[edited to add Bash options:]&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Bash behaves the same as zsh, by default.
There is an option &lt;code&gt;localvar_inherit&lt;/code&gt; to change this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;shopt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;localvar_inherit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;foo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;outside&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;outside&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;outside&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;inside&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;outside&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is also an option &lt;code&gt;localvar_unset&lt;/code&gt; which impacts a more complicated form
than we&amp;rsquo;re addressing here.&lt;/p&gt;
&lt;h4 id=&#34;dash&#34;&gt;Dash&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3: x: inside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;local&lt;/code&gt; keyword in dash does NOT reset the visible value, but does make
future modifications only visible within local scope;
this matches Bash with &lt;code&gt;localvar_inherit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The second &lt;code&gt;unset&lt;/code&gt; did not unset the global.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;ksh&#34;&gt;Ksh&lt;/h4&gt;
&lt;p&gt;This test was of the shell from &lt;code&gt;apk add -U loksh&lt;/code&gt; inside an Alpine 3.12
Docker container, which installed version &lt;code&gt;6.7.1-r0&lt;/code&gt; of &lt;code&gt;loksh&lt;/code&gt;, described as
“A Linux port of OpenBSD&amp;rsquo;s ksh”.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3: x: inside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6: x:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;local&lt;/code&gt; keyword in ksh is immediately resetting the visible value.&lt;/li&gt;
&lt;li&gt;The second &lt;code&gt;unset&lt;/code&gt; inside the function &lt;strong&gt;did unset the global variable!&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;freebsd-sh&#34;&gt;FreeBSD sh&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3: x: inside
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5: x:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6: x: outside
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the same as zsh and bash&amp;rsquo;s default.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Writing portable shell is hard.  Duh.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Slack Tax</title>
      <link>https://bridge.grumpy-troll.org/2019/12/slack-tax/</link>
      <pubDate>Thu, 12 Dec 2019 19:20:00 -0500</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2019/12/slack-tax/</guid>
      <description>&lt;p&gt;Chambers of commerce should be urgently talking with the FTC to try to stave
off an imminent forced tax on their members, by Slack.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;NB: I have no vested interest in any company mentioned here, unless my
retirement index-tracker fund has done so, in which case I&amp;rsquo;m probably hurting
myself by writing this.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Slack as a company makes Slack, the product.  It’s for team communications and
basic use is free but there are paid tiers which bill per active user. They’re
scrupulously fair about tracking as users become inactive and issuing billing
credits so that you don&amp;rsquo;t pay if people aren&amp;rsquo;t using it.&lt;/p&gt;
&lt;p&gt;The Slack product is good. Some years back when it was new, I mistakenly
objected to our company trying Slack to solve “get everyone using a team chat”
because we&amp;rsquo;d tried two other products and only the engineers would actually
use them, so I believed the problems were cultural and shuffling deck chairs
by trying the latest trendy chat tool was avoiding addressing the real
problem. I was persuaded to give it one more try. I was wrong: the ease of use
and fun quirkiness of Slack made it enticing enough that the entire company
got on board. For a while it solved our problems (right up until dropping the
gateways for IRC &amp;amp; XMPP killed the accessibility solutions our blind employee
was using).&lt;/p&gt;
&lt;p&gt;If a company wants to use Slack, that&amp;rsquo;s their call to make. Competition is
good and freedom to choose matters. There are competitors (eg Mattermost; or
Microsoft Teams) which creates an open marketplace.&lt;/p&gt;
&lt;p&gt;The problem comes when suddenly a company has no choice and in order to be
viable they are forced to buy a particular product, licensed per user. Much
like MS Office was effectively mandatory and changing document formats kept
locking out competitors, Slack is making that transition with a new paid
feature.&lt;/p&gt;
&lt;p&gt;Barrons extracted the critical part from a recent Slack blog post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I&amp;rsquo;ve been making network-based software for a living for the last 20
years,” Butterfield wrote. “Over that time I&amp;rsquo;ve worked on a lot of great
projects directly, and have seen many more from the inside, as an investor,
board member or just as a friend. I have never been more excited about a
product or its product market fit than I am about shared channels.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Shared Channels let you link together Slack teams. Within a company, great.
Between companies … it&amp;rsquo;s attractive. But you can only link together Slack
teams. It is not an open protocol and competitors are not welcome. There&amp;rsquo;s no
fundamental reason why you can&amp;rsquo;t bridge to other products but that&amp;rsquo;s not
compatible with this Silicon Valley investment.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve seen Shared Channels in beta used by a company to set up links with
customers and vendors. Important business links get Slack links. There might
be only two or three people routinely in such a channel, enough to avoid a
single person dependency, but there&amp;rsquo;s an implicit expectation from both sides
that when there&amp;rsquo;s a problem, other needed people can be brought in quickly to
help solve it.&lt;/p&gt;
&lt;p&gt;Shared Channels are undeniably useful, although there are cultural mores which
need to be established to prevent abuse by larger companies treating employees
of their business partners as their own, to delegate work to. Any company
about to set up a direct synchronous communication system open potentially to
“all their employees” and “all employees of all important business partners”
needs to take seriously the problems that can arise from that. Synchronous
communication is disruptive and it takes solid ground rules to keep
productivity from being damaged by just a few people who don&amp;rsquo;t follow cultural
expectations. When not all the people are your own employees, that will get
harder.&lt;/p&gt;
&lt;p&gt;The core problem though is that with Shared Channels there is effectively no
choice. Slack has a critical mass of customers and Shared Channels turns that
into a network effect, locking out competition. Who will pay for two team
communication products, or try to maintain one paid and one free, when they
can “just” use one? You can&amp;rsquo;t sanely just use Slack for a few liaison people
because much of the value is the ability to pull in other people to solve
problems in a hurry.&lt;/p&gt;
&lt;p&gt;So with Shared Channels being a paid tier feature which excludes other
companies&amp;rsquo; team chat, and the expectation being that all employees are on the
same team chat, suddenly we don&amp;rsquo;t have a good product winning in an open
marketplace. Instead, we have the current market leader setting up a major
barrier to competition and locking themselves in, while making sure that each
company trying to choose just has to use what all their business partners are
using.&lt;/p&gt;
&lt;p&gt;Those monthly per-user fees start looking a lot like a tax on doing business.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s an easy fix: ensure that Shared Channels use a documented protocol and
that Slack teams are freely able to link to other products, not just teams
hosted by Slack.&lt;/p&gt;
&lt;p&gt;-&lt;i&gt;The Grumpy Troll&lt;/i&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Git Aliases &amp; Shell</title>
      <link>https://bridge.grumpy-troll.org/2018/07/git-aliases-shell/</link>
      <pubDate>Sun, 22 Jul 2018 21:20:00 -0400</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2018/07/git-aliases-shell/</guid>
      <description>&lt;p&gt;Today I took a look at one particular git repository&amp;rsquo;s configuration and saw
something slightly off in the configuration for a credential helper, dating
from an old experiment with AWS CodeCommit.  I decided to dig deeper to figure
out what the actual rules are for shell commands inside git configuration
files.&lt;/p&gt;
&lt;p&gt;This side-diversion took a bit longer than expected.  It&amp;rsquo;s a Sunday.  Ah well.
I&amp;rsquo;ve seen too much cargo-culted incorrect information online, so it was time
to figure out an accurate answer.  If you see Leaning Toothpick Syndrome
markedly increasing the count of backslashes, then a little suspicion is
warranted.&lt;/p&gt;
&lt;p&gt;In fairness to those who are confused, including me before today: the git
logic tries to implement magic “do the right thing” fix-ups at a level beneath
the &lt;code&gt;GIT_TRACE&lt;/code&gt; reporting, so that it&amp;rsquo;s pretty hidden until you either read
the source or use a &lt;code&gt;strace(1)&lt;/code&gt; style of tool.  This makes the simple cases
right, even when they&amp;rsquo;re wrong, but makes the fancier cases obtuse.&lt;/p&gt;
&lt;p&gt;I decided to focus on looking into how &lt;code&gt;[alias]&lt;/code&gt; rules are handled, on the
(correct) assumption that it&amp;rsquo;s the same mechanisms involved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For clarity: avoid cleverness inside configuration files!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If the steps stated here matter, then the complexity really should be moved
out of &lt;code&gt;~/.gitconfig&lt;/code&gt; and into a separate helper script.  When you start
having to worry about where strings are parsed and how they&amp;rsquo;re handled in
which situations, the correct response is to back away slowly, not breaking
eye-contact, and create a very short script to handle things instead.&lt;/p&gt;
&lt;p&gt;Notwithstanding correctness, sometimes you need to know how to tame the beast.&lt;/p&gt;
&lt;h3 id=&#34;git-config-documentation&#34;&gt;&lt;code&gt;git config&lt;/code&gt; documentation&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s start by reading the actual documentation; this is found in
&lt;code&gt;git-config(5)&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;alias.*&lt;/code&gt;&lt;br&gt;
Command aliases for the &lt;code&gt;git(1)&lt;/code&gt; command wrapper - e.g. after defining &lt;code&gt;alias.last = cat-file commit HEAD&lt;/code&gt;, the invocation &lt;code&gt;git last&lt;/code&gt; is equivalent to &lt;code&gt;git cat-file commit HEAD&lt;/code&gt;. To avoid confusion and troubles with script usage, aliases that hide existing Git commands are ignored. Arguments are split by spaces, the usual shell quoting and escaping is supported. A quote pair or a backslash can be used to quote them.&lt;/p&gt;
&lt;p&gt;If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. For example, defining &lt;code&gt;alias.new = !gitk --all --not ORIG_HEAD&lt;/code&gt;, the invocation &lt;code&gt;git new&lt;/code&gt; is equivalent to running the shell command &lt;code&gt;gitk --all --not ORIG_HEAD&lt;/code&gt;. Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory.  GIT_PREFIX is set as returned by running git rev-parse &amp;ndash;show-prefix from the original current directory. See git-rev-parse(1).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&amp;rsquo;s a decent start but things go wrong mysteriously when we try to do
something non-trivial.  A simple for-loop &lt;em&gt;appears&lt;/em&gt; to fail at the &lt;code&gt;&amp;quot;$@&amp;quot;&lt;/code&gt;
stage, but why?&lt;/p&gt;
&lt;p&gt;Also under “Syntax”:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The following escape sequences (beside &lt;code&gt;\&amp;quot;&lt;/code&gt; and &lt;code&gt;\\&lt;/code&gt;) are recognized:
&lt;code&gt;\n&lt;/code&gt; for newline character (NL), &lt;code&gt;\t&lt;/code&gt; for horizontal tabulation (HT, TAB)
and &lt;code&gt;\b&lt;/code&gt; for backspace (BS).
Other char escape sequences (including octal escape sequences) are invalid.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;shell-side-digression&#34;&gt;Shell side-digression&lt;/h3&gt;
&lt;p&gt;Something which will become important below is a quirky corner of POSIX shell
handling.  Despite POSIX giving us the convention of &lt;code&gt;--&lt;/code&gt; to terminate option
processing and treat following parameters as non-option entries, when you
invoke &lt;code&gt;sh -c FOO -- arg1&lt;/code&gt; you are &lt;em&gt;not&lt;/em&gt; using &lt;code&gt;--&lt;/code&gt; to terminate options.&lt;/p&gt;
&lt;p&gt;Instead, &lt;a href=&#34;http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html&#34;&gt;per the specification&lt;/a&gt;,
in &lt;code&gt;-c&lt;/code&gt; handling, the first non-option parameter is the string providing the
input to be parsed for shell commands (here &lt;code&gt;FOO&lt;/code&gt;) and the next parameter
provides the name of the command!  It&amp;rsquo;s &lt;code&gt;argv[0]&lt;/code&gt; pass-through.
Thus &lt;code&gt;sh -c FOO -- arg1&lt;/code&gt; will invoke a shell, with &lt;code&gt;$0&lt;/code&gt; equal to &lt;code&gt;--&lt;/code&gt;, &lt;code&gt;$1&lt;/code&gt; of
&lt;code&gt;arg1&lt;/code&gt; and then parse and handle &lt;code&gt;FOO&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After the first non-option, no other strings are examined to see if they might
be options; there is no permutation.&lt;/p&gt;
&lt;p&gt;Thus &lt;code&gt;sh -c FOO BAR -u&lt;/code&gt; does not risk &lt;code&gt;-u&lt;/code&gt; telling the shell to treat unset
variable references as errors.  Instead, &lt;code&gt;FOO&lt;/code&gt; is invoked in a context
where &lt;code&gt;argv=[&amp;quot;BAR&amp;quot;, &amp;quot;-u&amp;quot;]&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;what-actually-happens-with-git&#34;&gt;What actually happens with git&lt;/h3&gt;
&lt;p&gt;Git&amp;rsquo;s config parser and the shell-or-other invocation are the two layers to
worry about.&lt;/p&gt;
&lt;p&gt;The config parser uses both &lt;code&gt;;&lt;/code&gt; and &lt;code&gt;#&lt;/code&gt; as comment markers, so an &lt;code&gt;;&lt;/code&gt; if
unquoted will terminate things.  Given that &lt;code&gt;;&lt;/code&gt; is a sub-list terminator in
shell syntax, this is a little unfortunate.&lt;/p&gt;
&lt;p&gt;A shell is not necessarily used when an alias is treated as a shell command.
That&amp;rsquo;s very careful documentation wording, above.  “it will be treated as a
shell command” does not mean that a shell will be used, merely that it&amp;rsquo;s a
shell command to be handled.  Git will often resort to using a shell, but
it&amp;rsquo;s not a commitment to do so.&lt;/p&gt;
&lt;p&gt;Git parses the configuration file handling basic stripping of comments and
handling of the &lt;code&gt;\n&lt;/code&gt; substitutions and quoting, before the alias mechanisms
come into play.&lt;/p&gt;
&lt;p&gt;Git can split a string into separate fields, for invoking as a command,
without needing to go near a shell to do so.  The &lt;code&gt;alias.c:split_cmdline()&lt;/code&gt;
function handles this, splitting on whitespace while not splitting within
quoted strings.  It handles single and double-quotes, with double-quotes
supporting a backslash escape purely for avoiding breaking out of quoted
state.  No other escapes are supported, &lt;strong&gt;but the configuration parsing has
already handled some&lt;/strong&gt;.  You can have:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[alias]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;na&#34;&gt;foo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;!&amp;#34;bar\nbaz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and the value of stored for the alias &lt;code&gt;foo&lt;/code&gt; will be:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;!bar
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;baz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead, the quotes handling for &lt;code&gt;split_cmdline&lt;/code&gt; are for quotes inside the
original quotes; this is what lets us write:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[alias]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;na&#34;&gt;foo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;!printf &amp;#39;%s\\n&amp;#39; first \&amp;#34;sec ond\&amp;#34; third&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and invoke:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;GIT_TRACE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt; git foo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;20:50:18.430074 git.c:654               trace: exec: git-foo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;20:50:18.430779 run-command.c:637       trace: run_command: git-foo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;20:50:18.432151 run-command.c:637       trace: run_command: &amp;#39;printf &amp;#39;\&amp;#39;&amp;#39;%s\n&amp;#39;\&amp;#39;&amp;#39; first &amp;#34;sec ond&amp;#34; third&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;first
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;sec ond
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;third
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note here that we used &lt;code&gt;\\&lt;/code&gt; to avoid having the git config parser handle the
&lt;code&gt;\n&lt;/code&gt;.  A &lt;code&gt;strace(1)&lt;/code&gt; shows us:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;%&lt;/span&gt; strace -ff git foo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[...]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[pid 16724] execve(&amp;#34;/bin/sh&amp;#34;, [&amp;#34;/bin/sh&amp;#34;, &amp;#34;-c&amp;#34;, &amp;#34;printf &amp;#39;%s\\n&amp;#39; first \&amp;#34;sec ond\&amp;#34; th&amp;#34;..., &amp;#34;printf &amp;#39;%s\\n&amp;#39; first \&amp;#34;sec ond\&amp;#34; th&amp;#34;...], [/* 62 vars */] &amp;lt;unfinished ...&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[...]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;%&lt;/span&gt; strace -ff sh -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;printf &amp;#39;%s\n&amp;#39;&amp;#34;&lt;/span&gt; first &lt;span class=&#34;s1&#34;&gt;&amp;#39;sec ond&amp;#39;&lt;/span&gt; third
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;execve(&amp;#34;/bin/sh&amp;#34;, [&amp;#34;sh&amp;#34;, &amp;#34;-c&amp;#34;, &amp;#34;printf &amp;#39;%s\\n&amp;#39;&amp;#34;, &amp;#34;first&amp;#34;, &amp;#34;sec ond&amp;#34;, &amp;#34;third&amp;#34;], [/* 61 vars */]) = 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, the sequence &lt;code&gt;\\n&lt;/code&gt; is simply how strace is showing that &lt;code&gt;\n&lt;/code&gt; as a
two-character sequence is the string being passed through.&lt;/p&gt;
&lt;p&gt;Returning to Git&amp;rsquo;s codebase:
Without an exclamation mark, &lt;code&gt;split_cmdline()&lt;/code&gt; is used and the results put
into the current process&amp;rsquo;s &lt;code&gt;argv[]&lt;/code&gt; vector after the initial &lt;code&gt;git&lt;/code&gt;, and
argument processing effectively then restarts, letting git decide again what
should be done.&lt;/p&gt;
&lt;p&gt;Without an exclamation mark, that&amp;rsquo;s it.  All done, quick and clean and simple.
Everything after here assumes that the entry starts with an exclamation mark.&lt;/p&gt;
&lt;p&gt;What the exclamation mark means is really “run this entry as a sub-command
now, and exit”.  This is found in &lt;code&gt;git.c:handle_alias()&lt;/code&gt;.
The invocation is then &lt;code&gt;run_command()&lt;/code&gt; on a struct with &lt;code&gt;.use_shell&lt;/code&gt; set.
The entire string of the alias value is passed into this as a single string,
no whitespace handling.  &lt;code&gt;split_cmdline()&lt;/code&gt; &lt;em&gt;does not apply&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But just having &lt;code&gt;.use_shell&lt;/code&gt; set still doesn&amp;rsquo;t mean that a shell is used.
What it means is that &lt;code&gt;run-command.c:prepare_shell_cmd()&lt;/code&gt; will be used to
construct the shell command, which &lt;em&gt;might&lt;/em&gt; mean that a shell will be used.&lt;/p&gt;
&lt;p&gt;What &lt;code&gt;prepare_shell_cmd()&lt;/code&gt; does is look to see if the value of the command
might be a single word without any special characters.  Those special
characters are backtick itself or any of: &lt;code&gt;|&amp;amp;;&amp;lt;&amp;gt;()$\&amp;quot;&#39; \t\n*?[#~=%&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Since a whitespace is in the list of characters, simply having two words is
enough to trigger invoking the shell.&lt;/p&gt;
&lt;p&gt;Once the shell is invoked, &lt;em&gt;the exact form invoked depends upon whether or not
the invoker of the command provided parameters on the command-line&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In all cases, the shell is invoked with at least four parameters in &lt;code&gt;argv&lt;/code&gt;.
The first two are &lt;code&gt;[&amp;quot;sh&amp;quot;, &amp;quot;-c&amp;quot;]&lt;/code&gt;.  The third will be the string from the
configuration, if and only if no parameters were supplied to git by the
invoker.  If parameters were provided, then instead the third parameter for
the shell is modified, by adding the five characters &lt;code&gt; &amp;quot;$@&amp;quot;&lt;/code&gt; to the end of it.
(Five: &lt;code&gt;SPACE&lt;/code&gt;, &lt;code&gt;QUOTATION MARK&lt;/code&gt;, &lt;code&gt;DOLLAR SIGN&lt;/code&gt;, &lt;code&gt;COMMERCIAL AT&lt;/code&gt;, &lt;code&gt;QUOTATION MARK&lt;/code&gt;.)
Git tries to be clever and assumes that you&amp;rsquo;ll want the arguments available at
the end of whatever string is given.&lt;/p&gt;
&lt;p&gt;This works for simple commands.  But for a text which tries to handle
arguments itself, it&amp;rsquo;s a hindrance to be worked around.&lt;/p&gt;
&lt;p&gt;The fourth of the always-present parameters for the shell is the text from the
alias definition.  Repeated, as the name of the shell.&lt;/p&gt;
&lt;p&gt;Any elements in the new shell&amp;rsquo;s &lt;code&gt;argv&lt;/code&gt; after that are passed through from the
invoker of git.&lt;/p&gt;
&lt;h3 id=&#34;what-this-means-for-us&#34;&gt;What this means for us&lt;/h3&gt;
&lt;p&gt;Handling the auto-inserted &lt;code&gt; &amp;quot;$@&amp;quot;&lt;/code&gt; is simple enough, once you know that it
needs to be handled: simply end your alias definition with a shell comment
character, the octothorpe &lt;code&gt;#&lt;/code&gt; (Unicode &lt;code&gt;NUMBER SIGN&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;This is correct alias definition for &lt;code&gt;~/.gitconfig&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[alias]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;na&#34;&gt;wibble&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;!set -x\necho $#\nfor x in \&amp;#34;$@\&amp;#34;; do echo \&amp;#34;: {$x}\&amp;#34;; done #&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  wobble = &amp;#34;!for x in \&amp;#34;$@\&amp;#34;; do echo \&amp;#34;: {$x}\&amp;#34;; done #&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, the git configuration file parsing has resulted in the list of aliases
containing an entry for &lt;code&gt;wibble&lt;/code&gt; and one for &lt;code&gt;wobble&lt;/code&gt;, where the stored
strings are:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;!set -x
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; x in &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;: {&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and the same but without the first two lines.&lt;/p&gt;
&lt;p&gt;We can invoke &lt;code&gt;git wibble&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;%&lt;/span&gt; git wibble
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+ echo 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;%&lt;/span&gt; git wibble foo &lt;span class=&#34;s1&#34;&gt;&amp;#39;bar baz&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+ echo 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+ for x in &amp;#39;&amp;#34;$@&amp;#34;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+ echo &amp;#39;: {foo}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;: {foo}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+ for x in &amp;#39;&amp;#34;$@&amp;#34;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+ echo &amp;#39;: {bar baz}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;: {bar baz}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those two invocations using &lt;code&gt;git wobble&lt;/code&gt; instead (to make this a little more
condensed and easier to scan) would have resulted in these &lt;code&gt;argv&lt;/code&gt; arrays being
processed (using single-quotes for strings to avoid introducing backslashes):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;sh&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;for x in &amp;#34;$@&amp;#34;; do echo &amp;#34;: {$x}&amp;#34;; done #&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;for x in &amp;#34;$@&amp;#34;; do echo &amp;#34;: {$x}&amp;#34;; done #&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# ignored, shell $0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;second&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;sh&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;for x in &amp;#34;$@&amp;#34;; do echo &amp;#34;: {$x}&amp;#34;; done # &amp;#34;$@&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;for x in &amp;#34;$@&amp;#34;; do echo &amp;#34;: {$x}&amp;#34;; done #&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# ignored, shell $0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# $1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s1&#34;&gt;&amp;#39;bar baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# $2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;credential-helpers&#34;&gt;Credential Helpers&lt;/h3&gt;
&lt;p&gt;The AWS documentation currently up at
&lt;a href=&#34;https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-https-unixes.html&#34;&gt;https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-https-unixes.html&lt;/a&gt;
has you run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global credential.helper &lt;span class=&#34;s1&#34;&gt;&amp;#39;!aws codecommit credential-helper $@&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve not used CodeCommit in a while and am not setting it up again now to
confirm, but I believe this is wrong.  The same mechanisms are in play for how
git invokes commands, (except that without an exclamation mark, if not given a
complete path, the command tried for &lt;code&gt;&amp;quot;foo&amp;quot;&lt;/code&gt; will be &lt;code&gt;&amp;quot;git credential-foo&amp;quot;&lt;/code&gt;)
so you&amp;rsquo;ll end up with git invoking:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;sh&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;aws codecommit credential-helper $@ &amp;#34;$@&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;aws codecommit credential-helper $@&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# ignored, shell $0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;get&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and the shell then invoking:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;aws&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;codecommit&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;credential-helper&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;get&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;get&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clearly the &lt;code&gt;aws codecommit credential-helper&lt;/code&gt; is ignoring extraneous
parameters, and relying upon the available sub-commands being any of
&lt;code&gt;[&amp;quot;get&amp;quot;, &amp;quot;store&amp;quot;, &amp;quot;erase&amp;quot;]&lt;/code&gt;, none of which contain whitespace, so &lt;code&gt;$@&lt;/code&gt;
degenerating to &lt;code&gt;$*&lt;/code&gt; here is harmless.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s easier than expected to have arbitrary shell in a git alias, you just
need to know about the undocumented implicit sometimes-added &lt;code&gt; &amp;quot;$@&amp;quot;&lt;/code&gt;.  That
still doesn&amp;rsquo;t mean you &lt;em&gt;should&lt;/em&gt; do so.&lt;/p&gt;
&lt;p&gt;-&lt;i&gt;The Grumpy Troll&lt;/i&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Alpine Linux on ARM (Turris Router)</title>
      <link>https://bridge.grumpy-troll.org/2018/03/alpine-arm/</link>
      <pubDate>Sun, 18 Mar 2018 13:25:00 -0400</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2018/03/alpine-arm/</guid>
      <description>&lt;p&gt;My home router is a Turris Omnia, which provides the option for running LXC
containers; I use this for SSH jumphosts and other such things as belong &amp;ldquo;on
the router itself&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Last night I decided that it was time to install an Alpine Linux container, to
complement the Debian container which has been predominantly used to date.
This presented a few issues, but all was done.  In this post: networking from
no-network, CIDR (classless) routes accepted over DHCP, and other quirks.&lt;/p&gt;
&lt;p&gt;Apologies in advance for use of &lt;code&gt;cat&lt;/code&gt; straight to a terminal; busybox&amp;rsquo;s cat(1)
does not support &lt;code&gt;-v&lt;/code&gt; and I&amp;rsquo;m choosing to trust the state of the OS at the
point in time where I&amp;rsquo;m running these commands, such that I consider the risk
acceptable.&lt;/p&gt;
&lt;p&gt;The version presented here is cleaned up, with debugging repetitions removed,
showing only what I believe to be the steps needed.&lt;/p&gt;
&lt;h3 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h3&gt;
&lt;p&gt;I live in Pittsburgh, PA.  The OS is &amp;ldquo;Alpine&amp;rdquo;, the nearest semi-famous
mountain is Mount Washington (popular with tourists) so we have a container
name: &lt;code&gt;washington&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Create the container, I happened to use the Web UI here, as it presented a
convenient drop-down of options.  The interface is through LuCI, the current
web front-end to the UCI configuration system.  Thus for me, the URL was
&lt;a href=&#34;https://turris.lan/cgi-bin/luci/admin/services/lxc&#34;&gt;https://turris.lan/cgi-bin/luci/admin/services/lxc&lt;/a&gt; &amp;ndash; I mention this so as
to note in passing that one of the nice things about the Turris was how easy
it was to install a key/cert signed by my personal Certificate Authority.&lt;/p&gt;
&lt;p&gt;I chose &lt;code&gt;Alpine 3.7&lt;/code&gt;.  I did not start it.  This was the end of the use of the
web interface: CLI all the way from here.  With logbooks, so that if I ever
need to automate this then I have a playbook to use for building the
configuration management system rules.  From logbooks of previous installs, I
see that I could have avoided the Web UI here too with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lxc-create -t download -n x -- -l
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lxc-create -t download -n washington -- -d Alpine -r 3.7 -a armv7l
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Logged into the turris system as root, the next step is some basic
configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grep &lt;span class=&#34;s1&#34;&gt;&amp;#39;^lxc\.network\.hwaddr&amp;#39;&lt;/span&gt; /srv/lxc/washington/config
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /srv/lxc/washington/rootfs/etc/hostname
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; washington &amp;gt; /srv/lxc/washington/rootfs/etc/hostname
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I used the MAC address from the first line to populate the Single Source of
Truth used for generating my home DHCP and DNS server configs; this is easier
if you use whatever&amp;rsquo;s on the Turris, I forget the details, but I disabled much
of that stuff because I already have ISC dhcpd and Unbound running at home.&lt;/p&gt;
&lt;p&gt;Next, the basic configuration to get running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lxc-start -n washington
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uci show lxc-auto
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uci add lxc-auto container
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uci &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; lxc-auto.@container&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;-1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;.name&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;washington
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uci &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; lxc-auto.@container&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;-1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;.timeout&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uci show lxc-auto
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uci commit lxc-auto
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lxc-attach -n washington
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point, assuming &lt;code&gt;lxc-auto&lt;/code&gt; is enabled for system boot, the container
should start automatically on next boot; I seem to recall that this took much
poking and prodding when I first set up a container on this host, because the
usual LXC auto-start stuff all exists but is unused, with UCI instead being
the only working mechanism.&lt;/p&gt;
&lt;h3 id=&#34;networking&#34;&gt;Networking&lt;/h3&gt;
&lt;p&gt;Our next problem is that we have no networking, no &lt;code&gt;/etc/network/interfaces&lt;/code&gt;,
very little in the way of networking tools, and very little installed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;#&lt;/span&gt; apk info &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sort &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; xargs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;alpine-baselayout alpine-keys apk-tools busybox libc-utils
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  libressl2.6-libcrypto libressl2.6-libssl musl musl-utils
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  scanelf zlib
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[ manual linebreaks added ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While &lt;a href=&#34;https://wiki.alpinelinux.org/wiki/Configure_Networking&#34;&gt;https://wiki.alpinelinux.org/wiki/Configure_Networking&lt;/a&gt; provides some
guidance, it still assumes more of a system than we have.  Why are things so
bare?  &lt;a href=&#34;https://doc.turris.cz/doc/en/public/lxc_alpine&#34;&gt;https://doc.turris.cz/doc/en/public/lxc_alpine&lt;/a&gt; explains that the
original package source Turris used dropped all their &lt;code&gt;armhf&lt;/code&gt; packages without
notice, so Turris switched to the minimal OS images directly from Alpine,
which unfortunately are intended for use in other scenarios.  So, manual IP
configuration and route-table manipulation it is.  Adding in the steps we need
from both sources and adapting for being inside a container, we have this;
I&amp;rsquo;ve changed some IP details, so we&amp;rsquo;re assuming that our IP (per DHCP
reservation) is &lt;code&gt;192.168.1.201&lt;/code&gt; in a &lt;code&gt;/24&lt;/code&gt; network with DNS servers on the
&lt;code&gt;.10&lt;/code&gt; and &lt;code&gt;.11&lt;/code&gt; IPs and default gateway (the Turris itself) on the &lt;code&gt;.1&lt;/code&gt; IP.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /etc/hosts
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;::1\t\tlocalhost ipv6-localhost ipv6-loopback\nfe00::0\t\tipv6-localnet\n&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /etc/hosts
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; x in 0:mcastprefix 1:allnodes 2:allrouters 3:allhosts&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;ff02::%s\t\tipv6-%s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;%:*&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;#*:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt; &amp;gt;&amp;gt; /etc/hosts
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /etc/hosts
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;auto lo eth0\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;iface lo inet%s loopback\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; 6&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;iface eth0 inet%s %s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;6&lt;/span&gt; manual &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; dhcp&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;\tudhcpc_opts -O staticroutes\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt; &amp;gt; /etc/network/interfaces
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -t proc proc /proc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 192.168.1.201 netmask 255.255.255.0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;route add default gw 192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;domain lan\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;nameserver 192.168.1.%s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt; 11&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;options timeout 1 attempts 1 rotate\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  &amp;gt; /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apk update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apk upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apk add busybox-initscripts
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-update add networking &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rc-update add bootmisc boot &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rc-update add syslog boot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apk add zsh vim curl git acl attr iputils rsync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;reboot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As is normal for containers, &lt;code&gt;reboot&lt;/code&gt; merely restarts the container, not the
outside OS.  Above I enable IPv6 and IPv4.  Note that Alpine does not appear
to support NDP for IP configuration.  Note too that for IPv4, I&amp;rsquo;ve used
&lt;code&gt;dhcpc_opts -O staticroutes&lt;/code&gt; as an option line in &lt;code&gt;/etc/network/interfaces&lt;/code&gt;.
I&amp;rsquo;ve confirmed that this adds to the options on the command-line, instead of
replacing them.  This is how we tell the Alpine DHCP client that we want to
request an extra option, DHCP option 121 per RFC 3442.  At this point, the
option will be requested and details made available in environment variables
to the configuration scripts, but nothing else done.&lt;/p&gt;
&lt;p&gt;We can then look at &lt;code&gt;less /var/log/messages&lt;/code&gt; and see a bunch of spam filling
the logs quickly because &lt;code&gt;/etc/inittab&lt;/code&gt; is configured to request getty
processes listening on TTYs which don&amp;rsquo;t exist.  &lt;code&gt;vi /etc/inittab&lt;/code&gt; and remove
the &lt;code&gt;tty5&lt;/code&gt; and &lt;code&gt;tty6&lt;/code&gt; entries.  I also commented out 2, 3 and 4: we don&amp;rsquo;t need
these things.  One &lt;code&gt;getty&lt;/code&gt; should be enough: in fact, it&amp;rsquo;s probably more than
enough, since &lt;code&gt;lxc-attach -n CONTAINER&lt;/code&gt; does not use a serial line to connect
in.  We can comment out &lt;em&gt;all&lt;/em&gt; of the &lt;code&gt;getty&lt;/code&gt; lines quite safely and reclaim a
little RAM.&lt;/p&gt;
&lt;p&gt;To debug what was needed for &lt;code&gt;udhcpc&lt;/code&gt;, I read
&lt;code&gt;/usr/share/udhcpc/default.script&lt;/code&gt; to see how things fit together.  Then,
taking advantage of &amp;ldquo;not yet permitting remote login&amp;rdquo; to do unsafe
FS-race-attack-prone things in /tmp, I just ran:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir /etc/udhcpc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;env &amp;gt; /tmp/troll.$$&amp;#39;&lt;/span&gt; &amp;gt; /etc/udhcpc/udhcpc.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;service networking restart
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm /etc/udhcpc/udhcpc.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This yields a file in &lt;code&gt;/tmp&lt;/code&gt; for each invocation of the udhcpc script, letting
us see exactly which variables are exposed each time, and at what stages we&amp;rsquo;re
called.  At this point, there was much invocation of &lt;code&gt;strings&lt;/code&gt; upon binaries
and other debugging techniques to get to where I figured out the option name
required: it has been renamed at least once, many search results online are
outdated.  What I did get from search-fu was that &lt;code&gt;dhcpc_opts&lt;/code&gt; is the relevant
option in &lt;code&gt;/etc/network/interfaces&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Time to create some shell scripts to manage bringing up the CIDR routes.
Here&amp;rsquo;s what I created and the scripts I wrote; please note, that renew and
deconfig have not been heavily tested.  I know that bringing the interface up
works well enough.&lt;/p&gt;
&lt;p&gt;Of note: DHCP option 121, aka &lt;code&gt;cidr-routes&lt;/code&gt;, aka
&lt;code&gt;rfc3442-classless-static-routes&lt;/code&gt;, is required to include &lt;em&gt;all&lt;/em&gt; routes,
including the default route, as clients should ignore the normal route option
in the presence of DHCP option 121.&lt;/p&gt;
&lt;p&gt;(This is in contrast to &lt;code&gt;ms-cidr-routes&lt;/code&gt; aka &lt;code&gt;ms-classless-static-routes&lt;/code&gt; aka
private option space usage of code 249 for Microsoft platforms: although it&amp;rsquo;s
documented in &lt;a href=&#34;https://msdn.microsoft.com/en-us/library/cc227282.aspx&#34;&gt;MSDN&lt;/a&gt;
as differing only in option code, various resources online claim that the
default route should be omitted from that option.  Fun.)&lt;/p&gt;
&lt;p&gt;In my code, I do the opposite: I leave the route from DHCP option 3 (RFC 2132)
in place, and skip any default route found in the DHCP option 121.  This
removes a window of turning down a freshly turned up interface, or a bunch of
other complexity to handle this.  In practice, if your default route differs
between option 3 and option 121, then you&amp;rsquo;re crazy and on your own.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /etc/udhcpc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; D in post-bound post-renew pre-deconfig&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  touch &lt;span class=&#34;nv&#34;&gt;$D&lt;/span&gt;/cidr-routes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  chmod &lt;span class=&#34;m&#34;&gt;755&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$D&lt;/span&gt;/cidr-routes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  vi &lt;span class=&#34;nv&#34;&gt;$D&lt;/span&gt;/cidr-routes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;etcudhcpcpost-boundcidr-routes&#34;&gt;&lt;code&gt;/etc/udhcpc/post-bound/cidr-routes&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;statedir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/var/lib/udhcpc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;togglefile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$statedir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/cidr-routes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cachefile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$statedir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/latest.staticroutes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RETURN_AFTER_SETTINGS&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;staticroutes&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -d &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$statedir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; mkdir &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$statedir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &amp;gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cachefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$staticroutes&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# the sed relies upon a GNU extension, which is available (addr,+N)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$staticroutes&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  *&lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;0.0.0.0/0&lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;*&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;use_routes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$staticroutes&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; xargs -n &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sed &lt;span class=&#34;s1&#34;&gt;&amp;#39;/^0\.0\.0\.0\/0$/,+1d&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; xargs&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  *&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;use_routes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$staticroutes&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;#!/bin/sh&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;enable() {&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;  ip route add %s via %s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$use_routes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;disable() {&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;  ip route del %s via %s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$use_routes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  cat &lt;span class=&#34;s&#34;&gt;&amp;lt;&amp;lt;&amp;#39;EOTAIL&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;case &amp;#34;${1:-missing}&amp;#34; in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  missing) ;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  enable) enable ;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  disable) disable ;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  *) echo &amp;gt;&amp;amp;2 &amp;#39;bad arg&amp;#39;; exit 1 ;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;esac
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;EOTAIL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &amp;gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$togglefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod &lt;span class=&#34;m&#34;&gt;0755&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$togglefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$togglefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;etcudhcpcpost-renewcidr-routes&#34;&gt;&lt;code&gt;/etc/udhcpc/post-renew/cidr-routes&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;staticroutes&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;RETURN_AFTER_SETTINGS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;t
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;. /etc/udhcpc/post-bound/cidr-routes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;unset&lt;/span&gt; RETURN_AFTER_SETTINGS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cachefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; /etc/udhcpc/post-bound/cidr-routes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;previous&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;cat &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cachefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$staticroutes&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$previous&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;# don&amp;#39;t force back on; if disabled, leave disabled&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$togglefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; disable
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; /etc/udhcpc/post-bound/cidr-routes
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;etcudhcpcpre-deconfigcidr-routes&#34;&gt;&lt;code&gt;/etc/udhcpc/pre-deconfig/cidr-routes&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RETURN_AFTER_SETTINGS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;t
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;. /etc/udhcpc/post-bound/cidr-routes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;unset&lt;/span&gt; RETURN_AFTER_SETTINGS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$togglefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$togglefile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; disable
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;security-setup&#34;&gt;Security Setup&lt;/h2&gt;
&lt;p&gt;Time for CA certificate configuration, SSH, and so forth.  I don&amp;rsquo;t enable DSA,
or RSA, and don&amp;rsquo;t want to have those sitting around on disk; I also don&amp;rsquo;t like
components auto-creating SSH hostkeys if they think they&amp;rsquo;re missing: if it
gets used, it encourages sloppiness around hostkey verification.  If the files
disappear, it&amp;rsquo;s time to use console and restore from backups, or create new
ones and distribute the new public keys via secure mechanisms.&lt;/p&gt;
&lt;h3 id=&#34;pkix-ssltls&#34;&gt;PKIX, SSL/TLS&lt;/h3&gt;
&lt;p&gt;The directory &lt;code&gt;/usr/local/share/ca-certificates&lt;/code&gt; exists, is empty, and any
&lt;code&gt;.crt&lt;/code&gt; files will be picked up by &lt;code&gt;update-ca-certificates&lt;/code&gt; and trusted
immediately.  Note though that the symlink created in &lt;code&gt;/etc/ssl/certs/&lt;/code&gt; has
&lt;code&gt;ca-cert-&lt;/code&gt; prepended to the filename (of the symlink itself), which was an
unexpected difference and caused a few moments of head-scratching as I
wondered why my certs hadn&amp;rsquo;t been picked up.&lt;/p&gt;
&lt;p&gt;Before making changes, I used &lt;code&gt;curl&lt;/code&gt; to verify that certs issued by public CAs
were working, and that those issued by my CA were resulting in failure.  I
pasted in the certificate of my household Certificate Authority, then ran
&lt;code&gt;update-ca-certificates&lt;/code&gt;.  I then re-ran the &lt;code&gt;curl&lt;/code&gt; tests and All Was Good.&lt;/p&gt;
&lt;h3 id=&#34;openssh&#34;&gt;OpenSSH&lt;/h3&gt;
&lt;p&gt;I tend to stick to OpenSSH at this time.  Note though that OpenSSH 7.5, as
included in Alpine 3.7, has a bug where use of &lt;code&gt;-o VerifyHostKeyDNS=ask&lt;/code&gt; (or
anything other than &lt;code&gt;=no&lt;/code&gt;) will cause a segfault for any host missing &lt;code&gt;SSHFP&lt;/code&gt;
records in DNS.  As far as I can tell, this is entirely an upstream bug and
the fix is to upgrade to OpenSSH 7.6.  I just left it with this known bug in
place.&lt;/p&gt;
&lt;p&gt;I also tend to place all authorized keys files into
&lt;code&gt;/etc/ssh/userauth/USERNAME&lt;/code&gt; instead of &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;, as this is
more amenable both to central distribution and to using systems such as
&lt;code&gt;etckeeper&lt;/code&gt; to detect &lt;strong&gt;and record&lt;/strong&gt; changes in how authentication can happen.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apk add openssh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;\n\nSSHD_DISABLE_KEYGEN=yes\n&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /etc/conf.d/sshd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; want in ed25519 ecdsa&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ssh-keygen -t &lt;span class=&#34;nv&#34;&gt;$want&lt;/span&gt; -f /etc/ssh/ssh_host_&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;want&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;_key -N &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; -C washington.lan &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tail -n +0 /etc/ssh/ssh_host_*_key.pub
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;KEYDIR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/ssh/userauth&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; mkdir &lt;span class=&#34;nv&#34;&gt;$KEYDIR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; vi &lt;span class=&#34;nv&#34;&gt;$KEYDIR&lt;/span&gt;/root &lt;span class=&#34;nv&#34;&gt;$KEYDIR&lt;/span&gt;/troll
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vi /etc/ssh/sshd_config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Changes to &lt;code&gt;sshd_config&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# uncomment *only* ECDSA and Ed25519 keys
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Double-check that &amp;#34;PermitRootLogin prohibit-password&amp;#34; is the default, else
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# set it.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;LogLevel VERBOSE
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AuthorizedKeysFile      /etc/ssh/userauth/%u
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PasswordAuthentication no
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ChallengeResponseAuthentication no
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AcceptEnv WINDOW TMUX_PANE TZ ITERM_PROFILE COLORFGBG
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;UseDNS no
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AllowUsers *@127.0.0.1 *@::1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AllowUsers root@192.0.2.7 root@198.51.100.49
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AllowUsers troll@192.0.2.7 troll@198.51.100.49
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then basic user setup.  I couldn&amp;rsquo;t log in as the &lt;code&gt;troll&lt;/code&gt; user at first; after
running &lt;code&gt;/usr/sbin/sshd -ddd -p 24&lt;/code&gt; and connecting to that, I saw the
permission denied errors trying to access the file; a little more
investigation led to this gem:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;#&lt;/span&gt; ls -ld /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;drwx------    1 root     root           108 Mar 18 09:46 /
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I have no idea what led to that &amp;hellip; &amp;ldquo;most peculiar setting&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-update add sshd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-status
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;service sshd start
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;addgroup -g &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt; human
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;adduser -h /home/troll -g &lt;span class=&#34;s1&#34;&gt;&amp;#39;Grumpy Troll&amp;#39;&lt;/span&gt; -s /bin/zsh -u &lt;span class=&#34;m&#34;&gt;3000&lt;/span&gt; -G human troll
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;# hit enter a couple of times for password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vi /etc/shadow
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;# on the line for `troll`, replace the second field with just a single &amp;#39;*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod &lt;span class=&#34;m&#34;&gt;0755&lt;/span&gt; /
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;fin&#34;&gt;Fin&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;To get an &lt;code&gt;openssl&lt;/code&gt; binary, use &lt;code&gt;apk add libressl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;tput&lt;/code&gt; command is in &lt;code&gt;ncurses&lt;/code&gt;, but I reworked the personal config files
git repo (not covered above) to check for existence of the &lt;code&gt;tput&lt;/code&gt; command
and fallback to an assumption of ANSI escape sequences for color, which
seemed eminently sane.  If a TTY doesn&amp;rsquo;t support the ANSI sequences, then
the system should have a curses system and working &lt;code&gt;tput&lt;/code&gt; installed.  If
every TTY does, let&amp;rsquo;s default to sane strings instead of re-deriving them on
startup.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turris.lan/cgi-bin/luci/admin/network/firewall/forwards&#34;&gt;https://turris.lan/cgi-bin/luci/admin/network/firewall/forwards&lt;/a&gt; to enable
a port-forward from the WAN on a non-standard port, to the IP of the
container on port 22
&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; inside the container to change &lt;code&gt;AllowUsers&lt;/code&gt;
accordingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s about it.  The complexity was all in the debugging to find the key
pieces of information needed.&lt;/p&gt;
&lt;p&gt;-&lt;i&gt;The Grumpy Troll&lt;/i&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>boot2docker xhyve DNS</title>
      <link>https://bridge.grumpy-troll.org/2017/11/boot2docker-xhyve-dns/</link>
      <pubDate>Tue, 14 Nov 2017 15:30:00 -0500</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2017/11/boot2docker-xhyve-dns/</guid>
      <description>&lt;p&gt;Using macOS with Docker can be &amp;ldquo;interesting&amp;rdquo;.  When I got started, I followed
the useful advice at
&lt;a href=&#34;https://pilsniak.com/how-to-install-docker-on-mac-os-using-brew/&#34;&gt;https://pilsniak.com/how-to-install-docker-on-mac-os-using-brew/&lt;/a&gt;.  This
approach appealed to me, especially the use of xhyve.  Because sometimes I
just make life difficult for myself.&lt;/p&gt;
&lt;p&gt;Thus my initial setup was:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install docker docker-machine xhyve docker-machine-driver-xhyve
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;f=/usr/local/opt/docker-machine-driver-xhyve/bin/docker-machine-driver-xhyve
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chown root:wheel $f; sudo chmod u+s $f
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  # because yay, more setuid root binaries; it&amp;#39;s written in Go, which is
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  # something at least.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-machine create default --driver xhyve --xhyve-experimental-nfs-share
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;eval &amp;#34;$(docker-machine env default)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tmutil addexclusion $HOME/.docker ; sudo tmutil addexclusion -p $HOME/.docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(Warning: that &lt;code&gt;docker-machine create&lt;/code&gt; line will fail for releases of the
xhyve driver after &lt;code&gt;v0.3.3&lt;/code&gt;; you&amp;rsquo;ll need to add an area to be shared to the
end of the command-line; use &lt;code&gt;/Users&lt;/code&gt; to match previous behavior.)&lt;/p&gt;
&lt;p&gt;Excluding the Docker VM area from regular backups is a useful addition.  Note
that the usual docker-machine eval guidance from the maintainers is wrong:
it will result in exporting an empty environment variable named &lt;code&gt;export&lt;/code&gt;.
Wrap the command-substitution in double-quotes to preserve newlines.
This is why decent tools emitting export commands for eval will insert
semi-colons at the end of each line, and avoid comments: to make life easier
for sloppy command-line usage and missing quotes.&lt;/p&gt;
&lt;p&gt;This gives us a VM running the fairly minimal OS named &lt;code&gt;boot2docker&lt;/code&gt;, which
runs Docker and exposes that to Docker on the host macOS via some environment
variables pointing the client tool at the VM instead of a local socket.&lt;/p&gt;
&lt;p&gt;The biggest problem I had was that my DNS server as seen by the VM kept
getting reset to be the IP of my laptop on that virtual network, and my
laptop&amp;rsquo;s DNS resolver was not set to be exposed over the network.  I&amp;rsquo;m
reluctant to do so, because I do attend conferences and use untrusted WiFi,
but exposing the DNS server to &lt;em&gt;only&lt;/em&gt; also listen to an arbitrary list of
dynamically created virtual networks, but not to the WiFi, is a fragile pain I
don&amp;rsquo;t want.  So the laptop is not itself a suitable DNS server for the VM
running boot2docker.&lt;/p&gt;
&lt;p&gt;Alas, the &lt;code&gt;docker-machine-driver-xhyve&lt;/code&gt; driver&amp;rsquo;s DHCP server simply doesn&amp;rsquo;t
set a DNS field and ignores whatever&amp;rsquo;s in the config elsewhere, so the VMs end
up with the IP address of the server being used as the DNS server IP too.
Always.&lt;/p&gt;
&lt;p&gt;In addition, the latest tagged version of that driver, &lt;code&gt;0.3.3&lt;/code&gt;, predates a fix
from August wherein that driver was also stomping over &lt;code&gt;bootlocal.sh&lt;/code&gt; on each
boot, removing the ability of users to work around the first issue.  My
grateful thanks to Hugues Alary for committing the fix, which I found in git
when I went looking to figure out why &lt;code&gt;boot2local.sh&lt;/code&gt; changes weren&amp;rsquo;t
persisting.&lt;/p&gt;
&lt;p&gt;Really, the xhyve driver is neat, but not yet ready for prime time.  In
fairness, it does call itself version &lt;code&gt;0.3.3&lt;/code&gt;.  That&amp;rsquo;s pretty clear.  So let&amp;rsquo;s
show how to stick with it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-machine stop
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew unlink docker-machine-driver-xhyve
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install --HEAD docker-machine-driver-xhyve
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;f=/usr/local/opt/docker-machine-driver-xhyve/bin/docker-machine-driver-xhyve
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chown root:wheel $f; sudo chmod u+s $f
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  # to get the password in the cache, because I/O is messed up through docker-machine
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-machine start
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# json: cannot unmarshal bool into Go struct field Driver.Virtio9p of type []string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ah, JSON config changes.  I have Docker images I really don&amp;rsquo;t want to have to
reload.  So instead of nuking the machine and recreating, we study commit
&lt;code&gt;dff9c59d&lt;/code&gt; of the driver, bring up a temporary second machine to have a
template to compare against, and then:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;C=&amp;#34;$HOME/.docker/machine/machines/default/config.json&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;jq &amp;lt; &amp;#34;$C&amp;#34; &amp;gt; &amp;#34;$C.new&amp;#34; &amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  .Driver.Virtio9p = null |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  .Driver.NFSShares = [.Driver.Virtio9pFolder] |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  .Driver.NFSShare = false |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  .Driver.NFSSharesRoot = &amp;#34;/xhyve-nfsshares&amp;#34; |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  .Driver.Virtio9pRoot = &amp;#34;/xhyve-virtio9p&amp;#34; |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  del(.Driver.Virtio9pFolder)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ls -ld &amp;#34;$C&amp;#34; &amp;#34;$C.new&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod 0600 &amp;#34;$C.new&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv &amp;#34;$C.new&amp;#34; &amp;#34;$C&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That should get us a bootable VM.
At this point, we can once more boot boot2docker, and &lt;code&gt;bootlocal.sh&lt;/code&gt; won&amp;rsquo;t be
stomped over on each boot (but has cruft left in it).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;docker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machine&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;docker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machine&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ssh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Hi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lib&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;boot2docker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;cp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;usr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;share&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;udhcpc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;./&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dhcp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;script&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;vi&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dhcp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;script&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# near top, after other checks but before the &amp;#39;case&amp;#39;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lib&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;boot2docker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dns&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;servers&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dns&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;$(cat /var/lib/boot2docker/dns-servers)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;vi&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dns&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;servers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# whatever IPs you want, separated by whitespace; no comments allowed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# for Google&amp;#39;s Public DNS servers:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mf&#34;&gt;8.8&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;4.4&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;8.8&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;8.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;vi&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bootlocal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# everything in this script is now junk, left behind by old versions of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# the xhyve driver, so bring it down to:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;c1&#34;&gt;#!/bin/sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;sed&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;s,/sbin/udhcpc,&amp;amp; -s /var/lib/boot2docker/dhcp.script,&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;etc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dhcp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;c1&#34;&gt;# and to trigger one run now, because we&amp;#39;re too late&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;DEVICE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;eth0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sbin&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;udhcpc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lib&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;boot2docker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dhcp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DEVICE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hostname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bin&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hostname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;udhcpc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DEVICE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;docker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machine&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stop&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;docker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machine&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And lo, working DNS.&lt;/p&gt;
&lt;p&gt;-&lt;i&gt;The Grumpy Troll&lt;/i&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Golang SSH Redux</title>
      <link>https://bridge.grumpy-troll.org/2017/04/golang-ssh-redux/</link>
      <pubDate>Tue, 25 Apr 2017 20:20:00 -0400</pubDate>
      <author>Phil Pennock</author>
      <guid>https://bridge.grumpy-troll.org/2017/04/golang-ssh-redux/</guid>
      <description>&lt;p&gt;I&amp;rsquo;d like to set a couple of things straight, for the record.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll cover the post/blog, and then I&amp;rsquo;d like to counter some misconceptions.
While part of me thinks &amp;ldquo;I must&amp;rsquo;ve been very unclear to have so many people
misunderstand&amp;rdquo;, I also saw how many people commented without bothering to
read, so really there&amp;rsquo;s a limit to how much self-flagellation will happen.&lt;/p&gt;
&lt;p&gt;I am not a security researcher.  I do not try to get bug bounties.  I normally
care about things like CVEs when I&amp;rsquo;m coordinating the Exim project&amp;rsquo;s response
to some problem and talking to CERTs, packagers, etc.  I&amp;rsquo;m thus either on the
receiving side of the CVE report or requesting a CVE for software for which I
am somehow responsible.&lt;/p&gt;
&lt;h3 id=&#34;the-post&#34;&gt;The Post&lt;/h3&gt;
&lt;p&gt;In late March, while working on a project for a client, I encountered
something surprising in the state of the world around &lt;a href=&#34;https://golang.org/&#34; title=&#34;The Go Programming Language&#34;&gt;Golang&lt;/a&gt;
(the programming language) and &lt;a href=&#34;https://godoc.org/golang.org/x/crypto/ssh&#34; title=&#34;package ssh&#34;&gt;its SSH support&lt;/a&gt;
(Secure SHell, for secure connections to remote computers by limited sets of
authorized users, often with the goal of running arbitrary commands on those
computers: so remote access, rather than remote retrieval of resources).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://bridge.grumpy-troll.org/2017/04/golang-ssh-security/&#34;&gt;On April 2nd, I wrote about what happened&lt;/a&gt;.
On April 3rd, I added the CVE number when it was assigned.
On April 4th, I started a Twitter poll and linked to it.
The poll ran for the maximum time allowed (10 days).  After it was over, I
updated the blog post again with the results.
Like most of my blog posts, a couple of friends read it and commented but
nothing more came of the issue.&lt;/p&gt;
&lt;p&gt;Then on April 15th, out of the blue, a former colleague tagged me in a message
with &amp;ldquo;your article is tops on HN, in the unlikely event you were not already
aware&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I was not aware.  I had received one message earlier that day from someone I
vaguely knew, and who had read the post and had some more concerns about their
own experiences with the security handling of a certain vendor.  I didn&amp;rsquo;t know
there was anything else happening.&lt;/p&gt;
&lt;p&gt;In fact, the post went to the top of &lt;a href=&#34;https://news.ycombinator.com/item?id=14121780&#34;&gt;Hacker News&lt;/a&gt;, then also
appeared on Lobsters, Slashdot, two different &lt;code&gt;/r/netsec&lt;/code&gt; posts on Reddit and
all sorts of other places.  (Yes, this Grumpy Troll goes by &lt;code&gt;syscomet&lt;/code&gt; in a
few fora).&lt;/p&gt;
&lt;p&gt;This blog is hosted in an AWS S3 bucket, with AWS CloudFront in front of it.
That setup handled the load without blinking; it would handle a far higher
load than anything thrown at it that weekend.  Total added cost to me for
around 64k post views, 608k HTTP requests and 21GB of traffic has been just
under $3.  That I don&amp;rsquo;t need to care that this was over one weekend instead of
spread over the month is an advantage of using infrastructure built to scale
for giants and riding along as a minnow.&lt;/p&gt;
&lt;p&gt;Also: I need to get around to using HTML Subresource Integrity checksums and
offloading serving jQuery and some CSS stuff to common hosting.  That would
have saved me most of that $3, while speeding access for most people and with
a &amp;ldquo;probably acceptable&amp;rdquo; privacy trade-off for resources which are &amp;ldquo;probably
loaded anyway, and use a cryptographic checksum to avoid even having to leak
that we might need to load it again&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;In the post, I aimed for my usual level of dry humor, for which folks should
be able to see a warning in the sidebar.  Expeditions are still failing to
report.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;golang&#34;&gt;Golang&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Go Programming Language never had an insecure SSH library.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What they had was a library with an API prone to misuse.  There was never a
documentation flaw.  The requirement for secure use was always documented.&lt;/p&gt;
&lt;p&gt;I know this, because I read the API docs and clearly understood that it was on
me to implement hostkey verification.  After some brief playing around to
sketch out what this would look like, I decided to search to see if there were
common debugged libraries which already handled the corner cases, and so
looked to see what other projects were doing.  Not Invented Here (NIH) is a
real problem and I try to avoid it.&lt;/p&gt;
&lt;p&gt;At this point, I discovered how few people (none which I saw) were bothering
to implement the hostkey verification.  They got working connections, they
never checked that things failed when they should fail.&lt;/p&gt;
&lt;p&gt;The handling of this issue by the Go maintainers was &lt;em&gt;exemplary&lt;/em&gt;.  They had
no security problem but accepted that so many people misusing the library was
a security problem and that they could do something about this.  They changed
the library defaults so as to force programmers to make a decision about how
to handle verification.  Instead of naive use leading to code which doesn&amp;rsquo;t
fail when it should, naive use now leads to code which doesn&amp;rsquo;t work until
programmers decide how to handle ensuring their application fails when it
should.&lt;/p&gt;
&lt;p&gt;Further, the Go maintainers have continued to improve the available facilities
in this area, with features such as a new sub-package &lt;code&gt;knownhosts&lt;/code&gt; to make it
easier to Do What OpenSSH Does.&lt;/p&gt;
&lt;p&gt;The CVE added to the blog-post, &lt;a href=&#34;http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2017-3204&#34;&gt;CVE-2017-3204&lt;/a&gt; is an identifier for the
issue, so that other programmers fixing their library-using code have a common
identifier to use in describing what they&amp;rsquo;re responding to, and communicating
this to their customers and users and having folks able to clearly link things
together instead of having to puzzle and decode which set of security issues
some release is claiming to be addressing.  This is, to my understanding, part
of the point of the CVE system.&lt;/p&gt;
&lt;p&gt;There was one tiny snafu in reporting the issue, which I described without
naming names, in how PGP was handled.  The person who had asked me to resend
my email without PGP provided the following within a comment on the
&lt;a href=&#34;https://news.ycombinator.com/item?id=14121780&#34;&gt;HN post&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;99% of the PGP-encrypted emails we get to security@golang.org are bogus
security reports. Whereas &amp;ldquo;cleartext&amp;rdquo; security reports are only about 5-10%
bogus. Getting a PGP-encrypted email to security@golang.org has basically
become a reliable signal that the report is going to be bogus, so I stopped
caring about spending the 5 minutes decrypting the damn thing (logging in to
the key server to get the key, remembering how to use gpg).&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is a somewhat depressing indictment upon the state of affairs with use of
secure communications tool.  I can&amp;rsquo;t fault him for anything he said.  I did
get a laugh out of:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&amp;rsquo;d say it was worth it. This thread was more fun than using gpg, even if
the time savings is a wash.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Context: I have patches in GnuPG, in SKS (the keyserver software) and am
somewhat heavily involved in the PGP keyserver community (wrote the setup
guide which everyone uses today, etc).  I use GnuPG and it&amp;rsquo;s (1) better than
the alternatives; (2) high quality cryptographic engineering; (3) not created
by UX professionals.&lt;/p&gt;
&lt;p&gt;If I&amp;rsquo;d known how bad the stats were for PGP mail to security addresses, I
wouldn&amp;rsquo;t have bothered.  Instead, I&amp;rsquo;ve now pinned &lt;code&gt;golang.org&lt;/code&gt; in my
mail-server as a domain which requires validated TLS when sending mail to the
MX.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;the-vendor&#34;&gt;The Vendor&lt;/h3&gt;
&lt;p&gt;The vendor I mentioned in the original blog-post stepped into the fray and
described their view.  Please remember that &lt;strong&gt;this is not the only vendor with
a security problem here&lt;/strong&gt;.  I found no examples in my searches at the time of
anything doing this right.  I picked one vendor of popular devops tooling,
whose work I respected, and reported it to them.  They&amp;rsquo;re already far better
than most, or I wouldn&amp;rsquo;t have been recommending their tools to start with.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll summarize the vendor&amp;rsquo;s response on HN as &amp;ldquo;we didn&amp;rsquo;t communicate clearly;
we said some things but changed our minds and didn&amp;rsquo;t tell the person
reporting; we have better ways to handle SSH and have always said to use
them&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I was pleased that they repeatedly noted that I was accurate in my portrayal
of their response.&lt;/p&gt;
&lt;p&gt;Their stance about the impact of the problem has improved.&lt;/p&gt;
&lt;p&gt;About Vault, they wrote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With the addition of the ability to generate SSH certificates (which was on
our roadmap for a long time and added in 0.7, prior to both the original
report and the blog post).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;About Packer, they describe that the availability of SSH Certificates makes it
less of an issue.&lt;/p&gt;
&lt;p&gt;I know quite a few people using Packer.  None are willing to say that they&amp;rsquo;re
using SSH certificate generation in image generation.  There&amp;rsquo;s a better way
&lt;em&gt;available&lt;/em&gt; but it&amp;rsquo;s not used by default.  I emphasize this point because this
is the same core issue, reframed, which was in the Golang package: it was
possible to use it correctly, but almost nobody did so and it wasn&amp;rsquo;t the easy
path.&lt;/p&gt;
&lt;p&gt;One small aside: I was an attendee at the vendor&amp;rsquo;s first conference when the
feature of Vault handling SSH for users was introduced.  At the conference
party, I grabbed the author of the feature and probably edged across the line
into rudeness as I described why what they had was not something I&amp;rsquo;d risk
deploying and pointed him at SSH Certificates as a saner path forward without
the same operational failure modes and a much smaller attack surface for those
who want that.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m glad that the vendor put SSH certificates on their roadmap after I
explained what they were and why their first pass implementation was
problematic.  It&amp;rsquo;s good when people listen, put aside the problems incurred by
my forgetting about human social graces, and work to improve things anyway.
That Vault can use SSH Certificates for host and user management is a good
thing.  It&amp;rsquo;s not a replacement for hostkey verification unless and until it&amp;rsquo;s
almost as easy to operationally deploy as is non-CA TOFU SSH where clients
ignore the &amp;ldquo;F&amp;rdquo; in &amp;ldquo;&lt;abbr title=&#34;Trust On First Use&#34;&gt;TOFU&lt;/abbr&gt;&amp;rdquo;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;other-thoughts&#34;&gt;Other Thoughts&lt;/h3&gt;
&lt;p&gt;The quality of the cryptography in Golang is better than in most other
languages.  I&amp;rsquo;ve read the source to a few TLS libraries in my time and
Golang&amp;rsquo;s is by far the nicest, with good encapsulation via &lt;code&gt;crypto/subtle&lt;/code&gt; of
issues such as key-variant timing protection.&lt;/p&gt;
&lt;p&gt;Golang isn&amp;rsquo;t perfect and is sometimes frustrating, but pragmatically it&amp;rsquo;s the
only language I&amp;rsquo;ll use today for a variety of systems programming and security
sensitive tasks.  There are a number of other options in this space too (eg,
Rust) and they&amp;rsquo;re all gaining some traction.  We finally have a realistic shot
at migrating a lot of security-boundary services on Unix-heritage systems to
code written in languages not particularly prone to buffer overflow attacks.&lt;/p&gt;
&lt;p&gt;Things have reached the point where I can take a stance and decline to deploy
on my own systems any new Internet facing servers written in C.  Some of those
which exist now will remain for a while; servers from groups with an excellent
track record (OpenBSD) might cause me to make an exception, based upon their
reputation.&lt;/p&gt;
&lt;p&gt;Sure, there are more security problems than buffer overflows.  But you know
what?  When systemically purging that one class of flaw gets rid of at least
95% of the remote code execution flaws, I&amp;rsquo;ll take it.&lt;/p&gt;
&lt;p&gt;I look at two approaches to programming language security.  On the one hand,
the Golang maintainers have a type-safe language immune from buffer overruns,
with text templating systems designed to auto-escape data appropriately to
avoid various web-based attacks (JS injections etc) and where an API which is
being heavily misused gets changed to force folks to confront their problem
space more fully.  On the other hand, we have C compiler maintainers who
assert that any behavior not defined in the C standard is not something where
they have to pick one behavior and stick with it, but can instead declare it
an impossible situation, that code which relies upon common assumptions is
buggy, and proceed to &lt;em&gt;optimize away the security checks&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If I program in C, I need to defend against the compiler maintainers.&lt;br&gt;
If I program in Go, the language maintainers defend me from my mistakes.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re choosing software to deploy on your systems,
which language would you rather that the vendors be writing in?&lt;/p&gt;
&lt;hr&gt;
&lt;div class=&#34;retrocomment&#34;&gt;
2024-12: A server-side vulnerability in misuse of the `PublicKeyCallback` API
has echoes of this issue:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://nvd.nist.gov/vuln/detail/CVE-2024-45337&#34;&gt;CVE-2024-45337&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://platform.sh/blog/uncovered-and-patched-golang-vunerability/&#34;&gt;https://platform.sh/blog/uncovered-and-patched-golang-vunerability/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://groups.google.com/g/golang-announce/c/-nPEi39gI4Q&#34;&gt;https://groups.google.com/g/golang-announce/c/-nPEi39gI4Q&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</description>
    </item>
    
  </channel>
</rss>
