unixdude.nethttps://www.unixdude.net/2024-01-28T00:00:00-05:00Solenoid installs2024-01-28T00:00:00-05:002024-01-28T00:00:00-05:00Danieltag:www.unixdude.net,2024-01-28:/posts/2024/Jan/28/solenoid-installs/<p>A few weeks ago, I installed a solenoid in my truck, to control my ham
radio's power based on the ignition key position: I want the radio to
power on and off with the engine.</p>
<p>As with most mobile ham radios, my radio has an auto-power-down function,
but I want …<a class="label label-primary read-more" href="/posts/2024/Jan/28/solenoid-installs/"><span>Continue reading</span></a></p><p>A few weeks ago, I installed a solenoid in my truck, to control my ham
radio's power based on the ignition key position: I want the radio to
power on and off with the engine.</p>
<p>As with most mobile ham radios, my radio has an auto-power-down function,
but I want it to power on as well. At the suggestion of a friend, I
purchased <a href="https://www.amazon.com/dp/B0BJ9RQFWX">these solenoids</a>.
I don't know if all ham radios do this, but mine power up when they
regain power, if they were powered on when they lost power. So, the
solenoid will power my radio up and down.</p>
<p>This solenoid is easy to use, as can be seen by the wiring diagram:</p>
<p><img alt="wiring diagram" src="/images/2024/solenoids/wiring_diagram.jpg"></p>
<p>The hardest part of this type of job is getting through the firewall.
Fortunately, I had already done that when I <a href="/posts/2022/Sep/25/ft-7800r-install/">wired up my FT-7800R in
my truck</a>. Installing this
solenoid involved only some rewiring, and placement of the solenoid.</p>
<p>First, we had to find a switched trigger source. We searched the
fuse box and found an unused pin that was powered only when the ignition
was switched on, so we used that, wiring it to the "positive trigger source"
pin on the solenoid.</p>
<p><img alt="trigger source" src="/images/2024/solenoids/IMG_1119.jpeg"></p>
<p><img alt="installed solenoid" src="/images/2024/solenoids/IMG_1121.jpeg"></p>
<p>The radio ground was unchanged. The radio power source was changed from
battery to solenoid, and the solenoid is powered directly from the battery.
It's a pretty simple install, as you can see.</p>
<hr>
<p>Yesterday I added a solenoid to my wife's 2010 Lincoln Navigator. I did this
because we now have a third driver in the family, and I wanted a dashcam to
have some video in case we need it.</p>
<p>At a friend's suggestion, we bought <a href="https://www.amazon.com/dp/B07VCHVTCM">this dashcam</a>.
I'm not yet going to recommend this camera, but it seems okay so far. In any case,
yesterday I finished the install, with the same professional-grade install that
I did for the radio.</p>
<p>First, we had to find a switched trigger source. We were able to locate an
unused (and powered!) fuse slot in the fuse box, so we used this adapter to make our own
circuit. This goes to the solenoid positive trigger pin. Note that we also
get the positive power line from the fuse box too.</p>
<p><img alt="fuse box" src="/images/2024/solenoids/IMG_1117.jpeg"></p>
<p>We located the solenoid on the firewall at the top of the engine compartment,
similar in location to where we did it on the F-150.</p>
<p><img alt="solenoid" src="/images/2024/solenoids/IMG_1113.jpeg"></p>
<p>As with the F-150, the hardest part of this job was pulling cables through
the firewall. (The next hardest part was pulling them behind the A-pillar
trim.)</p>
<p><img alt="firewall" src="/images/2024/solenoids/IMG_1114.jpeg"></p>
<p>This camera came with a 12v power plug, and I didn't want to cut that off,
so I bought a <a href="https://www.amazon.com/dp/B07H1MGWFN">12v socket</a>. I then
soldered that to the wires we passed through the firewall, and I plugged
the camera into this. Then I zip-tied this to a U-shaped beam under the
dash.</p>
<p><img alt="12v socket" src="/images/2024/solenoids/IMG_1111.jpeg"></p>
<p><img alt="beam" src="/images/2024/solenoids/IMG_1108.jpeg"></p>
<p>All of the wires are hidden behind trim of course. Camera is suction-cup
mounted to the windshield, and now it powers on with the ignition key.</p>
<p><img alt="camera" src="/images/2024/solenoids/IMG_1118.jpeg"></p>ESPHome home control2024-01-15T00:00:00-05:002024-01-15T00:00:00-05:00Danieltag:www.unixdude.net,2024-01-15:/posts/2024/Jan/15/esphome-home-control/<p>I have continued playing with my M5stack. I now have it programmed to show
3 types of data:</p>
<ol>
<li>The local weather forecast for the next several days; either the 5-day
forecast, or a single day at a time.</li>
<li>The temperature and humidity readings in my home office and garage,
or …</li><a class="label label-primary read-more" href="/posts/2024/Jan/15/esphome-home-control/"><span>Continue reading</span></a></ol><p>I have continued playing with my M5stack. I now have it programmed to show
3 types of data:</p>
<ol>
<li>The local weather forecast for the next several days; either the 5-day
forecast, or a single day at a time.</li>
<li>The temperature and humidity readings in my home office and garage,
or the reading from a single location.</li>
<li>The status of my Kasa Smart outlets. This view also controls some of the
outlets.</li>
</ol>
<p>I already posted about the weather forecast. This post is about the
other 2 items.</p>
<p>My ESP32 in the garage has a PIR sensor as well as a DHT11. The PIR sensor
is used to detect motion, which then initiates an automation to turn on the
garage light. This has been super helpful, because we do not have to leave
the garage light on: it always turns on when we enter
the garage. The DHT11 provides temperature and humidity readings in the garage.</p>
<p>One of the reasons I wanted the M5stack was to be able to use it as a remote
control for the house, and I have now achieved that. The code for this is
a mess, but it can be found <a href="https://github.com/ataridude/ESPHome/blob/main/m5stack_home_control.yml">here</a>.</p>
<p>I would prefer to use arrays for things like the names of the
switches (both my "friendly name" and the Home Assistant name), and I would prefer
to use array length to know when the last item is being viewed. I assume it is
possible to do this in ESPHome, but I don't know how to do it yet.</p>
<p>In any case, I can now cycle through the lights, and I can control all but the
garage light (because there's no need to control this one manually). This functionality was suprisingly easy
to add tonight, and it is super cool: Now I can carry my M5stack, and push a few
buttons to control the lights around the house.</p>ESPHome weather display on an m5stack2023-12-10T00:00:00-05:002023-12-10T00:00:00-05:00Danieltag:www.unixdude.net,2023-12-10:/posts/2023/Dec/10/esphome-weather-display-on-an-m5stack/<p>After using a couple of ESP32s and doing some things with them, I
decided I wanted to get an M5stack. I bought a <a href="https://shop.m5stack.com/products/esp32-basic-core-lot-development-kit-v2-7">first generation
core</a>,
and am working to use that as a display for various things around
the house.</p>
<p>As I started using the m5stack and trying to …<a class="label label-primary read-more" href="/posts/2023/Dec/10/esphome-weather-display-on-an-m5stack/"><span>Continue reading</span></a></p><p>After using a couple of ESP32s and doing some things with them, I
decided I wanted to get an M5stack. I bought a <a href="https://shop.m5stack.com/products/esp32-basic-core-lot-development-kit-v2-7">first generation
core</a>,
and am working to use that as a display for various things around
the house.</p>
<p>As I started using the m5stack and trying to do this project,
I did not find a complete ESPHome m5stack "hello world" type program, or a
complete ESPHome weather "hello world" type program, so in this
project and posting, I create both of those. I uploaded the ESPHome code
to <a href="https://github.com/ataridude/ESPHome">my github repository</a>.</p>
<p>Note from my code that I am using font Roboto-Medium.ttf. You can use any font
you like, just make sure it is uploaded to your device.</p>
<p>Also, hopefully others find this as useful as I intend it to be, and as useful as I
would have found something like this, when I started on this project.</p>
<p>I decided my first project would be a weather forecast display. This
is a project that many people have done, and tonight I was able to
get the data to my M5stack.</p>
<p>As I said, I consider this a "Hello World" type program, and I post it here
because it took me a while to piece this all together. I found lots of
writeups from people who had done this project, but they seemed to be
assuming knowledge that I didn't have; hopefully this post includes
everything a new user needs to get this done.</p>
<p>First, you need to create a sensor in Home Assistant. To do this, edit
your <code>/homeassistant/configuration.yaml</code> file. I added the following code to mine,
using the Home Assistant File editor. If you do not have the File editor installed,
you can add it as an Add-on.</p>
<div class="highlight"><pre><span></span><code><span class="x">sensor:</span>
<span class="x"> - platform: template</span>
<span class="x"> sensors:</span>
<span class="x"> wx_forecast:</span>
<span class="x"> friendly_name: "Local forecast"</span>
<span class="x"> value_template: >-</span>
<span class="x"> </span><span class="cp">{%</span> <span class="k">set</span> <span class="nv">weather</span> <span class="o">=</span> <span class="o">{</span>
<span class="s2">"sunny"</span><span class="o">:</span> <span class="s2">"sunny"</span><span class="o">,</span>
<span class="s2">"clear-day"</span><span class="o">:</span> <span class="s2">"clear-day"</span><span class="o">,</span>
<span class="s2">"clear-night"</span><span class="o">:</span> <span class="s2">"clear-night"</span><span class="o">,</span>
<span class="s2">"cloudy"</span><span class="o">:</span> <span class="s2">"cloudy"</span><span class="o">,</span>
<span class="s2">"rainy"</span><span class="o">:</span> <span class="s2">"rainy"</span><span class="o">,</span>
<span class="s2">"sleet"</span><span class="o">:</span> <span class="s2">"sleet"</span><span class="o">,</span>
<span class="s2">"snow"</span><span class="o">:</span> <span class="s2">"snow"</span><span class="o">,</span>
<span class="s2">"wind"</span><span class="o">:</span> <span class="s2">"wind"</span><span class="o">,</span>
<span class="s2">"fog"</span><span class="o">:</span> <span class="s2">"fog"</span><span class="o">,</span>
<span class="s2">"partlycloudy"</span><span class="o">:</span> <span class="s2">"partlycloudy"</span><span class="o">,</span>
<span class="o">}</span> <span class="cp">%}</span>
<span class="x"> </span><span class="cp">{%</span> <span class="k">for</span> <span class="nv">state</span> <span class="k">in</span> <span class="nv">states.weather.forecast_home.attributes.forecast</span><span class="o">[</span><span class="m">1</span><span class="o">:</span><span class="m">6</span><span class="o">]</span> -<span class="cp">%}</span>
<span class="x"> </span><span class="cp">{{</span> <span class="nv">as_timestamp</span><span class="o">(</span><span class="nv">state.datetime</span><span class="o">)|</span> <span class="nf">timestamp_custom</span><span class="o">(</span><span class="s2">"%a"</span><span class="o">)</span> <span class="cp">}}</span><span class="x">;</span><span class="cp">{{</span><span class="nv">state.templow</span><span class="cp">}}</span><span class="x">;</span><span class="cp">{{</span> <span class="nv">state.temperature</span> <span class="cp">}}</span><span class="x">;</span><span class="cp">{{</span> <span class="nv">state.precipitation</span> <span class="cp">}}</span><span class="x">in;</span><span class="cp">{{</span> <span class="nv">weather</span><span class="o">[</span><span class="nv">state.condition</span><span class="o">]</span> <span class="cp">}}</span><span class="x">#</span>
<span class="x"> </span><span class="cp">{%</span>- <span class="k">endfor</span> <span class="cp">%}</span>
</code></pre></div>
<p>This code is basically copied from <a href="https://community.home-assistant.io/t/selected-forecast-items-to-esp32-epaper-display/307976/9">this post</a>, but I removed the day name translation. I could also remove the weather hash, but I kept that for now.
I also changed the format slightly - I separate the low and high temperatures into separate fields.</p>
<p>After I edited the <code>configuration.yaml</code> file, I checked and then reloaded the configuration on the Developer Tools page, YAML tab.
After the reload, I confirmed the existence of my state, by going to the States tab on the Developer Tools page, and
filtering states for <code>wx_forecast</code>. This completes and confirms the HA side.</p>
<p>Then, in ESPHome, I added the sensor to my m5stack configuration:</p>
<div class="highlight"><pre><span></span><code>text_sensor:
<span class="k">-</span> platform: homeassistant
id: w_forecast
entity_id: sensor.wx_forecast
</code></pre></div>
<p>My display code is basically copied from the same post as above. Here is my display lambda code:</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="n">std</span><span class="p">::</span><span class="n">string</span><span class="w"> </span><span class="n">fivedays</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">w_forecast</span><span class="p">)</span><span class="o">.</span><span class="n">state</span><span class="p">;</span>
<span class="w"> </span><span class="n">std</span><span class="p">::</span><span class="n">vector</span><span class="o"><</span><span class="n">std</span><span class="p">::</span><span class="n">string</span><span class="o">></span><span class="w"> </span><span class="n">five</span><span class="p">;</span>
<span class="w"> </span><span class="n">std</span><span class="p">::</span><span class="n">vector</span><span class="o"><</span><span class="n">std</span><span class="p">::</span><span class="n">string</span><span class="o">></span><span class="w"> </span><span class="n">v</span><span class="p">;</span>
<span class="w"> </span><span class="n">ESP_LOGD</span><span class="p">(</span><span class="s2">"fivedays [</span><span class="si">%s</span><span class="s2">]"</span><span class="p">,</span><span class="w"> </span><span class="n">fivedays</span><span class="o">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="w"> </span><span class="n">five</span><span class="o">.</span><span class="n">clear</span><span class="p">();</span>
<span class="w"> </span><span class="nb nb-Type">int</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="nb nb-Type">int</span><span class="w"> </span><span class="n">wx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="n">position</span><span class="w"> </span><span class="n">x</span>
<span class="w"> </span><span class="nb nb-Type">int</span><span class="w"> </span><span class="n">wy</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="n">position</span><span class="w"> </span><span class="n">y</span>
<span class="w"> </span><span class="nb">char</span><span class="w"> </span><span class="o">*</span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">strtok</span><span class="p">(</span><span class="n">const_cast</span><span class="o"><</span><span class="nb">char</span><span class="o">*></span><span class="p">(</span><span class="n">fivedays</span><span class="o">.</span><span class="n">c_str</span><span class="p">()),</span><span class="w"> </span><span class="s2">"#"</span><span class="p">);</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="n">splits</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">string</span><span class="w"> </span><span class="p">(</span><span class="n">I</span><span class="w"> </span><span class="n">believe</span><span class="p">,</span><span class="w"> </span><span class="n">I</span><span class="w"> </span><span class="s2">"found"</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">code</span><span class="p">)</span>
<span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">NULL</span><span class="p">)</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">five</span><span class="o">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">token</span><span class="p">);</span>
<span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">strtok</span><span class="w"> </span><span class="p">(</span><span class="n">NULL</span><span class="p">,</span><span class="w"> </span><span class="s2">"#"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">here</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">days</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">std</span><span class="p">::</span><span class="n">string</span><span class="w"> </span><span class="n">fiv</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">five</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="o">//</span><span class="n">it</span><span class="o">.</span><span class="n">rectangle</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">wy</span><span class="p">,</span><span class="w"> </span><span class="mi">128</span><span class="p">,</span><span class="w"> </span><span class="mi">59</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">adds</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">border</span><span class="w"> </span><span class="n">around</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="s2">"day"</span>
<span class="w"> </span><span class="o">//</span><span class="n">it</span><span class="o">.</span><span class="n">rectangle</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">wy</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">126</span><span class="p">,</span><span class="w"> </span><span class="mi">57</span><span class="p">);</span>
<span class="w"> </span><span class="n">std</span><span class="p">::</span><span class="n">string</span><span class="w"> </span><span class="nb">str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span>
<span class="w"> </span><span class="nb">str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fiv</span><span class="p">;</span>
<span class="w"> </span><span class="n">ESP_LOGD</span><span class="p">(</span><span class="s2">"test: "</span><span class="p">,</span><span class="w"> </span><span class="s2">"String to Vector: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span><span class="w"> </span><span class="nb">str</span><span class="o">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="w"> </span><span class="n">v</span><span class="o">.</span><span class="n">clear</span><span class="p">();</span>
<span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">strtok</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="nb">str</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="s2">";"</span><span class="p">);</span>
<span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">NULL</span><span class="p">)</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">v</span><span class="o">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">token</span><span class="p">);</span>
<span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">strtok</span><span class="w"> </span><span class="p">(</span><span class="n">NULL</span><span class="p">,</span><span class="w"> </span><span class="s2">";"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="s2">"day"</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">std</span><span class="p">::</span><span class="n">string</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="p">){</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Day</span><span class="w"> </span><span class="p">(</span><span class="n">Mon</span><span class="o">/</span><span class="n">Tue</span><span class="o">...</span><span class="p">)</span>
<span class="w"> </span><span class="n">it</span><span class="o">.</span><span class="n">printf</span><span class="p">(</span><span class="n">wx</span><span class="p">,</span><span class="w"> </span><span class="n">wy</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">font_roboto_medium22</span><span class="p">),</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">my_red</span><span class="p">),</span><span class="w"> </span><span class="n">TextAlign</span><span class="p">::</span><span class="n">TOP_LEFT</span><span class="p">,</span><span class="w"> </span><span class="s2">"</span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="w"> </span><span class="p">}</span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">){</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Temperature</span>
<span class="w"> </span><span class="n">it</span><span class="o">.</span><span class="n">printf</span><span class="p">(</span><span class="n">wx</span><span class="w"> </span><span class="o">+</span><span class="mi">60</span><span class="p">,</span><span class="w"> </span><span class="n">wy</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">font_roboto_medium22</span><span class="p">),</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">my_red</span><span class="p">),</span><span class="w"> </span><span class="n">TextAlign</span><span class="p">::</span><span class="n">TOP_LEFT</span><span class="p">,</span><span class="w"> </span><span class="s2">"</span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="w"> </span><span class="p">}</span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">2</span><span class="p">){</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Precipitation</span>
<span class="w"> </span><span class="n">it</span><span class="o">.</span><span class="n">printf</span><span class="p">(</span><span class="n">wx</span><span class="w"> </span><span class="o">+</span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="n">wy</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">font_roboto_medium22</span><span class="p">),</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">my_red</span><span class="p">),</span><span class="w"> </span><span class="n">TextAlign</span><span class="p">::</span><span class="n">TOP_LEFT</span><span class="p">,</span><span class="w"> </span><span class="s2">"</span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="w"> </span><span class="p">}</span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">3</span><span class="p">){</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">weather</span><span class="w"> </span><span class="n">icon</span>
<span class="w"> </span><span class="n">it</span><span class="o">.</span><span class="n">printf</span><span class="p">(</span><span class="n">wx</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">135</span><span class="p">,</span><span class="w"> </span><span class="n">wy</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">font_roboto_medium22</span><span class="p">),</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">my_red</span><span class="p">),</span><span class="w"> </span><span class="n">TextAlign</span><span class="p">::</span><span class="n">TOP_LEFT</span><span class="p">,</span><span class="w"> </span><span class="s2">"</span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="w"> </span><span class="p">}</span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">4</span><span class="p">){</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">weather</span><span class="w"> </span><span class="n">icon</span>
<span class="w"> </span><span class="n">it</span><span class="o">.</span><span class="n">printf</span><span class="p">(</span><span class="n">wx</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">210</span><span class="p">,</span><span class="w"> </span><span class="n">wy</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">font_roboto_medium22</span><span class="p">),</span><span class="w"> </span><span class="n">id</span><span class="p">(</span><span class="n">my_red</span><span class="p">),</span><span class="w"> </span><span class="n">TextAlign</span><span class="p">::</span><span class="n">TOP_LEFT</span><span class="p">,</span><span class="w"> </span><span class="s2">"</span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">ESP_LOGD</span><span class="p">(</span><span class="s2">"test: "</span><span class="p">,</span><span class="w"> </span><span class="s2">"String to Vector: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="o">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="n">wy</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">25</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">down</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="n">pixels</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">output</span><span class="w"> </span><span class="n">next</span><span class="w"> </span><span class="n">day</span>
<span class="w"> </span><span class="p">}</span>
</code></pre></div>
<p>The result of all this work is a weather forecast on my m5stack:</p>
<p><img alt="m5stack" src="/images/m5stack/first_weather.jpg"></p>
<p>Now that I have the data in my m5stack, I will set out to get the display how I want it.</p>Home Automation using Home Assistant2023-11-28T00:00:00-05:002023-11-28T00:00:00-05:00Danieltag:www.unixdude.net,2023-11-28:/posts/2023/Nov/28/home-automation-using-home-assistant/<p>A few weeks back I mentioned Home Assistant. After I set that up, I
bought some Kasa smart outlets. Two have replaced timers, so I no longer
have to change the start times as the time of sunset changes throughout
the year. A third is set up in my home …<a class="label label-primary read-more" href="/posts/2023/Nov/28/home-automation-using-home-assistant/"><span>Continue reading</span></a></p><p>A few weeks back I mentioned Home Assistant. After I set that up, I
bought some Kasa smart outlets. Two have replaced timers, so I no longer
have to change the start times as the time of sunset changes throughout
the year. A third is set up in my home office, and is set to come on an
hour before sunset. I installed this plug because I often find myself
in a dark office at the end of my work day. I don't even notice it
getting dark outside because I'm staring at lighted screens, so this
keeps me out of the darkness when I shut down.</p>
<p>A couple days ago, I installed the 4th plug. This one is in the garage, and it now
switches one of the two LED shop lights that I installed on Saturday. The
other LED shop light is switched inside the house. Originally, both
of those LED shop lights were switched inside the house, but that is
two 5,000 lumen shop lights -- and those replaced two 825-lumen compact
fluorescent bulbs -- so it was extremely bright, much brighter than we
were used to. I moved one of the
shop lights to the Kasa switched outlet, which cuts down on the brightness,
but means that I would have to manually power the second light, from the app on
my phone.</p>
<p>I happened to have a spare ESP32 and a HC-SR501 motion sensor. A quick
web search revealed <a href="https://esp32io.com/tutorials/esp32-motion-sensor">this tutorial</a>,
which I used to get started. I set my sensor for maximum
time delay as well as maximum range. I configured the sensor in Home Assistant,
then, added two automations for the sensor: turn on the power to the garage
light smart plug when motion is detected, and turn off the power when motion
is no longer reported. With the long time delay, we get several minutes of
light before it turns off.</p>
<p>This entire setup and automation took me maybe 30 minutes, probably much less,
and this is one reason why I like ESPHome: It is easy to automate with an ESP32
and a sensor. The entire config is in this one stanza:</p>
<div class="highlight"><pre><span></span><code>binary_sensor:
<span class="k">-</span> platform: gpio
pin: 13
name: "motion_sensor"
device_class: motion
</code></pre></div>
<p>The automations are super simple too: I set up a trigger of "garage_sensor motion_sensor
started detecting motion," and the action is to turn on the outlet. I have a related
automation with a trigger of "garage_sensor motion_sensor stopped detecting motion."</p>
<p>ESP32s are so inexpensive, too, which makes automation like this super affordable. Now I'm
trying to decide what other automation projects I will do. I think I will automate
garage door open detection, as well as closing it. I also want to automate the lights on
the 2nd/3rd floor staircase: the stairs are very dark. I'm sure I will come up with
other ideas as well.</p>Home Assistant; Ham radio digital voice2023-11-05T00:00:00-04:002023-11-05T00:00:00-04:00Danieltag:www.unixdude.net,2023-11-05:/posts/2023/Nov/05/home-assistant-ham-radio-digital-voice/<p>A while ago, I bought a <a href="https://www.ebay.com/itm/302057854467">Freenove starter kit</a>. I
never did much with it until recently, when a friend gave me an <a href="https://en.wikipedia.org/wiki/ESP32">ESP32</a>
and pointed me to <a href="https://www.home-assistant.io">Home Assistant</a>.</p>
<p>The Freenove kit included a DHT11 temperature/humidity module, so my first Home Assistant
project was to set up a …<a class="label label-primary read-more" href="/posts/2023/Nov/05/home-assistant-ham-radio-digital-voice/"><span>Continue reading</span></a></p><p>A while ago, I bought a <a href="https://www.ebay.com/itm/302057854467">Freenove starter kit</a>. I
never did much with it until recently, when a friend gave me an <a href="https://en.wikipedia.org/wiki/ESP32">ESP32</a>
and pointed me to <a href="https://www.home-assistant.io">Home Assistant</a>.</p>
<p>The Freenove kit included a DHT11 temperature/humidity module, so my first Home Assistant
project was to set up a temperature/humidity sensor for my home office. It was much easier
than I thought it would be: I downloaded and installed the <a href="https://www.home-assistant.io/installation/alternative">Home Assistant OVA</a>, then I installed <a href="https://esphome.io">ESPHome</a> on the ESP32, then
I configured the ESP32 with the DHT11 device.</p>
<p>This is inexpensive, useful, and cool. I can see me buying many of these ESP32 or similar devices,
using them to monitor and control various things around the house. And, with the
<a href="https://esphome.io/components/wireguard">WireGuard support in ESPHome</a>,
I will be able to monitor remote devices as well, such as the temperature in my travel trailer,
which is parked at a friend's house.</p>
<hr>
<p>Another thing I have been playing with recently is digital voice in ham radio. A friend
lent me a <a href="https://www.yaesu.com/indexVS.cfm?cmd=DisplayProducts&ProdCatID=111&encProdID=7CDB93B02164B1FB036530FBD7D37F1A&DivisionID=65&isArchived=0">Yaesu FT-70D</a>
and a <a href="https://www.onallbands.com/what-you-need-to-know-about-mmdvm-hotspots/">MMDVM hotspot</a> running
<a href="https://w0chp.radio/wpsd/">WPSD</a>. After attaching the hotspot to my home WLAN, and a small bit of
configuration, I have the FT-70D connected to the hotspot, and have a world of digital voice radio
available to me, through the many <a href="https://w0chp.radio/ysf-reflectors/">YSF reflectors</a> that exist.</p>
<p>In addition to the ESP32 modules and sensors mentioned above, the FT-70D and a MMDVM hotspot are now
on my list.</p>IC-7300 external keypad2023-10-03T00:00:00-04:002023-10-03T00:00:00-04:00Danieltag:www.unixdude.net,2023-10-03:/posts/2023/Oct/03/ic-7300-external-keypad/<p>I recently purchased an <a href="https://www.icomamerica.com/lineup/products/IC-7300/">Icom IC-7300</a> ham radio for use at home, and when
researching the radio around the time of purchase, I discovered
<a href="https://www.youtube.com/playlist?list=PL48JZWhCJoH3bGOyfmZVxgRHFqs2VUG8P">this excellent YouTube video playlist</a>,
covering many aspects of the radio.</p>
<p>When I got to <a href="https://www.youtube.com/watch?v=I3b1aNdg3pQ">video 46</a>,
I discovered that the IC-7300 supports an external keypad …<a class="label label-primary read-more" href="/posts/2023/Oct/03/ic-7300-external-keypad/"><span>Continue reading</span></a></p><p>I recently purchased an <a href="https://www.icomamerica.com/lineup/products/IC-7300/">Icom IC-7300</a> ham radio for use at home, and when
researching the radio around the time of purchase, I discovered
<a href="https://www.youtube.com/playlist?list=PL48JZWhCJoH3bGOyfmZVxgRHFqs2VUG8P">this excellent YouTube video playlist</a>,
covering many aspects of the radio.</p>
<p>When I got to <a href="https://www.youtube.com/watch?v=I3b1aNdg3pQ">video 46</a>,
I discovered that the IC-7300 supports an external keypad, and decided
I wanted one of my own. This past weekend, I made one.</p>
<p>In addition to using that video, I found <a href="http://www.whiskeytangohotel.com/2020/03/icom-ic7300-memory-external-keypad.html">this page</a>
detailing another guy's external keypad build.</p>
<p>I don't have a drill press, but I think my unit came out well. I could have done a better job with the feet, but this works well enough.</p>
<p><img alt="1" src="/images/ic-7300/1.jpg"></p>
<p><img alt="2" src="/images/ic-7300/2.jpg"></p>
<p><img alt="3" src="/images/ic-7300/3.jpg"></p>
<p><img alt="4" src="/images/ic-7300/4.jpg"></p>
<p><img alt="5" src="/images/ic-7300/5.jpg"></p>
<p><img alt="6" src="/images/ic-7300/6.jpg"></p>Mobile Wireguard clients2023-07-02T00:00:00-04:002023-07-02T00:00:00-04:00Danieltag:www.unixdude.net,2023-07-02:/posts/2023/Jul/02/mobile-wireguard-clients/<p>I have been using Wireguard for about a year & a half, when I switched
from ZeroTier to Wireguard for most of my needs. Unfortunately, since
the start, my Wireguard configuration has been completely manual, and
very annoying since I had to create a new interface for every client.</p>
<p>No more …<a class="label label-primary read-more" href="/posts/2023/Jul/02/mobile-wireguard-clients/"><span>Continue reading</span></a></p><p>I have been using Wireguard for about a year & a half, when I switched
from ZeroTier to Wireguard for most of my needs. Unfortunately, since
the start, my Wireguard configuration has been completely manual, and
very annoying since I had to create a new interface for every client.</p>
<p>No more.</p>
<p>Today I used the <a href="https://www.wireguardconfig.com">Wireguard Config Generator</a>
to generate the configs (I did not specify my endpoint on the website
of course). The result was a single server config with keys for two dozen
mobile clients. Today I configured three of those mobile clients -- one each for
my MacBook Air, my iPhone, and my iPad.</p>
<p>I considered using <a href="https://www.netmaker.io">Netmaker</a> but decided it was way more than
I needed. The config generator made that part of the config easy. Loading the config
into the Mac client was easy. Loading a config file into the iOS client is not difficult,
but it was way more fun using <a href="https://www.cyberciti.biz/faq/how-to-generate-wireguard-qr-code-on-linux-for-mobile/">qrencode</a> to generate a QR code to configure the iOS devices.</p>
<p>After I used the Wireguard Config Generator, I updated the client files to specify my endpoint,
then I loaded the configs and was off to the races -- with that part anyway.</p>
<p>I had one more step to make this useful. The entire point of my Wiregaurd config is to
remotely access my home network. But, since my Wireguard server is on a DigialOcean VM,
and I want my systems to access my home network without masquerading, this required
a configuration change to my FRR config on my DigitalOcean VM.</p>
<p>I just had to add two blocks:</p>
<div class="highlight"><pre><span></span><code><span class="nv">interface</span><span class="w"> </span><span class="nv">wg0</span>
<span class="w"> </span><span class="nv">ip</span><span class="w"> </span><span class="nv">ospf</span><span class="w"> </span><span class="nv">network</span><span class="w"> </span><span class="nv">non</span><span class="o">-</span><span class="nv">broadcast</span>
<span class="k">exit</span>
</code></pre></div>
<p>To the <code>router ospf</code> configuration, I added the subnet I dedicated to my Wireguard clients:</p>
<div class="highlight"><pre><span></span><code>network 192.168.100.128/27 area 0
</code></pre></div>
<p>Now everything can route to my Wireguard clients, and my Wireguard clients can access my
entire network.</p>POTA kit2023-06-14T00:00:00-04:002023-06-14T00:00:00-04:00Danieltag:www.unixdude.net,2023-06-14:/posts/2023/Jun/14/pota-kit/<p><a href="https://parksontheair.com">Parks On The Air</a>, or POTA, is one of the aspects of amateur radio that I have been
enjoying -- both hunting and activating. Ever since I starting doing POTA activations, I have been carrying my
equipment in a Rubbermaid container, which is bulky and heavy. The only thing it has …<a class="label label-primary read-more" href="/posts/2023/Jun/14/pota-kit/"><span>Continue reading</span></a></p><p><a href="https://parksontheair.com">Parks On The Air</a>, or POTA, is one of the aspects of amateur radio that I have been
enjoying -- both hunting and activating. Ever since I starting doing POTA activations, I have been carrying my
equipment in a Rubbermaid container, which is bulky and heavy. The only thing it has going for it is
that it carries everything I need.</p>
<p>Pretty much since getting started doing POTA, I have wanted a battery box and a radio box. I did a lot
of research about battery boxes, and there are many different options, both <a href="https://duckduckgo.com/?q=diy+battery+box">DIY</a> and <a href="https://powerwerx.com/solar-portable-power">ready-made ones for purchase</a>. Somewhere I saw a build
that used the <a href="https://www.harborfreight.com/tool-storage-organization/tool-boxes-bags-belts/utility-cases-ammo-boxes/tactical-ammoutility-box-64113.html">Harbor Freight Tactical Ammo Box</a>, and
I decided to use that box for my build. I went with this box because I really like the idea of having all of the controls
and connections under the flip-top lid on that box, rather than on the outside of the box, which is how
most of these boxes are built.</p>
<p>Over the weekend, with the help of a local ham friend, I built my battery box. Installed on my box are
dual PowerPole connectors, dual USB connectors behind a rocker switch and 5A fuse, and a 50A circuit breaker as the main power switch. There is room for more connections should I choose to add them.</p>
<p>The Harbor Freight box I used has the benefit of being a little larger than the typical battery box, so in
addition to the <a href="https://www.bioennopower.com/collections/lifepo4-batteries-for-communication-equipment-ham-radio/products/copy-of-12v-15ah-lfp-battery-pvc-blf-1215w?variant=19610918405">Bioenno BLF-1215A</a>,
I am also able to carry my 40/20/15/10 EFHW <a href="https://www.thingiverse.com/thing:5448369">antenna</a>,
<a href="https://www.amazon.com/gp/product/B07Z5VY7B6/">NanoVNA</a>, <a href="https://www.amazon.com/Upgraded-Precision-Consumption-Performance-Backlight/dp/B0B8VWNY9X/">battery monitor</a>, and Bioenno battery charger -- all inside the battery box. Today, the ability to
carry all these extra items seems like a benefit; time will tell if that impression holds.</p>
<p>I also finally cut out the foam in my <a href="https://www.harborfreight.com/3800-weatherproof-protective-case-large-black-63927.html">Apache 3800</a>
case to carry my radio (an <a href="http://www.icomamerica.com/en/products/amateur/hf/706/default.aspx">Icom IC-706MKIIG</a>), along with its microphone and power cord, and <a href="https://www.niftyaccessories.com/IC-706MKIIG.php">nifty mini-manual</a>.</p>
<p>I am very pleased with the result for both of these, and look forward to using this kit at an upcoming POTA activation.</p>
<p><img alt="case" src="/images/battery_box/IMG_9556.jpeg"></p>
<p><img alt="case" src="/images/battery_box/IMG_9557.jpeg"></p>
<p><img alt="case" src="/images/battery_box/IMG_9558.jpeg"></p>
<p><img alt="case" src="/images/battery_box/IMG_9559.jpeg"></p>
<p><img alt="case" src="/images/battery_box/IMG_9560.png"></p>
<p><img alt="case" src="/images/battery_box/radio_1.png"></p>
<p><img alt="case" src="/images/battery_box/radio_2.png"></p>
<p><img alt="case" src="/images/battery_box/radio_3.jpeg"></p>
<p><img alt="case" src="/images/battery_box/both.jpeg"></p>DKIM, Postfix, and FreeBSD2023-05-10T00:00:00-04:002023-05-10T00:00:00-04:00Danieltag:www.unixdude.net,2023-05-10:/posts/2023/May/10/dkim-postfix-and-freebsd/<p>Recently, I discovered that I was not able to email my wife's mac.com address from my
mail server here. <a href="https://support.apple.com/en-us/HT204137">Apple's support document</a> indicates
that they require DKIM and SPF, and that they support DMARC. I have long had
<a href="/posts/2019/Feb/14/postfix_relay/">SPF configured</a>, but I had not configured DKIM or DMARC.</p>
<p>Over …<a class="label label-primary read-more" href="/posts/2023/May/10/dkim-postfix-and-freebsd/"><span>Continue reading</span></a></p><p>Recently, I discovered that I was not able to email my wife's mac.com address from my
mail server here. <a href="https://support.apple.com/en-us/HT204137">Apple's support document</a> indicates
that they require DKIM and SPF, and that they support DMARC. I have long had
<a href="/posts/2019/Feb/14/postfix_relay/">SPF configured</a>, but I had not configured DKIM or DMARC.</p>
<p>Over the weekend, I set up DKIM. DKIM is basically a public-key cryptographic solution used
to confirm the server that sent an email. In order to set this up on my FreeBSD 12 server,
I followed <a href="https://www.prado.it/2012/04/26/how-to-run-postfix-with-opendkim-on-freebsd-9-0/">this guide</a>
which is pretty close to what I ended up doing, and I took some inspiration from
<a href="https://www.dan.me.uk/blog/2016/06/01/add-dkim-signing-to-freebsd-servers/">this guide</a>.
Even with these guides, I had to make a few changes to meet with success on my FreeBSD 12 system.</p>
<p>I chose to install the package, not using source: <code>pkg install opendkim</code></p>
<p>Next, instead of adding the user to the <code>mail</code> group, I left off that parameter:</p>
<div class="highlight"><pre><span></span><code><span class="n">pw</span><span class="w"> </span><span class="n">useradd</span><span class="w"> </span><span class="o">-</span><span class="n">n</span><span class="w"> </span><span class="n">opendkim</span><span class="w"> </span><span class="o">-</span><span class="n">d</span><span class="w"> </span><span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">opendkim</span><span class="w"> </span><span class="o">-</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="n">s</span><span class="w"> </span><span class="s2">"/usr/sbin/nologin"</span><span class="w"> </span><span class="o">-</span><span class="n">w</span><span class="w"> </span><span class="n">no</span>
</code></pre></div>
<p>I had to do this because I was getting errors while trying to start opendkim. The errors
complained that the <code>opendkim</code> user was a member of the mail group, and that there were
several members of that group. I'm not sure why that mattered, but I could only get it to work
this way, and I'm good with that.</p>
<p>My milter config file is at <code>/usr/local/etc/mail/opendkim.conf</code>, and the contents are what
the other howto shows:</p>
<div class="highlight"><pre><span></span><code><span class="n">LogWhy</span><span class="w"> </span><span class="n">yes</span>
<span class="n">Syslog</span><span class="w"> </span><span class="n">yes</span>
<span class="n">SyslogSuccess</span><span class="w"> </span><span class="n">yes</span>
<span class="n">Canonicalization</span><span class="w"> </span><span class="n">relaxed</span><span class="o">/</span><span class="n">simple</span>
<span class="n">Domain</span><span class="w"> </span><span class="n">unixdude</span><span class="o">.</span><span class="n">net</span>
<span class="n">Selector</span><span class="w"> </span><span class="n">iss</span>
<span class="n">KeyFile</span><span class="w"> </span><span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">opendkim</span><span class="o">/</span><span class="n">iss</span><span class="o">.</span><span class="n">private</span>
<span class="n">Socket</span><span class="w"> </span><span class="n">inet</span><span class="p">:</span><span class="mi">8891</span><span class="err">@</span><span class="n">localhost</span>
<span class="n">ReportAddress</span><span class="w"> </span><span class="n">root</span>
<span class="n">SendReports</span><span class="w"> </span><span class="n">yes</span>
</code></pre></div>
<p>I added the following two lines to <code>/etc/rc.conf</code>:</p>
<div class="highlight"><pre><span></span><code>milteropendkim_enable="YES"
milteropendkim_uid="opendkim"
</code></pre></div>
<p>I added the following three lines to <code>/usr/local/etc/postfix/main.cf</code>:</p>
<div class="highlight"><pre><span></span><code>smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
</code></pre></div>
<p>I generated the key as shown -- <code>opendkim-genkey -D /var/db/opendkim -d unixdude.net -s iss</code>. The
selector (<code>iss</code> in this case) can be any string you want. I went with <code>iss</code> because that is
the system name on which this is running. This places the files in <code>/var/db/opendkim</code>, named
<code>iss.private</code> and <code>iss.txt</code>.</p>
<p>You create two records in DNS; for my server, I created <code>iss._domainkey.unixdude.net</code> as a <code>TXT</code>
record with the contents of <code>iss.txt</code> above:</p>
<div class="highlight"><pre><span></span><code><span class="o">[</span><span class="n">root@iss(F12):/usr/local/etc/mail</span><span class="o">]</span><span class="k">host</span><span class="w"> </span><span class="o">-</span><span class="n">t</span><span class="w"> </span><span class="n">txt</span><span class="w"> </span><span class="n">iss</span><span class="p">.</span><span class="n">_domainkey</span><span class="p">.</span><span class="n">unixdude</span><span class="p">.</span><span class="n">net</span>
<span class="n">iss</span><span class="p">.</span><span class="n">_domainkey</span><span class="p">.</span><span class="n">unixdude</span><span class="p">.</span><span class="n">net</span><span class="w"> </span><span class="n">descriptive</span><span class="w"> </span><span class="nc">text</span><span class="w"> </span><span class="ss">"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNKaHQWGIuv7uCByXo9/gQon4BAYKXddNS3oS21tXszTz+Z+BN+ROqZSMKyScdqYdn+dP8TBTnWENKV1BCcJLDRLXi8Nmkiafm4MAswxBtPVRwanJVgHAgPqRuy8KARI/I7LmOt4ZxGkngLgfpqC0BXUBDTIOQJNw+GZJctfKuXQIDAQAB"</span>
<span class="o">[</span><span class="n">root@iss(F12):/usr/local/etc/mail</span><span class="o">]</span>
</code></pre></div>
<p>I also created <code>_adsp._domainkey.unixdude.net</code> as
a <code>TXT</code> record, with the following contents:</p>
<div class="highlight"><pre><span></span><code>dkim=unknown
</code></pre></div>
<p>When I test the key, I receive a message saying that the key is not secure, and my google
searches indicate that this means the server is not using DNSSEC. I'm okay with this.</p>
<div class="highlight"><pre><span></span><code><span class="p">[</span><span class="n">root</span><span class="err">@</span><span class="n">iss</span><span class="p">(</span><span class="n">F12</span><span class="p">):</span><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">mail</span><span class="p">]</span><span class="n">opendkim</span><span class="o">-</span><span class="n">testkey</span><span class="w"> </span><span class="o">-</span><span class="n">vvv</span><span class="w"> </span><span class="o">-</span><span class="n">d</span><span class="w"> </span><span class="n">unixdude</span><span class="o">.</span><span class="n">net</span><span class="w"> </span><span class="o">-</span><span class="n">s</span><span class="w"> </span><span class="n">iss</span><span class="w"> </span><span class="o">-</span><span class="n">k</span><span class="w"> </span><span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">opendkim</span><span class="o">/</span><span class="n">iss</span><span class="o">.</span><span class="n">private</span>
<span class="n">opendkim</span><span class="o">-</span><span class="n">testkey</span><span class="p">:</span><span class="w"> </span><span class="n">key</span><span class="w"> </span><span class="n">loaded</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">opendkim</span><span class="o">/</span><span class="n">iss</span><span class="o">.</span><span class="n">private</span>
<span class="n">opendkim</span><span class="o">-</span><span class="n">testkey</span><span class="p">:</span><span class="w"> </span><span class="n">checking</span><span class="w"> </span><span class="n">key</span><span class="w"> </span><span class="s1">'iss._domainkey.unixdude.net'</span>
<span class="n">opendkim</span><span class="o">-</span><span class="n">testkey</span><span class="p">:</span><span class="w"> </span><span class="n">key</span><span class="w"> </span><span class="ow">not</span><span class="w"> </span><span class="n">secure</span>
<span class="n">opendkim</span><span class="o">-</span><span class="n">testkey</span><span class="p">:</span><span class="w"> </span><span class="n">key</span><span class="w"> </span><span class="n">OK</span>
<span class="p">[</span><span class="n">root</span><span class="err">@</span><span class="n">iss</span><span class="p">(</span><span class="n">F12</span><span class="p">):</span><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">mail</span><span class="p">]</span>
</code></pre></div>
<p>After I checked the key, I started/reloaded the services:</p>
<div class="highlight"><pre><span></span><code><span class="n">service</span><span class="w"> </span><span class="n">milter</span><span class="o">-</span><span class="n">opendkim</span><span class="w"> </span><span class="n">start</span>
<span class="n">service</span><span class="w"> </span><span class="n">postfix</span><span class="w"> </span><span class="n">reload</span>
</code></pre></div>
<p>After that, I was off to the races, and I can now email my wife's mac.con address again.</p>FT-7800R install, continued2022-10-03T00:00:00-04:002022-10-03T00:00:00-04:00Danieltag:www.unixdude.net,2022-10-03:/posts/2022/Oct/03/ft-7800r-install-continued/<p>Continuing my earlier post about my Yaesu FT-7800R install, I completed the radio portion
this weekend, so now I can get on the air.</p>
<p>I had a <a href="https://www.diamondantenna.net/nr770hnmo.html">Diamond NR770HNMO</a> antenna, and
bought a <a href="https://www.gigaparts.com/vehicle-specific-bracket-antenna-mount-ford.html">fender bracket mount for my F150</a>.
All I needed was an NMO mount and to fish the …<a class="label label-primary read-more" href="/posts/2022/Oct/03/ft-7800r-install-continued/"><span>Continue reading</span></a></p><p>Continuing my earlier post about my Yaesu FT-7800R install, I completed the radio portion
this weekend, so now I can get on the air.</p>
<p>I had a <a href="https://www.diamondantenna.net/nr770hnmo.html">Diamond NR770HNMO</a> antenna, and
bought a <a href="https://www.gigaparts.com/vehicle-specific-bracket-antenna-mount-ford.html">fender bracket mount for my F150</a>.
All I needed was an NMO mount and to fish the coax line to the radio.</p>
<p>A local ham friend had an NMO mount and a connector, so on Saturday I went to his house
to complete the install.</p>
<p>After some doing, we got the coax through the firewall, then we ran it all the way to the
back of the cab, where the radio was mounted. We crimped on a connector, then discovered
that the cable was too short. By an inch.</p>
<p>We noticed that we ran the cable in and out of a bracket in the front footwell, so I removed
one of the bracket's mounting screws and pushed the cable around the bracket. That gave us
just enough cable length to reach the radio without relocating the radio. Crisis averted.</p>
<p>Then we turned it on, and we discovered that its sensitivity wasn't up to par.</p>
<p>In the end, my friend gave me a loaner Kenwood 2m radio so I would at least be able to get on the air.</p>
<p>When I got home, I did some experimenting with another local ham, and I found that yes, the Kenwood
is definitely a little bit more sensitive, but the Yaesu seems to be working fine at my house.
I'll use the FT-7800R for a couple weeks, then decide whether to keep or replace it. If I replace it,
another radio should be very easy to install.</p>
<p>The only necessary remaining piece for this install is to build or buy a mount for the faceplate. I'm thinking
of building a wooden something that mounts to the front seat bolts and holds the faceplate in front of
the center console.</p>
<p>In addition to that, I still want to hide the controller and speaker cables under the carpet.</p>
<p>Anyway, now on to the pics of what was completed this weekend.</p>
<p>First, the mounted antenna. It's a little tight getting the coax through the crack between the fender and
the hood, but it seems to be okay.</p>
<p><img alt="antenna" src="/images/ft7800r-install/101-antenna.jpg"></p>
<p>The antenna wiring heading toward the firewall:</p>
<p><img alt="wiring" src="/images/ft7800r-install/102-wiring.jpg"></p>
<p>The wires going through the firewall:</p>
<p><img alt="firewall" src="/images/ft7800r-install/103-firewall.jpg"></p>
<p>The radio in the back of the cab:</p>
<p><img alt="firewall" src="/images/ft7800r-install/104-radio.jpg"></p>
<p>The remote face plate in the front:</p>
<p><img alt="faceplate" src="/images/ft7800r-install/105-faceplate.jpg"></p>
<p>The remote face plate and mike in the front:</p>
<p><img alt="faceplate" src="/images/ft7800r-install/106-front.jpg"></p>
<hr>
<p>Update: A friend pointed out that I missed a couple obvious photos here, and I was also asked about how
tight the fit is between the hood and the fender.</p>
<p>First, a pic showing the antenna (it is difficult to photo since it's so thin).</p>
<p><img alt="antenna" src="/images/ft7800r-install/107-antenna.jpg"></p>
<p>Now a couple pictures of the mount, including a closeup:</p>
<p><img alt="closeup" src="/images/ft7800r-install/108-closeup.jpg">
<img alt="mount" src="/images/ft7800r-install/109-mount.jpg"></p>
<p>Finally a profile picture of the truck:</p>
<p><img alt="profile" src="/images/ft7800r-install/110-profile.jpg"></p>FT-7800R install2022-09-25T00:00:00-04:002022-09-25T00:00:00-04:00Danieltag:www.unixdude.net,2022-09-25:/posts/2022/Sep/25/ft-7800r-install/<p>This is not my typical type of post, but as I have said, communications and networking are
things that excite me greatly. It's why I collect antique telephones, why I enjoy scanner
and shortwave listening, why I learned the OSI model and have spent much of my career in
and …<a class="label label-primary read-more" href="/posts/2022/Sep/25/ft-7800r-install/"><span>Continue reading</span></a></p><p>This is not my typical type of post, but as I have said, communications and networking are
things that excite me greatly. It's why I collect antique telephones, why I enjoy scanner
and shortwave listening, why I learned the OSI model and have spent much of my career in
and around computer networking.</p>
<p>It's also why, over the summer, I studied for, took, and passed the Technician amateur license
exam, after which I bought a Baofeng UV-5R. It didn't take me long to realize that the
UV-5R does not receive anything at my house, so I quickly replaced it with a Yaesu VX-6R, which
I am finding to be an excellent HT.</p>
<p>I also bought a Yaesu FT-7800R to install in my F150. I finally got around to that install
today, and I know that there are lots of questions about how to install a ham radio into a
vehicle, so I am documenting my install in this post.</p>
<p>First, there is a grommet with a nipple on it on both the driver side and the passenger side
of the F150. I started my install by cutting the nipple slightly and running a fish tape
through the firewall through that grommet.</p>
<p><img alt="fish tape in engine compartment" src="/images/ft7800r-install/1-fish-tape-engine.jpg">
<img alt="fish tape in engine compartment" src="/images/ft7800r-install/2-fish-tape-engine.jpg"></p>
<p>As expected, the fish tape came out in the footwell:</p>
<p><img alt="fish tape in footwell" src="/images/ft7800r-install/3-fish-tape-footwell.jpg"></p>
<p>I ran the power cable through the grommet to the back of the truck, where I am installing
the FT-7800R. Conveniently, the F150 has cable raceways under the door trim:</p>
<p><img alt="power cable behind trim" src="/images/ft7800r-install/4-power-cable-behind-trim.jpg">
<img alt="power cable in raceway" src="/images/ft7800r-install/5-power-cable-front-raceway.jpg"></p>
<p>In this picture of the raceway you can also see where the faceplate and speaker cables leave the trim and go under the seat:</p>
<p><img alt="power cable in raceway" src="/images/ft7800r-install/6-power-cable-front-raceway.jpg">
<img alt="power cable in raceway" src="/images/ft7800r-install/8-power-cable-rear-raceway.jpg"></p>
<p>At the B-pillar, I came out of the cable raceway but stayed below the trim. I tried
but was unable to push all the way through. (I did not use fish tape for this part.)
It's possible that the cable raceway only exists in the door wells, or that the bundle
of cables bunches up at the pillar, with some of the cables go up the pillar.</p>
<p><img alt="power cable in raceway" src="/images/ft7800r-install/7-power-cable-B-pillar.jpg"></p>
<p>At the C-pillar, I pushed the cables under the trim and around the seat belt bolt:</p>
<p><img alt="power cable in raceway" src="/images/ft7800r-install/9-c-pillar-cables.jpg"></p>
<p>I used L-brackets to mount a piece of wood to the passenger-side rear seat belt bolts.
It works very well, and is solid, even without any nuts holding the board in place.
The seat does push slightly against the radio when the seatback is moved up or down, but
during normal use there is plenty of room behind the rear seat for the radio.</p>
<p><img alt="power cable in raceway" src="/images/ft7800r-install/11-radio-mounted.jpg"></p>
<p>In addition to power, I ran the speaker and faceplate cables through the cable raceway.
Those cables come out right at the B pillar and go under the front seat. I would
like to hide those cables under the carpet, so they are only visible under the seat.</p>
<p><img alt="power cable in raceway" src="/images/ft7800r-install/12-b-pillar-cables.jpg"></p>
<p>The engine compartment has lots of places to reach ground. I chose this center-mounted
bolt:</p>
<p><img alt="power cable in raceway" src="/images/ft7800r-install/13-negative+firewall.jpg"></p>
<p>I added an ATO fuse holder to the positive cable right at the battery, and I zip tied
the positive power cable to other things along the way to the grommet:</p>
<p><img alt="power cable in raceway" src="/images/ft7800r-install/14-positive.jpg"></p>
<p>I do not currently have an antenna or a mount for the face plate, but I will be fixing
both of those issues this week. For my first test, I used a ground plane antenna I
made about a month ago.</p>
<p><img alt="power cable in raceway" src="/images/ft7800r-install/15-first_test.jpg">
<img alt="power cable in raceway" src="/images/ft7800r-install/16-ground_plane_antenna.jpg"></p>
<p>I received an excellent signal report on a repeater that I cannot hit with my HT, so
I count this as a win.</p>
<p>There are two more things to do. First, I need to install a permanent antenna; the plan
is to use an NMO mount on the roof. Second, I need a mount for the faceplate and
microphone; the plan for that is to get the FT-7800R version of the <a href="https://www.lidoradio.com/products/lm-300-1001-seat-bolt-mount-with-microphone-hanger-for-yaesu-ft-857-ft-7800-ft-7900-ft-8800-ft-8900">Lido LM-300-1001</a>.</p>
<p>I should call out that I forgot to install a ferrite core in the power feed, so I may
suffer from engine noise. If I do, I will splice the line and install a ferrite core.</p>Using the program map type in autofs2022-09-15T09:50:00-04:002022-09-15T09:50:00-04:00Danieltag:www.unixdude.net,2022-09-15:/posts/2022/Sep/15/using-the-program-map-type-in-autofs/<p>I recently learned about the <code>program</code> map type in the Linux automounter.</p>
<p>You can read about it <a href="https://man.cx/auto.master#heading3">here</a>, but there is little information about it,
and I was unable to find any web pages about it, so I am writing this post since it might help others.</p>
<p>The NFS automounter …<a class="label label-primary read-more" href="/posts/2022/Sep/15/using-the-program-map-type-in-autofs/"><span>Continue reading</span></a></p><p>I recently learned about the <code>program</code> map type in the Linux automounter.</p>
<p>You can read about it <a href="https://man.cx/auto.master#heading3">here</a>, but there is little information about it,
and I was unable to find any web pages about it, so I am writing this post since it might help others.</p>
<p>The NFS automounter on Linux can take a static map, which we all know, including wildcard. The static map
is in the format of <code>key options export_location</code>.</p>
<p>Well, the automounter also supports a <code>program</code> map type. The <code>program</code> map type specifics a program to run,
and the program returns the export location for any key in that mount location:</p>
<div class="highlight"><pre><span></span><code>/mnt/nfs program:/usr/local/sbin/mapper.py
</code></pre></div>
<p>It's really quite simple: The program takes the <code>key</code> as argument 1, and returns the <code>export_location</code> that
would be found in a static map. The program does not return anything else -- it returns only the location of
the NFS export. If the program returns anything else, autofs reports an error in the logs. The documentation
does say that it can return "everything but the key," but this was not my experience.</p>
<p>In my example, if the user executes <code>cd /mnt/nfs/blah</code>, the automounter executes <code>/usr/local/sbin/mapper.py blah</code>,
and the script might return something like <code>filer.example.com:/volume1/maps/abc/123</code>, which would then be mounted
at <code>/mnt/nfs/blah</code> as expected.</p>
<p>This can be useful if you want a fully dynamic automount map that is maintained by a group other than the sysadmins
of a server. In my case, a development organization maintains a list of exports in a file that the script retrieves
with HTTP. The script then parses that list of exports in the program, and any given key returns the correct location for the
automounter to mount. The developers can now update their own autofs maps without involving the admins.</p>Better spam blocking2022-09-15T09:30:00-04:002022-09-15T09:30:00-04:00Danieltag:www.unixdude.net,2022-09-15:/posts/2022/Sep/15/better-spam-blocking/<p>As mentioned previously, I continue to be inundated with spam email. Fortunately,
every spam has an observable email format: the email comes in the form of user@word1.word2.TLD.</p>
<p>This allows me to update my <code>blocked_senders</code> file to defer every email in that format with a 450, using
this …<a class="label label-primary read-more" href="/posts/2022/Sep/15/better-spam-blocking/"><span>Continue reading</span></a></p><p>As mentioned previously, I continue to be inundated with spam email. Fortunately,
every spam has an observable email format: the email comes in the form of user@word1.word2.TLD.</p>
<p>This allows me to update my <code>blocked_senders</code> file to defer every email in that format with a 450, using
this pattern:</p>
<div class="highlight"><pre><span></span><code><span class="o">/</span><span class="err">@</span><span class="p">.</span><span class="o">*</span><span class="err">\</span><span class="p">..</span><span class="o">*</span><span class="err">\</span><span class="p">..</span><span class="o">*</span><span class="err">$</span><span class="o">/</span><span class="w"> </span><span class="mi">450</span><span class="w"> </span><span class="nx">No</span><span class="w"> </span><span class="nx">such</span><span class="w"> </span><span class="nx">address</span>
</code></pre></div>
<p>When I first started doing this, I was manually watching inbound email logs to see what was deferred. I
would then update the <code>blocked_senders</code> file to add the domain as a 521 (for obvious spam), or with an
"OK" for obviously valid email or unknown spam. Then I rebuild the <code>blocked_senders</code> map and reload postfix.</p>
<p>After a while, I noticed that the domain (word2.TLD) is always a parked domain. I wrote a script to
check each domain found in the deferred email logs to detect whether the domain is parked. With this
information, the script makes the decision and updates postfix for me: Parked domains are added to
<code>blocked_senders</code> with a 521, and other domains are added with an "OK" to allow my server to receive
the mail.</p>
<p>This is basically a simple knock-to-enter form of blocking, and so far it is working very well.</p>
<p>So now I get emails like this, as my scripts do their work:</p>
<div class="highlight"><pre><span></span><code>Adding spam domain: [mydealmenia.com]
Adding valid domain: [mail.goodreads.com]
Adding valid domain: [readaloudrevival.com]
</code></pre></div>
<p>All totally automatic now.</p>
<p>The code I used can be found <a href="https://github.com/ataridude/block_spam">here</a>.</p>Update2022-08-05T00:00:00-04:002022-08-05T00:00:00-04:00Danieltag:www.unixdude.net,2022-08-05:/posts/2022/Aug/05/update/<p>I've been busy at home, but not working in my home lab. Lately I have been preoccupied by a few things.</p>
<p>First, I have been busy with prepping -- I can see how things are going, and I predict we will need
stores of supplies, so I have been busy building …<a class="label label-primary read-more" href="/posts/2022/Aug/05/update/"><span>Continue reading</span></a></p><p>I've been busy at home, but not working in my home lab. Lately I have been preoccupied by a few things.</p>
<p>First, I have been busy with prepping -- I can see how things are going, and I predict we will need
stores of supplies, so I have been busy building those stores of supplies. Specifically, I have been
going through <a href="https://www.cityprepping.com">The Prepper's Roadmap</a> with my wife, and we have been
learning a lot.</p>
<p>Second, I studied for and passed the technician class amateur radio license exam. I bought a <a href="https://rigreference.com/rigs/6009-baofeng-uv-5r">Baofeng
UV-5R</a> and find that I am too far from the local repeaters to use it, so I'll sell this and find a better HT. I also picked
up a <a href="https://rigreference.com/rigs/4345-yaesu-ft-7800re">Yaesu FT-7800R</a> for my truck, and hope to get that installed soon.</p>
<p>Third, my family and I traveled to Orlando for a week at Disney World -- we went to the Magic Kingdom
and Hollywood Studios, but unfortunately we missed EPCOT since I got sick and have been sick for the
last week. We hope to head down to EPCOT sometime in the next few months to make up for the day we
missed. Speaking of Disney, Galaxy's Edge is quite possibly the best themed area I have ever visited,
and Rise of the Resistance is without question the best theme experience I have ever had in my life.</p>
<p>Back to the update... We have more prepping to do -- skills to learn, materials to gather, and bags
and vehicles to pack -- but after that I hope to get back to my home lab.</p>Dealing with spam2022-08-01T00:00:00-04:002022-08-01T00:00:00-04:00Danieltag:www.unixdude.net,2022-08-01:/posts/2022/Aug/01/dealing-with-spam/<p>Somehow I have been added to several mailing lists, all run by the same organization. The emails look legit, and
are ones that I might have subscribed to, but I cannot get off of them no matter what I do -- it seems that they
ignore all requests to be removed …<a class="label label-primary read-more" href="/posts/2022/Aug/01/dealing-with-spam/"><span>Continue reading</span></a></p><p>Somehow I have been added to several mailing lists, all run by the same organization. The emails look legit, and
are ones that I might have subscribed to, but I cannot get off of them no matter what I do -- it seems that they
ignore all requests to be removed from the list. Postfix to the rescue: Postfix has a <code>smtpd_sender_restrictions</code>
setting, which allows the user to deny specific senders -- but the caveat is that it uses the envelope sender,
not the "from" header in the message.</p>
<p>I took note of the envelope senders, and have added those to a <code>blocked_senders</code> file:</p>
<div class="highlight"><pre><span></span><code><span class="o">/</span><span class="mi">6045</span><span class="o">-</span><span class="p">.</span><span class="o">*-</span><span class="mi">2315</span><span class="err">@</span><span class="p">.</span><span class="o">*</span><span class="nx">redacted</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="w"> </span><span class="mi">521</span><span class="w"> </span><span class="nx">No</span><span class="w"> </span><span class="nx">such</span><span class="w"> </span><span class="nx">address</span>
</code></pre></div>
<p>My <code>smtpd_sender_restrictions</code> configuration includes the line <code>check_sender_access pcre:/usr/local/etc/postfix/blocked_senders,</code>
which includes the single line above. Earlier versions of my blocked_senders file had multiple
rows for this one sender, but I finally realized that the first and last numbers (6045 & 2315) are
the account number, and that the middle number was an ever-increasing campaign ID.</p>
<p>Now, when mail comes in from those addresses, it is rejected, and I don't ever see it. And, for some
reason, this particular sender keeps sending me a half-dozen emails a day, despite getting 521 errors
for every email.</p>
<hr>
<p>More than this, though, I have noticed that spammers have gotten much more sophisticated: they register
real domains and run their spam servers on static IP addresses with real A & PTR DNS records, so they
look legitimate and they pass most of Postfix's spam tests. I mark every such email as spam, and I add
the envelope sending domain to the same blocked senders file mentioned above. Every day this
blocked_sender config catches multiple attempts from these same spammers. Until they register more real
hostnames and domain names, I might be good on spam blocking again!</p>Learning Python2022-05-20T00:00:00-04:002022-05-20T00:00:00-04:00Danieltag:www.unixdude.net,2022-05-20:/posts/2022/May/20/learning-python/<p>I have been programming in Perl since the early 1990s. My early scripts were what you would expect them to be, but my skill
of course increased over time. I have never found a thing I wanted to do that I could not do in Perl and/or shell scripting …<a class="label label-primary read-more" href="/posts/2022/May/20/learning-python/"><span>Continue reading</span></a></p><p>I have been programming in Perl since the early 1990s. My early scripts were what you would expect them to be, but my skill
of course increased over time. I have never found a thing I wanted to do that I could not do in Perl and/or shell scripting
(Bourne when portability is needed, Bash when it is not).</p>
<p>For my job, I need to learn Python, and a friend recommends the book <a href="https://automatetheboringstuff.com">Automate the Boring Stuff with Python</a>.
In addition to the book, the author also has a Udemy course, and I am working my way through that right now. One of the examples given
in the course is a Tic-Tac-Toe game.</p>
<p>Earlier this week, as I was going through the course, I had <a href="https://www.imdb.com/title/tt0086567/">WarGames</a> playing silently on the TV.
Right when the movie got to "put X in the center square," my course also got to "put X in the center square." See attached pic ... not
faked, and not timed. It just worked out this way.</p>
<p>Anyway, I decided to <a href="https://github.com/ataridude/learning-python">write a Tic-Tac-Toe game in Python</a>, and seeing as I have 1- never written
a game in my life, and 2- never written a line of Python, I think this is a pretty good first attempt. The only thing I took from the
course example was the idea for the hash data type; 100% of the rest of this was my own.</p>
<p><img alt="WarGames" src="/images/WarGames.jpg"></p>NAS shakeup2022-05-12T00:00:00-04:002022-05-12T00:00:00-04:00Danieltag:www.unixdude.net,2022-05-12:/posts/2022/May/12/nas-shakeup/<p>For my storage needs, my first NAS was a Synology DS415+. This was my primary NAS
until I purchased a DS1618+, at which time it became my backup unit. When I filled
up the DS1618+, I had a few options: buy a small NAS and use some 4TB disks I …<a class="label label-primary read-more" href="/posts/2022/May/12/nas-shakeup/"><span>Continue reading</span></a></p><p>For my storage needs, my first NAS was a Synology DS415+. This was my primary NAS
until I purchased a DS1618+, at which time it became my backup unit. When I filled
up the DS1618+, I had a few options: buy a small NAS and use some 4TB disks I had on hand,
or buy bigger disks, or buy a NAS with more storage bays. I chose to buy a DS220+, into
which I shoved two 4TB drives, and this became a second primary onsite unit.</p>
<p>A couple weeks ago, the DS415+ died, leaving me with no local backup. (I have a DS118
offsite at a friend's house, which is used as one of my two offsite backup targets.)
I purchased a DS1821+ as a new primary system, and the DS1618+ became my local backup
unit. All of my NASes are set up for SHR (primary is SHR2), and all of my NAS transitions
have been as seamless as one would expect from Synology: the process was as simple as:
move the drives, (re)install the OS, and go.</p>
<p>I will be selling the DS220+ soon, because I have added yet another spare 4TB drive to
the DS1821+, and am in the process of relocating data from the 220 to the 1821. This
also frees up the two 4TB drives that were in the DS220+, and those will go into the
other two NAS units.</p>
<p>I never like losing a NAS, but it's nice that this worked as well as one could hope.
As a bonus, I got an upgrade to the latest offering from Synology, which also allows
me to free up another drive bay: I can switch from SSD cache to NVMe cache -- when I
buy an NVMe drive or two.</p>ECMP in OSPF2022-02-20T00:00:00-05:002022-02-20T00:00:00-05:00Danieltag:www.unixdude.net,2022-02-20:/posts/2022/Feb/20/ecmp-in-ospf/<p>I have recently switched most of my offsite network connections to
WireGuard, away from ZeroTier. This is great for all of my systems
except for one: the NAS (a Synology DS118) I have at a friend's house
for offsite backup. That NAS can run ZeroTier and WireGuard, but
I don't …<a class="label label-primary read-more" href="/posts/2022/Feb/20/ecmp-in-ospf/"><span>Continue reading</span></a></p><p>I have recently switched most of my offsite network connections to
WireGuard, away from ZeroTier. This is great for all of my systems
except for one: the NAS (a Synology DS118) I have at a friend's house
for offsite backup. That NAS can run ZeroTier and WireGuard, but
I don't know of any dynamic routing protocol options for it, so I can
have only a single WireGuard connection rather than the two I want.
My solution here is to create a ZeroTier network between that NAS and
the two VMs I have at DigitalOcean. For my purposes, that is close
enough to having two WireGuard links and the ability to route to
both of them.</p>
<p>Another issue I wanted to resolve was the CPU load on my USG: with
WireGuard running on the USG, high traffic utilization on the WireGurad
links results in
100% CPU load on the USG, and that slows everything down. The solution there was to
add WireGuard links from my Pi-Hole systems at home to my DigitalOcean
VMs, and to increase the OSPF link cost from the USG to the DigitalOcean
VMs. As long as my USG is up, I'll have connectivity to my DOVMs,
but if one of my Pi-Hole systems is up, I will have better connectivity
to the DOVMs.</p>
<p>I also wanted to set all of this up with redundant links and ECMP such
that any link can be down, and have everything continue to work.</p>
<p>Below is a current network diagram. The offsite links are WireGuard with
the exception of the ZeroTier network (shown in green) that connects the two DOVMs and
the NAS. (The two DOVMs are of course also connected by a WireGuard link.)
A single OSPF area is used throughout, for all of the routing.</p>
<p>This has been an enjoyable project, and a great learning experience.</p>
<p><img alt="network" src="/images/complete_network_2022-02.jpg"></p>OSPF update, Cable cleanup, and finished rack2022-02-11T00:00:00-05:002022-02-11T00:00:00-05:00Danieltag:www.unixdude.net,2022-02-11:/posts/2022/Feb/11/cable-cleanup/<p>A quick update on my OSPF cutover. With one exception, everything has worked great.
The one exception is that OSPF would not work over the WireGuard link between my USG
and my primary DigitalOcean VM that runs FreeBSD 12 and FRR7. I thought that was odd,
since OSPF worked fine …<a class="label label-primary read-more" href="/posts/2022/Feb/11/cable-cleanup/"><span>Continue reading</span></a></p><p>A quick update on my OSPF cutover. With one exception, everything has worked great.
The one exception is that OSPF would not work over the WireGuard link between my USG
and my primary DigitalOcean VM that runs FreeBSD 12 and FRR7. I thought that was odd,
since OSPF worked fine over WireGuard between the USG and another VM at DigitalOcean,
running Ubuntu 20.04 LTS and FRR8.</p>
<p>After many web searches, I found <a href="https://docs.netgate.com/pfsense/en/latest/vpn/wireguard/routing.html">this page</a>
that mentions the two requirements to getting OSPF working over WireGuard:</p>
<ul>
<li>The WireGuard interface must be set to Non-Broadcast network type</li>
<li>OSPF neighbors must be statically configured</li>
</ul>
<p>Once I did that on both the USG and the FreeBSD/FRR7 system, my two routers neighbored
correctly, and my FreeBSD/FRR7 VM obtained the entire LSDB.</p>
<hr>
<p>Two weeks ago I finished racking everything in my new rack, and last weekend I
cleaned up the cables behind and below the rack.</p>
<p>As you can see from these "before" pics, the cables were a mess. There was
way too much slack, and just a jumble of cables below the rack.</p>
<p><img alt="before-1" src="/images/lab/before-1.jpg">
<img alt="before-2" src="/images/lab/before-2.jpg">
<img alt="before-3" src="/images/lab/before-3.jpg"></p>
<p>This jumble has been there for at least a year, and I finally bundled the cables and
moved the slack to the other side of the wall. I think the result is pretty awesome.</p>
<p><img alt="after-under-1" src="/images/lab/after-under-1.jpg">
<img alt="after-under-2" src="/images/lab/after-under-2.jpg"></p>
<hr>
<p>The finished lab looks pretty good. Many thanks to <a href="https://haydenjames.io/home-lab-beginners-guide-hardware/">Hayden James</a>
for the inspiration here. You can see my progress <a href="/tag/rack/">here</a>.</p>
<p><img alt="rack" src="/images/lab/rack.jpg"></p>
<p>For those who are interested, here's what's in and around that rack:</p>
<ul>
<li>U12: TRENDnet 24-port Keystone patch panel, with VCE inline couplers and VCE blanks</li>
<li>U11: Ubiquiti USW-Lite-16-PoE and Ubiquiti USG</li>
<li>U10: Cisco 2611 (Lab R1)</li>
<li>U9: Cisco 2610 (Lab R2)</li>
<li>U8: Cisco 2610 (Lab R3)</li>
<li>U7: Cisco 2960 (Lab S1)</li>
<li>U6: Cisco 2960 (Lab S2)</li>
<li>U5: Cisco 3750 (Lab S3)</li>
<li>U3-U4: Shelf holding Raspberry Pi 4B (8GB) and Lenovo M900 Tiny</li>
<li>U2: Empty</li>
<li>U1: Pyle PDU</li>
</ul>
<p>I left U2 empty so that I can reach the latches that allow my Networx rack to swing out.
Similarly, there is no shelf at U11; the UI devices are simply resting atop the Cisco in U10.</p>
<p>Below the rack are my Synology DS220+, a Grandstream DP750, a Linksys PAP2, and a UPS for the
infrastructure (not the lab devices).</p>
<p>The monitor above the rack is connected to my Raspberry Pi 4B. I would like to use it as
some sort of dashboard, but I haven't done anything with that yet. That's a project for
another day.</p>Lab update2022-01-31T00:00:00-05:002022-01-31T00:00:00-05:00Danieltag:www.unixdude.net,2022-01-31:/posts/2022/Jan/31/lab-update/<p>I have been hard at work these last few weeks in my home lab, and have done quite a bit. Here are a
few of the things I have done:</p>
<ul>
<li>Installed <a href="https://github.com/nirui/sshwifty">sshwifty</a>, a really cool, useful
package that a friend pointed me to.</li>
<li>Built a <a href="https://www.freeccnaworkbook.com/blog/ccna/how-to-make-a-t1-crossover">T1 crossover cable</a> to connect …</li><a class="label label-primary read-more" href="/posts/2022/Jan/31/lab-update/"><span>Continue reading</span></a></ul><p>I have been hard at work these last few weeks in my home lab, and have done quite a bit. Here are a
few of the things I have done:</p>
<ul>
<li>Installed <a href="https://github.com/nirui/sshwifty">sshwifty</a>, a really cool, useful
package that a friend pointed me to.</li>
<li>Built a <a href="https://www.freeccnaworkbook.com/blog/ccna/how-to-make-a-t1-crossover">T1 crossover cable</a> to connect the two routers I have in my home lab that have a T1 CSU/DSU WIC installed.</li>
<li>Installed <a href="https://www.portainer.io">Portainer</a> on my production Docker Swarm</li>
<li>Renumbered my home network from 192.168.1.0/24 to 192.168.8.0/24. I undertook this effort because
I have a NAS hosted at a friend's house, and his network also uses the 192.168.1.0/24 address space, so
that made it difficult for me to get to my NAS -- sure, I could get to it, but every system that needed
access to it needed to be on my ZeroTier storage network. Now, I can route directly to it, and return
packets are routed to my network instead of to his.</li>
<li>Deployed WireGuard extensively, using FRR, OSPF, and Anycast to access systems. Because of
CPU utilization issues I discovered after doing this, I might move my WireGuard routing/endpoint to my raspberry pi or to a VM in order to work around those issues.</li>
<li>Added more monitoring under <a href="https://kuma.unixdude.net/status">Kuma</a>.</li>
<li>Deployed a new NAS, a <a href="https://www.synology.com/en-us/products/DS220+">Synology DS220+</a> to take some load off my DS1618+ and to use it as cold storage.</li>
<li>Through the generosity of a friend, I have replaced my three Cisco 2950s with two 2960-Xs and one 3750G.</li>
<li>I have finished racking all my devices.</li>
<li>I have cabled up my Cisco lab again. I have also connected the lab to my home network via my
<a href="https://store.ui.com/products/unifi-security-gateway">Ubiquiti USG</a>'s
LAN2 port, so that I can route to it. I have console access to all of the devices, using <a href="https://www.amazon.com/Gearmo-Serial-Windows-Certified-Drivers/dp/B004ETDC8K/">a 4-port
USB-to-serial adapter</a>
and some mini USB cables (for the 2960s) connected to my Raspberry Pi, but I also want network access.
The current lab setup, which will hopefully be useful for much learning, is shown here:</li>
</ul>
<p><img alt="lab network" src="/images/lab-network-2022-01.jpg"></p>
<ul>
<li>I installed a patch panel, which cleaned up the rack a bit. Here are before & after pics. Because
my switch and router are not rack-mount devices, it's not as clean as it could be, but I still think
this came out very well.</li>
</ul>
<p><img alt="before" src="/images/patch-panel/before.jpeg"></p>
<p><img alt="after" src="/images/patch-panel/after.jpeg"></p>
<p>Next up:</p>
<ul>
<li>Configure the newly recabled Cisco lab.</li>
<li>Finish migrating data to the DS220+.</li>
<li>Clean up the power and ethernet cables behind and below the rack.</li>
</ul>Goodbye BGP, hello OSPF2022-01-12T00:00:00-05:002022-01-12T00:00:00-05:00Danieltag:www.unixdude.net,2022-01-12:/posts/2022/Jan/12/goodbye-bgp-hello-ospf/<p>Soon after I learned about <a href="https://en.wikipedia.org/wiki/Anycast">Anycast</a>, I employed it
on my home network, so that I can have
multiple instances of <a href="https://pi-hole.net">Pi-Hole</a>, mostly so that I can
take down my Raspberry Pi without hearing from the family about how the Internet is down.</p>
<p>Until last night, I was using <a href="https://www.cloudflare.com/learning/security/glossary/what-is-bgp/">BGP …</a><a class="label label-primary read-more" href="/posts/2022/Jan/12/goodbye-bgp-hello-ospf/"><span>Continue reading</span></a></p><p>Soon after I learned about <a href="https://en.wikipedia.org/wiki/Anycast">Anycast</a>, I employed it
on my home network, so that I can have
multiple instances of <a href="https://pi-hole.net">Pi-Hole</a>, mostly so that I can
take down my Raspberry Pi without hearing from the family about how the Internet is down.</p>
<p>Until last night, I was using <a href="https://www.cloudflare.com/learning/security/glossary/what-is-bgp/">BGP</a> (as part of <a href="https://frrouting.org">FRR</a>) for Anycast,
<a href="/posts/2021/Mar/11/frr-and-anycast/">running FRR on my Raspberry Pi</a> and some other devices, and peering those with each other
and with my router.</p>
<p>Well, I got tired of the full mesh that BGP requires, so last night I switched to
OSPF. It still supports Anycast, but does not require a full mesh of routers, making
it much more suitable to my needs. I mean, of course I knew BGP was not the right
long-term answer, but it was easy and all of my coworkers were able to help me with any
configuration questions -- and I got to say that I ran BGP at home, which was fun.</p>
<hr>
<p>I have been using <a href="https://www.zerotier.com">ZeroTier</a> as my VPN solution ever since
<a href="https://mindlesstux.com/2018/09/23/zerotier-multsite-lan-part-1-zerotier-and-making-a-multi-site-lan-man/">a friend mentioned it</a>.
I want to have some redundancy in case ZeroTier goes down, so last night I configured <a href="https://www.wireguard.com">WireGuard</a> on my Ubiquiti <a href="https://store.ui.com/products/unifi-security-gateway">USG</a>,
as well as one of my offsite systems. I prefer to run this on my router anyway, since
I don't want my whole network to go down if my Raspberry Pi goes down. If my network goes down because my router is down,
well, then I have bigger problems than not being able to access my remote systems.</p>
<p>So, I'll use WireGuard as the primary, and ZeroTier as the backup.</p>
<hr>
<p>One of my main goals right now is that I want to retire my Rasperry Pi 3B unit in favor of a
PoE-powered Pi 4B unit, freeing up a power oulet, reducing cables, and generally simplifying
my setup.</p>
<hr>
<p>I'm not done yet, but I'm making good progress on these goals.</p>MacBook Pro recovery2022-01-04T00:00:00-05:002022-01-04T00:00:00-05:00Danieltag:www.unixdude.net,2022-01-04:/posts/2022/Jan/04/macbook-pro-recovery/<p>My work laptop, a 2018 MacBook Pro, needs a new battery and so I will be shipping it off to
have that serviced. Prior to getting that serviced, I need a temporary system, so I
went to the office to grab one of the spares we have, another 2018 MBP …<a class="label label-primary read-more" href="/posts/2022/Jan/04/macbook-pro-recovery/"><span>Continue reading</span></a></p><p>My work laptop, a 2018 MacBook Pro, needs a new battery and so I will be shipping it off to
have that serviced. Prior to getting that serviced, I need a temporary system, so I
went to the office to grab one of the spares we have, another 2018 MBP. This system was
used by a former coworker, one who retired months ago, and he
no longer remembers the firmware password he configured on the system.</p>
<p>I thought this system was a brick, until an Apple expert pointed me to the <a href="https://support.apple.com/guide/apple-configurator-2/revive-or-restore-an-intel-based-mac-apdebea5be51/mac">DFU restore option
using Apple Configurator 2</a>.
The startup key sequence for this is ... tricky. I tried it a few times, but was not successful
until I watched <a href="https://www.youtube.com/watch?v=UaVgBP4gJsU">this YouTube video</a> that demonstrates
the process.</p>
<p>Using DFU mode, I first tried the Revive option. The laptop booted back to the firmware prompt, so
I then tried the Restore option, which caused this laptop to boot into Internet Recovery mode.
Perfect! Now I have this unit installing its original OS -- Mojave -- and will update to Big Sur
once it is done.</p>
<p>Internet Recovery is cool, but slow. It's a little faster over ethernet versus wifi, especially
with a gigabit connection, but it's still slow. Anyway, this thing is working now and is no
longer a brick, so it's good now.</p>Upgrading to DSM72022-01-03T00:00:00-05:002022-01-03T00:00:00-05:00Danieltag:www.unixdude.net,2022-01-03:/posts/2022/Jan/03/upgrading-to-dsm7/<p>I've been meaning to upgrade my Synology NASes to DSM7. I upgraded my DS415+
last week, and it went smoothly, so today I researched all the packages I have installed on my
primary unit, a DS1618+. I found that all but two would upgrade seamlessly;
the two trouble packages were …<a class="label label-primary read-more" href="/posts/2022/Jan/03/upgrading-to-dsm7/"><span>Continue reading</span></a></p><p>I've been meaning to upgrade my Synology NASes to DSM7. I upgraded my DS415+
last week, and it went smoothly, so today I researched all the packages I have installed on my
primary unit, a DS1618+. I found that all but two would upgrade seamlessly;
the two trouble packages were Plex Media Server and ZeroTier. Both of these
are supported on DSM7 -- see instructions for <a href="https://www.blackvoid.club/plex-migration-with-dsm-7/">PMS</a>
and <a href="https://docs.zerotier.com/devices/synology/">ZeroTier</a> -- but are more involved
than simply installing a package. In particular, PMS requires a very specific procedure
to migrate from DSM6.</p>
<p>I had ZeroTier up and running quickly and easily using the linked instructions, but Plex Media Server
gave me a lot of trouble,
so I figured I would post about it in case others run into the same issue.</p>
<p>I had PMS running on DSM6, and I wanted to migrate my installation to DSM7. This is not
as simple as upgrading a package, so I took care to perform the upgrade as instructed.
Following the instructions linked above, I downloaded PMS directly from Plex, then I
began a manual installation.</p>
<p>During the installation, the wizard presented an error
indicating a failed install (sorry but I did not take a screenshot).
I then started doing lots of research and tried the installation several times. I also saw the
migration log at <code>/volume1/Plex/Migration.log</code>, and it showed success, so that gave me an
idea: maybe the installation really did succeed, even though the wizard showed a failure.
I cleaned out everything (did a remove+erase of the PMS package, restored <code>/volume1/Plex</code>,
removed <code>/volume1/PlexMediaServer</code>) and reran the installation. Predictably the wizard
showed a failure, and I just left it and tailed the migration log until the log showed success:</p>
<div class="highlight"><pre><span></span><code>Plex Media Server migration to DSM 7 started: Mon Jan 3 15:37:31 EST 2022
=== Mon Jan 3 15:37:31 EST 2022 === Start: Change ownership
=== Mon Jan 3 15:37:38 EST 2022 === Completed: Change ownership
=== Mon Jan 3 15:37:38 EST 2022 === Start: Convert symbolic links
=== Mon Jan 3 15:38:55 EST 2022 === Completed: Convert symbolic links
=== Mon Jan 3 15:38:55 EST 2022 === Start: Migrate Plex Media Server
=== Mon Jan 3 15:40:42 EST 2022 === Completed: Migrate Plex Media Server
=== Mon Jan 3 15:40:42 EST 2022 === Completed: Migrate Plex Media Server
=== Mon Jan 3 15:40:42 EST 2022 === Start: Clean /volume1/Plex
=== Mon Jan 3 15:40:43 EST 2022 === Completed: Clean /volume1/Plex
</code></pre></div>
<p>I exited the wizard, and all was well.</p>
<p>There is one other change I did on the final, successful installation: when I removed
the PMS package after the first install (per instructions it requires two installs for a successful migration
from DSM6), I also removed the PlexMediaServer shared folder. Maybe that was key:
I did not remove that shared folder after the first install, until my final attempt.</p>
<hr>
<p>Unfortunately DSM7 is complaining about my 3rd party RAM upgrade (2x 8GB sticks that
were removed from my Lenovo M900 when I upgraded that to 32GB), but otherwise DSM7
is great, and the upgrade went smoothly with the exception of Plex.</p>Retiring my Dell R6102021-12-22T00:00:00-05:002021-12-22T00:00:00-05:00Danieltag:www.unixdude.net,2021-12-22:/posts/2021/Dec/22/retiring-my-dell-r610/<p>Ever since <a href="https://mindlesstux.com/">a friend</a> gave me a Dell R610, I have used that as
my primary home lab server. Due to its age, I find that I am unable to run the most
recent versions of some software packages, including the latest iterations of VMware
ESXi and vCenter. Also, my …<a class="label label-primary read-more" href="/posts/2021/Dec/22/retiring-my-dell-r610/"><span>Continue reading</span></a></p><p>Ever since <a href="https://mindlesstux.com/">a friend</a> gave me a Dell R610, I have used that as
my primary home lab server. Due to its age, I find that I am unable to run the most
recent versions of some software packages, including the latest iterations of VMware
ESXi and vCenter. Also, my unit sits in a small room near the 3rd floor AC return in my house,
and this means that the heat it generates is pulled into the AC system, which affects my home's HVAC
operation.</p>
<p>For these and other reasons, I have decided to retire the R610 and replace it. A while back,
I heard about <a href="https://www.servethehome.com/introducing-project-tinyminimicro-home-lab-revolution/">ServeTheHome's TinyMiniMicro project</a>, so I read some of their articles and decided to purchase a Lenovo M900 with an i7 CPU. I upgraded my unit to 32GB RAM (the max), and this model includes vPro technology, which
is a plus or a minus depending on who you ask. This unit will sit in my rack, rather
than on the floor leaning against the wall. It will be a huge bonus that the M900 generates
less heat and uses less power than the R610.</p>
<p>My new M900 is much less capable overall than my R610: my R610 had a dual 300GB HDs in a
mirror, 192GB RAM, and dual Xeon 3.0 GHz for a total of 16 vCPUs. The M900 has a single
256GB SSD, 32GB RAM, and a single 2.8GHz i7 CPU for a total of 8 vCPUs. Even so, I think
this was a great purchase
based on my use case, which is to run ESXi and a dozen or so VMs. Running vCSA 6, I maxed out on the R610 out at
40GB RAM usage, 10GB of which was vCSA -- so if I avoid vCSA, I'll stay below 30GB RAM usage.
Per-thread, the M900 is about 30% faster than the R610, so overall I think this is a huge win.</p>
<p>Having less RAM means I will have to be more selective about the VMs I run. Fortunately, since this type of
setup is so cheap, if I need more capacity, I'll just buy another one of these 1L systems, be
it a Lenovo or a Dell or an HP. So far, the M900 has exceeded my expectations.</p>
<p>Getting ESXi instsalled was an interesting experience: I couldn't get the M900 to boot from USB or PXE --
it seemed stuck on booting the included Windows installation. I quickly learned the reason for this: the
M900 was configured for UEFI booting only; BIOS booting was disabled. I enabled BIOS booting, downloaded the
Lenovo custom build of ESXi 7.0u2 and added it to my BIOS-only PXE setup, and started my install. I was off
to the races, and so far I haven't found any issues with this build on this hardware.</p>
<p>After I got ESXi 7 installed on it, I installed vCSA 7, then added both the R610 and M900 to that vCenter
Server so I could use vMotion to migrate all of the VMs. I had a few VMs on the local storage on the
R610 -- notably my UniFi Controller so that I can upgrade my Ubiquiti devices -- and it's so cool
that vCenter Server can migrate from local storage to local storage on different servers, without an intermediate
stop on a shared datastore. Obviously 256-300GB space is not enough for many VMs; most of my VMs are
stored on NFS.</p>
<p>Another thing that really impressed me last night is that all of my VMs migrated without issue:
No complaints about CPU mismatch. All VMs are now migrated, but I powered down a few of them
to save RAM. With vCSA powered down, this system will be perfect for my needs:</p>
<p><img alt="CPU/RAM Utilization" src="/images/m900/utilization.png"></p>
<p>I have a lot of cleanup to do here, but it's amazing how quiet and cool this area is, now that the R610
is powered down.</p>
<p><img alt="M900 under rack" src="/images/m900/under-rack.jpeg"></p>
<p>The powered-down R610:</p>
<p><img alt="R610" src="/images/m900/r610.jpeg"></p>
<p>The eBay seller did me right: this thing was super clean and looks practically new:</p>
<p><img alt="front" src="/images/m900/front.jpeg">
<img alt="top" src="/images/m900/top.jpeg">
<img alt="inside" src="/images/m900/inside.jpeg"></p>
<p>One last thing: I have decided to convert my PXE setup to UEFI-only: at this point, I have zero need for
BIOS PXE booting, and UEFI will be better. That will be an upcoming project.</p>Wall-mount rack2021-11-27T00:00:00-05:002021-11-27T00:00:00-05:00Danieltag:www.unixdude.net,2021-11-27:/posts/2021/Nov/27/wall-mount-rack/<p>Since I bought my Cisco equipment, I have had it racked in the cheap, two-post desktop rack
that came with the equipment when I bought it. It was pretty bad, but it worked:</p>
<p><img alt="original rack" src="/images/original-rack.jpg"></p>
<p>I really wanted something better than this, so I started doing some web searches to get
ideas …<a class="label label-primary read-more" href="/posts/2021/Nov/27/wall-mount-rack/"><span>Continue reading</span></a></p><p>Since I bought my Cisco equipment, I have had it racked in the cheap, two-post desktop rack
that came with the equipment when I bought it. It was pretty bad, but it worked:</p>
<p><img alt="original rack" src="/images/original-rack.jpg"></p>
<p>I really wanted something better than this, so I started doing some web searches to get
ideas on what I could do better. Along the way, I discovered <a href="https://haydenjames.io/home-lab-beginners-guide-hardware/">Hayden James's home lab beginner's guide</a>,
and decided I wanted a wall-mount rack. I did a lot of research about wall-mount racks, and
I settled on the <a href="https://www.networxproducts.com/12u-adjustable-depth-open-frame-swing-out-wall-mount-rack-301-series-flat-packed">Networks adjustable depth, swing-out 12U rack</a>.</p>
<p>The rack arrived last week, and I was able to mount it earlier this week. I still
have lots of work to do here, and this is going to be a huge improvement over the original
setup.</p>
<p><img alt="wall mount rack" src="/images/wall-mount-rack.jpg"></p>healthchecks.io2021-11-15T00:00:00-05:002021-11-15T00:00:00-05:00Danieltag:www.unixdude.net,2021-11-15:/posts/2021/Nov/15/healthchecksio/<p>Recently, <a href="http://mindlesstux.com">a friend</a> introduced me to <a href="https://healthchecks.io">healthchecks.io</a>, a service for monitoring cron jobs.<br>
A week or so ago, I configured several of my cron jobs to be monitored through this system, and it has already helped me:
yesterday I received a notification that an rsync job did not run …<a class="label label-primary read-more" href="/posts/2021/Nov/15/healthchecksio/"><span>Continue reading</span></a></p><p>Recently, <a href="http://mindlesstux.com">a friend</a> introduced me to <a href="https://healthchecks.io">healthchecks.io</a>, a service for monitoring cron jobs.<br>
A week or so ago, I configured several of my cron jobs to be monitored through this system, and it has already helped me:
yesterday I received a notification that an rsync job did not run, and the reason for that failure turned out to be
a filesystem that was unintentionally unmounted. I remounted the filesystem, and today I received a notification that
the rsync job finished as expected. What a great service.</p>Printer update2021-11-09T00:00:00-05:002021-11-09T00:00:00-05:00Danieltag:www.unixdude.net,2021-11-09:/posts/2021/Nov/09/printer-update/<p>Here's a months-overdue update on that <a href="https://www.unixdude.net/posts/2021/Sep/07/docker-build/">HP Envy printer</a>: I wasn't able to
return the printer, and I still wanted a better
scanner app than the HP's built-in one, so I decided to take the
Raspberry Pi 4 that I mentioned in an earlier post and attach the printer
directly …<a class="label label-primary read-more" href="/posts/2021/Nov/09/printer-update/"><span>Continue reading</span></a></p><p>Here's a months-overdue update on that <a href="https://www.unixdude.net/posts/2021/Sep/07/docker-build/">HP Envy printer</a>: I wasn't able to
return the printer, and I still wanted a better
scanner app than the HP's built-in one, so I decided to take the
Raspberry Pi 4 that I mentioned in an earlier post and attach the printer
directly to it. The goal of doing this was having better printing
than what I was doing before, where the printer was on my IoT VLAN,
and my print clients are all on my home VLAN.</p>
<p>In addition to having better printing functionality, I also wanted a better scanner app as I just mentioned. And, I wanted all of it working through <a href="https://traefik.io/traefik/">Traefik</a>, so that I can access the scanservjs app, printer admin page, and CUPS admin page all through Traefik.</p>
<p>As I mentioned, I got to learn about <code>docker build</code>, because there was no scanservjs image for ARM. Fortunately, building for ARM is super fast on my M1 MacBook Air. I mean, my Intel iMac could build the ARM image too, but it takes a lot less time on my M1 MBA -- a minute or two on the M1 vs 1,000 seconds on the Intel, so I won't be building the ARM image on my iMac again!</p>
<p>I have now automated the build process mentioned in the earlier post: every now and again, I will pull the latest code from sbs20's scanservjs repo, then build it using his Dockerfile, with this script:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/bash</span>
<span class="nv">IMAGE</span><span class="o">=</span><span class="s2">"ataridude/scanservjs"</span>
<span class="o">[[</span><span class="w"> </span>-z<span class="w"> </span><span class="nv">$1</span><span class="w"> </span><span class="o">]]</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Usage: </span><span class="nv">$0</span><span class="s2"> tag"</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span>-1
docker<span class="w"> </span>image<span class="w"> </span>build<span class="w"> </span>-t<span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">IMAGE</span><span class="si">}</span><span class="s2">:</span><span class="nv">$1</span><span class="s2">"</span><span class="w"> </span>-t<span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">IMAGE</span><span class="si">}</span><span class="s2">:latest"</span><span class="w"> </span>.<span class="w"> </span><span class="o">&&</span><span class="w"> </span>docker<span class="w"> </span>image<span class="w"> </span>push<span class="w"> </span><span class="si">${</span><span class="nv">IMAGE</span><span class="si">}</span>:<span class="nv">$1</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span>docker<span class="w"> </span>image<span class="w"> </span>push<span class="w"> </span><span class="si">${</span><span class="nv">IMAGE</span><span class="si">}</span>:latest
</code></pre></div>
<p>I know there are ways to fully automate that <code>docker build</code> process, and I'll get to that point at some time,
but for now it's nice to have an easy way to update the image.</p>
<p>As you might expect, my Raspberry Pi print server is installed via <a href="https://github.com/ataridude/ansible/blob/master/playbook_print_server.yml">an Ansible playbook</a>.
I should look into the playbook and <code>docker-container-scanner</code> role, since this was the first Docker-oriented role I wrote, and I'm sure there is much that could be improved in the role and the playbook.</p>Update on migration to Docker/Traefik2021-11-05T00:00:00-04:002021-11-05T00:00:00-04:00Danieltag:www.unixdude.net,2021-11-05:/posts/2021/Nov/05/update-on-migration-to-dockertraefik/<p>I have been working hard these last many weeks in order to move all services off of my main DigitalOcean Droplet/VM. In the process, I have been moving everything to Docker, with full automation via Ansible. I have finally achieved this goal, for the web services anyway, and now …<a class="label label-primary read-more" href="/posts/2021/Nov/05/update-on-migration-to-dockertraefik/"><span>Continue reading</span></a></p><p>I have been working hard these last many weeks in order to move all services off of my main DigitalOcean Droplet/VM. In the process, I have been moving everything to Docker, with full automation via Ansible. I have finally achieved this goal, for the web services anyway, and now I can deploy all of my web services with one or two Ansible commands: a bootstrap playbook (needed only for DigitalOcean VMs), and a deploy playbook (good for VMs at home as well as ones at DigitalOcean).</p>
<p>I have uploaded my code <a href="https://github.com/ataridude/ansible">to GitHub</a>. For DigitalOcean VMs, I start with <a href="https://github.com/ataridude/ansible/blob/master/playbook_bootstrap.yml">playbook_bootstrap.yml</a>, and for all VMs, I run <a href="https://github.com/ataridude/ansible/blob/master/playbook_blog_server.yml">playbook_blog_server.yml</a>.</p>
<p>This single command now sets up everything that used to be an Apache vhost on my main VM -- and all "vhosts" now run through <a href="https://traefik.io/traefik/">Traefik</a>.</p>
<p>This is a good milestone to reach, because it means I have now accomplished my goal of treating these services as "<a href="https://en.wikipedia.org/wiki/Infrastructure_as_code">infrastructure as code</a>": I no longer make changes on the web server -- all changes are done in Ansible and/or Docker, then pushed out to the server.</p>
<p>Next up, I will work on moving my mail server (postfix, dovecot, spamassassin).</p>
<hr>
<p>While working on this project, one of the things I ran into is that the Pelican search plugin I have been using, <a href="https://github.com/getpelican/pelican-plugins/tree/master/tipue_search">tipue_search</a>, is no longer supported. At first, this kept my main site on Apache, but eventually I decided I needed to get this sorted. I made <a href="https://github.com/ataridude/pelican_tipue_search">my own copy of tipue_search</a> and added it to the <a href="https://github.com/ataridude/unixdude.net/blob/master/Dockerfile">Dockerfile</a> I am using to build this site, and now I have it working in my Dockerized blog implementation. I had considered switching to the brand-new <a href="https://github.com/rehanhaider/pelican-algolia/tree/main/pelican/plugins/pelican_algolia">pelican-algolia plugin</a>, and while I still think that is a great solution I decided to stick with the tipue_search plugin I've been using for years.</p>
<hr>
<p>This Dockerfile uses a multi-stage build process, which I like because it lowers the requirements for my build server: the multi-stage build means that Pelican is not actually installed on my build server -- the entire build is done in a container. I got this idea from a friend, who <a href="https://gitlab.com/blcarman/blog/-/blob/master/Dockerfile">does this with his blog</a>.</p>
<hr>
<p>Hopefully I will now find it easier to update this and my <a href="https://www.ataridude.net">Atari blog</a>.</p>MinFS2021-10-28T00:00:00-04:002021-10-28T00:00:00-04:00Danieltag:www.unixdude.net,2021-10-28:/posts/2021/Oct/28/minfs/<p>Over the last month, I have been continuing my work at automating my systems and have made a lot of
progress in that effort.</p>
<p>Part of that will be relocating some files that I still want web-accessible, but not on this website.
Enter <a href="https://www.digitalocean.com/products/spaces/">DigitalOcean Spaces</a>, and <a href="https://github.com/minio/minfs">MinFS</a> to access it …<a class="label label-primary read-more" href="/posts/2021/Oct/28/minfs/"><span>Continue reading</span></a></p><p>Over the last month, I have been continuing my work at automating my systems and have made a lot of
progress in that effort.</p>
<p>Part of that will be relocating some files that I still want web-accessible, but not on this website.
Enter <a href="https://www.digitalocean.com/products/spaces/">DigitalOcean Spaces</a>, and <a href="https://github.com/minio/minfs">MinFS</a> to access it.</p>
<p>Set was super simple: After installation, I edited <code>/etc/minfs/config.json</code> to set my Spaces
access key and secret key, then added the /etc/fstab entry, created the directory, and mounted it.</p>
<p><code>/etc/fstab</code>:</p>
<div class="highlight"><pre><span></span><code>https://nyc3.digitaloceanspaces.com/unixdude /mnt/unixdude minfs defaults,cache=/tmp/unixdude-minfs 0 0
</code></pre></div>
<div class="highlight"><pre><span></span><code><span class="o">[</span><span class="n">root@ansible-test(U20):67:/mnt/unixdude/ataridude</span><span class="o">]</span><span class="n">df</span><span class="w"> </span><span class="o">-</span><span class="n">h</span><span class="w"> </span><span class="p">.</span>
<span class="n">Filesystem</span><span class="w"> </span><span class="k">Size</span><span class="w"> </span><span class="n">Used</span><span class="w"> </span><span class="n">Avail</span><span class="w"> </span><span class="k">Use</span><span class="o">%</span><span class="w"> </span><span class="n">Mounted</span><span class="w"> </span><span class="k">on</span>
<span class="n">MinFS</span><span class="w"> </span><span class="mi">64</span><span class="n">T</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">64</span><span class="n">T</span><span class="w"> </span><span class="mi">0</span><span class="o">%</span><span class="w"> </span><span class="o">/</span><span class="n">mnt</span><span class="o">/</span><span class="n">unixdude</span>
<span class="o">[</span><span class="n">root@ansible-test(U20):68:/mnt/unixdude/ataridude</span><span class="o">]</span><span class="n">ls</span><span class="w"> </span><span class="o">-</span><span class="n">l</span>
<span class="n">total</span><span class="w"> </span><span class="mi">0</span>
<span class="o">-</span><span class="n">rw</span><span class="o">-</span><span class="n">rw</span><span class="o">----</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">root</span><span class="w"> </span><span class="n">root</span><span class="w"> </span><span class="mi">67748394</span><span class="w"> </span><span class="n">Oct</span><span class="w"> </span><span class="mi">27</span><span class="w"> </span><span class="mi">22</span><span class="err">:</span><span class="mi">57</span><span class="w"> </span><span class="n">atari</span><span class="o">-</span><span class="mi">5200</span><span class="o">-</span><span class="n">jumpy</span><span class="o">-</span><span class="n">video</span><span class="p">.</span><span class="n">mov</span>
<span class="o">[</span><span class="n">root@ansible-test(U20):69:/mnt/unixdude/ataridude</span><span class="o">]</span>
</code></pre></div>
<p>The URL for that file is: <a href="https://unixdude.nyc3.digitaloceanspaces.com/ataridude/atari-5200-jumpy-video.mov">https://unixdude.nyc3.digitaloceanspaces.com/ataridude/atari-5200-jumpy-video.mov</a></p>
<p>Pretty slick. I'll be adding more to that space over time.</p>
<p>Also: 64T - wow!</p>acme.sh and Let's Encrypt certificates2021-10-18T00:00:00-04:002021-10-18T00:00:00-04:00Danieltag:www.unixdude.net,2021-10-18:/posts/2021/Oct/18/acme_sh-and-letsencrypt-certificates/<p>I have been needing to cut over to acme v2 for a long time, and I didn't bother doing it until way
too late -- this weekend. As a result of my tardiness, my iPhone has not been able to receive email
from my mail server for more than a month …<a class="label label-primary read-more" href="/posts/2021/Oct/18/acme_sh-and-letsencrypt-certificates/"><span>Continue reading</span></a></p><p>I have been needing to cut over to acme v2 for a long time, and I didn't bother doing it until way
too late -- this weekend. As a result of my tardiness, my iPhone has not been able to receive email
from my mail server for more than a month now.</p>
<p>My current mail server runs dovecot + postfix (plus some other stuff), manually installed on FreeBSD,
and I have been trying to get Docker Mailserver working on a new Ubuntu server and have been having
nothing but trouble with that, so I finally gave up. On the new setup I will still install everything
with Ansible, it just won't be Dockerized, and I'm okay with this.</p>
<p>Tonight I decided that in the interim I needed to at least fix the SSL certificates. While doing this,
I ran into a problem that I cannot believe is not called out anywhere, so I'm writing about it. To be
fair, this is documented <a href="https://github.com/acmesh-official/acme.sh/wiki/Server">here</a>, but no howto I found mentions it.</p>
<p><a href="https://letsencrypt.org/docs/client-options/">Let's Encrypt's client page</a> lists <a href="https://github.com/acmesh-official/acme.sh">acme.sh</a>, but does not bother to mention that one must pass in the <code>--server</code> parameter in order
to use the Let's Encrypt CA with <code>acme.sh</code>. One must do this because the default CA for acme.sh
is ZeroSSL.</p>
<p>In fact, none of the dozen or so howtos I read made any mention of this! And, since I had never heard
of ZeroSSL until tonight, I had no idea that it was a competitor to Let's Encrypt.</p>
<p>In order to create the Let's Encrypt certificate for my mail server (mail.unixdude.net), I set the
<code>DO_API_KEY</code> environment variable, then ran:</p>
<div class="highlight"><pre><span></span><code>acme.sh --issue -d mail.unixdude.net --dns dns_dgon --server letsencrypt
</code></pre></div>
<p>The magic there, for the Let's Encrypt user, is the <code>--server letsencrypt</code> parameter -- because as I mentioned the default is
ZeroSSL. If you try it without specifying the server, parts of this work and other parts of this do
not work, and the problem is not obvious, or at least it was not obvious to me.</p>
<p>In any case, I now have new certs running, and have a fully functioning system again -- which gives me
time to cut over to a new server.</p>
<p>I hope this post helps others who are running into the same issue I was hitting.</p>Blog automation2021-09-26T00:00:00-04:002021-09-26T00:00:00-04:00Danieltag:www.unixdude.net,2021-09-26:/posts/2021/Sep/26/blog-automation/<p>This is a short post after a lot of work.</p>
<p>I have been busy these last few weeks automating the installation of <a href="https://www.docker.com">Docker</a>,
<a href="https://traefik.io/traefik/">Traefik</a>, and <a href="https://www.ataridude.net">my Atari blog</a> with <a href="https://www.ansible.com/">Ansible</a>.
I have reached the point that I can take a fresh install of Ubuntu 20.04 LTS and end up …<a class="label label-primary read-more" href="/posts/2021/Sep/26/blog-automation/"><span>Continue reading</span></a></p><p>This is a short post after a lot of work.</p>
<p>I have been busy these last few weeks automating the installation of <a href="https://www.docker.com">Docker</a>,
<a href="https://traefik.io/traefik/">Traefik</a>, and <a href="https://www.ataridude.net">my Atari blog</a> with <a href="https://www.ansible.com/">Ansible</a>.
I have reached the point that I can take a fresh install of Ubuntu 20.04 LTS and end up with a
completely functional blog server. The "fresh install" requires only my user, SSH key, and passwordless
sudo configuration... once I have that, Ansible takes care of everything else. I simply run
<code>ansible-playbook playbook_blog_server.yml</code> ... et voila.</p>
<p>Now to work on correcting some of the things I did wrong here, and moving the unixdude.net blog into a
Docker container behind Traefik.</p>
<p>For those who are interested, I have <a href="https://github.com/ataridude/ansible">pushed the code</a>.</p>Docker build2021-09-07T00:00:00-04:002021-09-07T00:00:00-04:00Danieltag:www.unixdude.net,2021-09-07:/posts/2021/Sep/07/docker-build/<p>I have been having a rough couple months with printers: My Samsung C410w is getting old, so I wanted to
replace it. My wife recently heard about HP Instant Ink, so she wanted to try that service, but I
decided to buy a monochrome Brother printer -- which was great except …<a class="label label-primary read-more" href="/posts/2021/Sep/07/docker-build/"><span>Continue reading</span></a></p><p>I have been having a rough couple months with printers: My Samsung C410w is getting old, so I wanted to
replace it. My wife recently heard about HP Instant Ink, so she wanted to try that service, but I
decided to buy a monochrome Brother printer -- which was great except my wife decided she needed to be
able to print on cardstock, which the Brother could not do. So, we returned the Brother and picked up an HP
Envy 6055 -- and proceeded to print more than 750 pages last month. Suddenly, Instant Ink doesn't look
so affordable.</p>
<p>Anyway, in order to go back to a laser/LED printer, I need to find a replacement to the Envy's scanner.
Well, for 15+ years I have had a CanoScan LiDE 30, which still works fine. I had been using it for
years with <a href="http://www.ellert.se/twain-sane/">Mattias Ellert's excellent TWAIN SANE Mac utilities</a>,
but Mattias has not maintained this for 4 years now, so I switched to using scanimage on a Raspberry Pi.</p>
<p>Scanimage is fine for me, but there's no WAF there -- and now that I need to increase the WAF of my
scanner setup, and now that I'm learning about
Docker, I wanted to create a Docker image containing a scanner utility that I could run on a Raspberry
Pi connected to the scanner, which could then be left in a convenient location for my wife to use.</p>
<p>In doing some research for
this project, tonight I found <a href="https://github.com/sbs20/scanservjs">this nice front end</a>, which is
also <a href="https://hub.docker.com/r/sbs20/scanservjs">available as a Docker image</a>. Unfortunately, that
Docker image is only available for amd64 architecture -- but of course my Raspberry Pi is arm64.</p>
<p>I next tried building the image on the raspi, using the Dockerfile in the github source, but even with
an 8GB 4b model, 25+ minutes and it still had a ways to go -- and, it ran out of disk space.</p>
<p>Thankfully, I happen to have a M1 MacBook Air, so I fired up the new M1-specific version of Docker Desktop on that, and built an arm64 image
-- in about 30 seconds!</p>
<p>I quickly <a href="https://hub.docker.com/r/ataridude/scanservjs">pushed that to Docker Hub</a>, then easily ran it
on my Raspberry Pi with:</p>
<div class="highlight"><pre><span></span><code>docker container run --device=/dev/bus/usb/001/003 --name scanner -p 8080:8080 --rm ataridude/scanservjs
</code></pre></div>
<p>I'm not done yet, but this is a good place to stop tonight.</p>Traefik2021-08-12T00:00:00-04:002021-08-12T00:00:00-04:00Danieltag:www.unixdude.net,2021-08-12:/posts/2021/Aug/12/traefik/<p>Lately I have been learning about Docker, and I want to convert my current setup to Docker,
where it makes sense to do that. I currently run about 20 vhosts on this Digital Ocean droplet, and
at least one of those has a MySQL back end. I also run Postfix …<a class="label label-primary read-more" href="/posts/2021/Aug/12/traefik/"><span>Continue reading</span></a></p><p>Lately I have been learning about Docker, and I want to convert my current setup to Docker,
where it makes sense to do that. I currently run about 20 vhosts on this Digital Ocean droplet, and
at least one of those has a MySQL back end. I also run Postfix/Dovecot/Sieve for mail. And,
some other things run on here.</p>
<p>As part of my Docker studies, I was talking to a friend recently about my setup, and we were
discussing whether Docker is the right solution for the things I mentioned. For now I'm
going to hold off on using Docker for my mail server, but I know there are some
container-based mail server solutions; I will look at those later, once I have migrated at least
some of the vhosts.</p>
<p>My friend and I started by discussing the vhosts I run. Enter <a href="https://traefik.io">Traefik</a>, a free, Docker-
based and Docker-oriented proxy solution. Even better: containers are automatically registered
with Traefik, and from what I have seen so far the only configuration necessary is to set some
tags on the containers as they are run, in your docker-compose file.</p>
<p>I decided to take one of my vhosts -- <a href="http://www.ataridude.net/">www.ataridude.net</a> -- and run
it in/with Traefik. I had some good success last night, and while it is not completely ready yet,
you can add this host entry to your system if you want to see it before it goes live:</p>
<div class="highlight"><pre><span></span><code><span class="mf">159.65.169.236</span><span class="w"> </span><span class="n">www</span><span class="mf">.</span><span class="n">ataridude</span><span class="mf">.</span><span class="n">net</span>
</code></pre></div>
<p>To get this up and running, I built a docker-compose file based off of the one found in the
<a href="https://doc.traefik.io/traefik/getting-started/quick-start/">Traefik quick-start</a>:</p>
<div class="highlight"><pre><span></span><code><span class="n">version</span><span class="p">:</span><span class="w"> </span><span class="s1">'3'</span>
<span class="n">services</span><span class="p">:</span>
<span class="w"> </span><span class="n">reverse</span><span class="o">-</span><span class="n">proxy</span><span class="p">:</span>
<span class="w"> </span><span class="c1"># The official v2 Traefik docker image</span>
<span class="w"> </span><span class="n">image</span><span class="p">:</span><span class="w"> </span><span class="n">traefik</span><span class="p">:</span><span class="n">v2</span><span class="o">.</span><span class="mi">4</span>
<span class="w"> </span><span class="c1"># Enables the web UI and tells Traefik to listen to docker</span>
<span class="w"> </span><span class="n">command</span><span class="p">:</span><span class="w"> </span><span class="o">--</span><span class="n">api</span><span class="o">.</span><span class="n">insecure</span><span class="o">=</span><span class="bp">true</span><span class="w"> </span><span class="o">--</span><span class="n">providers</span><span class="o">.</span><span class="n">docker</span>
<span class="w"> </span><span class="n">ports</span><span class="p">:</span>
<span class="w"> </span><span class="c1"># The HTTP port</span>
<span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="s2">"80:80"</span>
<span class="w"> </span><span class="c1"># The Web UI (enabled by --api.insecure=true)</span>
<span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="s2">"8080:8080"</span>
<span class="w"> </span><span class="n">volumes</span><span class="p">:</span>
<span class="w"> </span><span class="c1"># So that Traefik can listen to the Docker events</span>
<span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">run</span><span class="o">/</span><span class="n">docker</span><span class="o">.</span><span class="n">sock</span><span class="p">:</span><span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">run</span><span class="o">/</span><span class="n">docker</span><span class="o">.</span><span class="n">sock</span>
<span class="w"> </span><span class="n">atariblog</span><span class="p">:</span>
<span class="w"> </span><span class="n">image</span><span class="p">:</span><span class="w"> </span><span class="n">ataridude</span><span class="o">/</span><span class="n">private</span><span class="p">:</span><span class="n">atariblog</span>
<span class="w"> </span><span class="n">labels</span><span class="p">:</span>
<span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="s2">"traefik.http.routers.atariblog.rule=Host(`www.ataridude.net`)"</span>
</code></pre></div>
<p>The observant reader will note that the Traefik admin console is open and available. Ordinarily this would not be a good idea,
but port 8080 is only available via ZeroTier (my Digital Ocean firewall configuration blocks port 8080), and if someone
joins my ZeroTier network, I have bigger issues than the Traefik admin console.</p>
<p>My blogs are Pelican-based, but I'm trying to get away from installing software on my systems,
at least as much as possible, so I decided to build the ataridude.net image using <a href="https://docs.docker.com/develop/develop-images/multistage-build/">Docker multi-
stage image builds</a>. This
means I do not need Pelican or anything else installed on my server -- I only need Docker.</p>
<p>Here is the Dockerfile I'm using to build that image:</p>
<div class="highlight"><pre><span></span><code>FROM python:3 AS builder
WORKDIR /usr/src/app
COPY requirements.txt ./requirements.txt
RUN pip install -r requirements.txt
ADD content ./content
ADD theme ./theme
COPY pelicanconf.py ./
RUN pelican /usr/src/app/content/ -s pelicanconf.py
FROM nginx:latest
COPY --from=0 /usr/src/app/output /usr/share/nginx/html
</code></pre></div>
<p>I based this on my friend Brad's <a href="https://gitlab.com/blcarman/blog/-/blob/master/Dockerfile">Dockerfile</a>.</p>
<p>It's not as automated as I'd like, but it does work, and I will continue to improve it.</p>
<p>In addition to greater automation, I plan to start moving vhosts from my FreeBSD droplet over to a new Docker- and Ubuntu-based droplet.</p>June 2021 update2021-06-14T00:00:00-04:002021-06-14T00:00:00-04:00Danieltag:www.unixdude.net,2021-06-14:/posts/2021/Jun/14/june-2021-update/<p>I've been busy over the last few weeks, so there will be several updates in here.</p>
<hr>
<p>Over the weekend I updated my Ubiquiti switches, which required that I shut down my ESXi servers. The
R610 came back just fine, but the raspi4 ARM one did not. The problem on the …<a class="label label-primary read-more" href="/posts/2021/Jun/14/june-2021-update/"><span>Continue reading</span></a></p><p>I've been busy over the last few weeks, so there will be several updates in here.</p>
<hr>
<p>Over the weekend I updated my Ubiquiti switches, which required that I shut down my ESXi servers. The
R610 came back just fine, but the raspi4 ARM one did not. The problem on the Raspberry Pi was
the <a href="https://github.com/pftf/RPi4/issues/104">known UEFI config corruption issue</a>, when the UEFI config
is saved on the SD chip.</p>
<p>I thought maybe I would have to buy a new SD chip, but it turns out that only the RPI_EFI.fd file was
corrupt. Unfortunately my previous blog posts did not serve their purpose -- I was not able to use them
to reinstall the Raspberry Pi ESXi ARM fling, and had to do all the research again, so:</p>
<ul>
<li><a href="https://rudimartinsen.com/2020/10/07/esxi-on-arm-fling-install-on-rpi/">ESXi on ARM installation howto</a> (there are many; this is the one I used last night)</li>
<li><a href="https://blogs.vmware.com/arm/2020/10/17/esxi-arm-with-iscsi/">ESXi on iSCSI on ARM</a></li>
</ul>
<p>Since my SD chip was fine -- "diff -r" showed that only RPI_EFI.fd had changed since install -- I copied the
RPI_EFI.fd file to the SD chip, then booted the Raspberry pi.</p>
<p>I updated the EFI config as shown in step 3 of the install link above (the step which removes the 3GB RAM limit),
then I reconfigured the EFI to boot from iSCSI, as shown in the second link. That post does not mention an issue
specific to Synology NASes, so I'll mention it here: when using a Synology NAS, the Boot LUN must be 1, not the
default of 0.</p>
<p>After that, I was able to boot my ESXi-on-ARM raspi again. It is currently sitting on my desk,
and I'll probably leave it there, since I do not have remote console to it: with it on my desk, I can
easily connect a monitor and keyboard when needed.</p>
<hr>
<p>I decided I wanted to refresh my VMware skills, so I signed up for VMUG Advantage again about a week ago. One
night last week I set up vCSA and then a cluster of 3 ESXi VMs. VMware's unsupported-but-works-fine-in-a-lab
nested virtualization functionality is awesome, and I was feeling generous so I gave those 3 ESXi VMs 32GB and
8 vCPUs. Since the R610 is not supported on v7 VMware products (the CPU is too old), I'm only running ESXi and
vCSA v6.7, but that's good enough for now. It might be time to upgrade soon.</p>
<p>I put those nested ESXi VMs on my lab VLAN, and I have discovered an interesting issue: I cannot use Netboot.xyz
with VMs in that cluster. I'm still trying to figure out why, but they just don't work. The ESXi VMs are on
VLAN 7 (lab VLAN, 192.168.7.0/24), and any VM on that VLAN that is hosted directly on the R610 will PXE boot to
netboot.xyz without issue, and I can install any OS. However, nested VMs (those running on the ESXi VMs) will
PXE boot to my menu, and can PXE boot local things, but they will not PXE boot netboot.xyz -- the netboot.xyz
menu loads, but no OS can be installed; see screenshot below. Again, if I do exactly the same thing with
a VM on the R610, it works perfectly. Bizarre.</p>
<p><img alt="Failed netboot.xyz install" src="/images/failed-netbootxyz-install.jpg"></p>
<hr>
<p>One specific reason for signing up for VMUG Advantuage again was that I want to play with NSX again, and to play
with vSAN for the first time. Those are both upcoming projects.</p>
<hr>
<p>Another fun thing I did over the weekend was to telnet to an Ubuntu VM from my Atari 800. There is a really cool
peripheral currently available for Atari 8-bit computers called the <a href="https://fujinet.online">FujiNet</a>, and using
a terminal program, one can use telnet. I have <a href="https://www.ataridude.net/posts/2021/Jun/14/telnetting-via-fujinet/">written up more on my Atari blog</a>.</p>ZeroTier Is Fun2021-05-18T00:00:00-04:002021-05-18T00:00:00-04:00Danieltag:www.unixdude.net,2021-05-18:/posts/2021/May/18/zerotier-is-fun/<p>I recently installed a <a href="https://www.synology.com/en-us/products/DS118">Synology DS118</a> at a friend's house
for remote backup using <a href="https://www.synology.com/en-us/dsm/feature/hyper_backup">Synology's Hyper Backup solution</a>.
The DS118 is on my ZeroTier "storage" network, a network shared by the main Synology at my house -- that way,
my main NAS backs up to a local system (that is, something …<a class="label label-primary read-more" href="/posts/2021/May/18/zerotier-is-fun/"><span>Continue reading</span></a></p><p>I recently installed a <a href="https://www.synology.com/en-us/products/DS118">Synology DS118</a> at a friend's house
for remote backup using <a href="https://www.synology.com/en-us/dsm/feature/hyper_backup">Synology's Hyper Backup solution</a>.
The DS118 is on my ZeroTier "storage" network, a network shared by the main Synology at my house -- that way,
my main NAS backs up to a local system (that is, something on the same layer 2 network), which makes that part
easy. I was able to prime the backup at my house and ensure that everything worked, then I gave the DS118 to my
friend to take to his house -- and the backup worked exactly as it did when both units were at my house.
This is why I like ZeroTier.</p>
<p>Today I realized that the DS118 is not sending notification emails, and I definitely want it to be able to send email
when it has something to report. I enabled notifications on the DS118 and configured it to send me mail. It seems
my friend's ISP is blocking outbound email, because every test message failed -- that is, until I reconfigured
the DS118 to relay through the raspberry pi at my house, which is on the same ZeroTier "storage" network.</p>
<p>The raspberry pi is my home mail relay, through which all other systems at my house send mail to my DigitalOcean droplet:
the raspi and the droplet are on the same ZeroTier network.</p>
<p>So, now I have notifications from my DS118, relayed through the raspi at my house, using two ZeroTier networks.</p>
<p>ZeroTier is fun.</p>PXE booting on ARM642021-04-23T00:00:00-04:002021-04-23T00:00:00-04:00Danieltag:www.unixdude.net,2021-04-23:/posts/2021/Apr/23/pxe-booting-on-arm64/<p>As previously mentioned, my next goal was going to be PXE booting VMs running on the
ESXi ARM Fling. Using <a href="https://discourse.ubuntu.com/t/netbooting-the-live-server-installer-via-uefi-pxe-on-arm-aarch64-arm64-and-x86-64-amd64/19240">this page</a> as a guide, I now have this working.</p>
<p>That guide is pretty good, but I had to do adapt it to my setup.</p>
<p>As instructed, I started by …<a class="label label-primary read-more" href="/posts/2021/Apr/23/pxe-booting-on-arm64/"><span>Continue reading</span></a></p><p>As previously mentioned, my next goal was going to be PXE booting VMs running on the
ESXi ARM Fling. Using <a href="https://discourse.ubuntu.com/t/netbooting-the-live-server-installer-via-uefi-pxe-on-arm-aarch64-arm64-and-x86-64-amd64/19240">this page</a> as a guide, I now have this working.</p>
<p>That guide is pretty good, but I had to do adapt it to my setup.</p>
<p>As instructed, I started by downloading the grub EFI binary and installing that
on my TFTP server, at <code>tftp.unixdude.net/grubnetaa64.efi.signed</code>.</p>
<p>I updated my <a href="https://store.ui.com/products/unifi-security-gateway">USG</a> to add a new IP subnet and VLAN
for the ARM64 VMs, so that I could set up a PXE configuration specifically for those VMs, without
affecting the PXE setup I have for x64 systems. Yes, if I ran my own DHCP server, I could have added
configuration to it to detect the client architecture and to have it send the correct boot code based
on that. This way was easy, and it has the benefit of not affecting my home network in the case that the DHCP
server VM is down. (High WAF in the network not going down.)</p>
<p>On the new subnet, I specify the DNS server (my <a href="https://www.unixdude.net/posts/2021/Mar/11/frr-and-anycast/">anycast IP address</a>), and
I point the "DHCP network boot" options to my TFTP server, with a boot filename of <code>/grubnetaa64.efi.signed</code>.
I also specify the DHCP TFTP Server IP address.</p>
<p>On the filer, I extracted the <code>grub.cfg</code>, <code>initrd</code>, and <code>vmlinuz</code> files as instructed. Initially
I tried putting those files into a <code>/arm64</code> directory on my filer, but that did not work for me, so
I moved them to the root directory and was able to PXE boot the VM right away.</p>
<p>I added the <code>grub.cfg</code> entry as indicated, modifying it to point to my local copy of the
Ubuntu image.</p>
<p>After that, I booted the VM. The first time I booted it, I ran out of memory because
I had only given the VM 1GB of RAM. I increased that to 4GB (half the memory of the raspi)
and was able to successfully PXE boot a VM and install Ubuntu.</p>
<p><img alt="arm-pxe2" src="/images/arm-pxe2.png">
<img alt="arm-pxe4" src="/images/arm-pxe4.png"></p>
<p>I consider this a success despite its impracticality: I cannot realistically PXE boot VMs if
I have to dedicate half the server's memory in order to do it.</p>ESXi on ARM Fling - Update2021-04-20T00:00:00-04:002021-04-20T00:00:00-04:00Danieltag:www.unixdude.net,2021-04-20:/posts/2021/Apr/20/esxi-on-arm-update/<p>Not long after my last post, the PoE HAT for my Raspberry Pi died -- to be more accurate, one of the components
on the board disintegrated. I don't know if this was a manufacturing defect or the result of a bad solder job
related to the fan, or something else …<a class="label label-primary read-more" href="/posts/2021/Apr/20/esxi-on-arm-update/"><span>Continue reading</span></a></p><p>Not long after my last post, the PoE HAT for my Raspberry Pi died -- to be more accurate, one of the components
on the board disintegrated. I don't know if this was a manufacturing defect or the result of a bad solder job
related to the fan, or something else. In any case, disposed of that HAT and ordered the GeeekPi PoE HAT. My
ESXi-on-ARM setup has been up and running since, connected by a single cable.</p>
<p><img alt="Raspi 4" src="/images/raspi4.jpg"></p>
<p>I have this system booting from iSCSI, so there are no local filesystems -- the SD card is used only to initiate
the boot process, the system boots from iSCSI, and datastores are on the filer and are accessed via NFS. I love
the elegance of this setup.</p>
<p><img alt="ESX-ARM" src="/images/esxarm.jpg"></p>
<p>So far, I have created several VMs: Alpine, CentOS 8, Ubuntu, Raspberry Pi OS, and FreeBSD. I haven't really
done much with any of this, but in my time with it so far I am impressed with its speed. Speed-wise, it obviously doesn't
compare to my R610, but for about $100, it's great for what it is, and it makes an excellent geek toy. And yes, I bought it mostly as a toy, but I do plan to
use it for some real-world services: In addition to general OS play, I plan to at least add a VM here to my <a href="https://www.unixdude.net/posts/2021/Mar/11/frr-and-anycast/">anycast DNS setup</a>.</p>
<p>I might also build a VM on this server in order to relocate my UniFi controller. I'm undecided
because I'm not sure how
much I like the problem that involves: if the raspi dies, so does my ability to run my controller. Solving this problem is important since I
currently run my UniFi controller in a VM that is hosted on my R610 and is NFS mounted. I do need to find
another solution to the UniFi controller issue, since there are two switches between my R610 and my filer --
meaning that whenever I upgrade one of those switches, I have to reloate the controller VM to my iMac. This
is not how you should do it!</p>
<p>I haven't installed vSphere in a while, but now that I have two ESXi servers, I might do that again, so
that I can manage this entire setup more easily.</p>ESXi on ARM Fling2021-04-07T00:00:00-04:002021-04-07T00:00:00-04:00Danieltag:www.unixdude.net,2021-04-07:/posts/2021/Apr/07/esxi-on-arm-fling/<p>I recently decided that I wanted to play with the <a href="https://flings.vmware.com/esxi-arm-edition">ESXi-on-ARM Fling</a>, so
I purchased a <a href="https://www.raspberrypi.org/products/raspberry-pi-4-model-b/">Raspberry Pi 4B</a> (8GB).
I received my unit so I set about installing ESXi.</p>
<p>I followed VMware's instructions in their "Fling-on-Raspberry-Pi" document. It's not a difficult
process, but there are a few things to …<a class="label label-primary read-more" href="/posts/2021/Apr/07/esxi-on-arm-fling/"><span>Continue reading</span></a></p><p>I recently decided that I wanted to play with the <a href="https://flings.vmware.com/esxi-arm-edition">ESXi-on-ARM Fling</a>, so
I purchased a <a href="https://www.raspberrypi.org/products/raspberry-pi-4-model-b/">Raspberry Pi 4B</a> (8GB).
I received my unit so I set about installing ESXi.</p>
<p>I followed VMware's instructions in their "Fling-on-Raspberry-Pi" document. It's not a difficult
process, but there are a few things to note about the process found in that document.</p>
<p>One of the first steps is to update the Raspberry Pi's EEPROM; VMware's document says to
use the Raspberry Pi Imager to flash an SD chip with the "Raspberry Pi EEPROM boot recovery" utility,
but I found this to not be available in the Imager utility. With a little help from my favorite search engine, I found
<a href="https://www.raspberrypi.org/documentation/hardware/raspberrypi/booteeprom.md">the official documentation on updating the boot EEPROM</a>,
which includes commands to do it from a running instance of Raspberry Pi OS. With that I was able
to confirm that my raspi is up-to-date.</p>
<p>One thing to note is that the official PoE HAT is not supported (at least, not out of the box), because
its fan is controlled by the I2C, which is nonfunctional when running ESXi. Unfortunately for me, I did
not know about this limitation before buying the official PoE HAT; I chose to solve this problem by
soldering leads from pins 4 & 6 on the GPIO directly to the fan -- I want the fan to always run anyway,
so that is a fine solution for me.</p>
<p>The main use for a micro SD chip is to configure the UEFI, so don't buy a large one. In my experience,
the micro SD chip is also your main boot device (without one inserted, my unit won't boot). Also, you
need a USB drive on which to install ESXi. NFS datastores are supported, so I am mounting those from
my NAS. I use one datastore for installation ISOs, and one for VMs.</p>
<p>I am looking into installing ESXi on an iSCSI LUN but so far I have not been able to get that
to work with my Synology: the Raspberry Pi connects (I see this on the Synology), but the LUN does not
show up in the device list on the raspi.</p>
<p>Be sure to follow the instructions on configuring the NTP client, since it works differently than an
x86_64 ESXi server does.</p>
<p>So far I have installed Ubuntu 20 and CentOS 8. This is looking like a great little toy!</p>M1 MacBook Air2021-04-06T00:00:00-04:002021-04-06T00:00:00-04:00Danieltag:www.unixdude.net,2021-04-06:/posts/2021/Apr/06/m1-macbook-air/<p>I recently decided to upgrade my personal laptop. Until recently, I was using a 2015 MacBook Pro that I
bought in 2016 after Apple announced they were bringing the butterfly keyboard to the MBP. My goal for
that purchase was for it to last until Apple released a better keyboard …<a class="label label-primary read-more" href="/posts/2021/Apr/06/m1-macbook-air/"><span>Continue reading</span></a></p><p>I recently decided to upgrade my personal laptop. Until recently, I was using a 2015 MacBook Pro that I
bought in 2016 after Apple announced they were bringing the butterfly keyboard to the MBP. My goal for
that purchase was for it to last until Apple released a better keyboard -- and it worked. Last month I
traded my 2015 MBP for a new 2020 M1-powered MacBook Air, and I could not be happier with this upgrade:
The M1 is an amazing CPU, the keyboard is awesome, and the M1 MBA is a phenomenal package.</p>
<p>Because of my love of operating systems (specifically Unix-based ones, obviously), one of the first
things I did with this system was to download the <a href="https://b2b.parallels.com/Apple-Silicon">technical preview of Parallels Desktop for M1</a>, so that I could run VMs on this new laptop.</p>
<p>So far, I have installed <a href="https://alpinelinux.org/downloads/">Alpine</a>, <a href="https://www.debian.org/distrib/netinst">Debian</a>, <a href="https://getfedora.org/en/server/download/">Fedora</a>, <a href="https://github.com/vmware/photon/wiki/Downloading-Photon-OS">Photon OS</a>, <a href="https://ubuntu.com/download/server/arm">Ubuntu</a>, <a href="https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewARM64">Windows 10</a>, and <a href="https://www.freebsd.org/where/">FreeBSD</a>. Most of
those distros/OSes install exactly as you would hope, but a few required some special tweaks.</p>
<p>For Alpine and Photon OS, I found that the Parallels VM must be configured as a Debian VM.</p>
<p>For FreeBSD, I used "Other Linux" as the OS type, and the most important part for FreeBSD is to
configure the VM to use only 1 vCPU -- it will not boot with more than 1 vCPU.</p>
<p>So far I am extremely impressed with this M1 unit. With the ability to run such a variety of
operating systems in VMs, it will prove even more useful to me.</p>Lab, and network upgrade2021-03-12T00:00:00-05:002021-03-12T00:00:00-05:00Danieltag:www.unixdude.net,2021-03-12:/posts/2021/Mar/12/catalyst/<p>I like networking -- always have. However, today, my networking knowledge is only okay -- but I want it to be great! My goal is to
achieve CCNP certification in 6 months. To help with this effort, I have added a Cisco lab to my home network.
It is old equipment (2x …<a class="label label-primary read-more" href="/posts/2021/Mar/12/catalyst/"><span>Continue reading</span></a></p><p>I like networking -- always have. However, today, my networking knowledge is only okay -- but I want it to be great! My goal is to
achieve CCNP certification in 6 months. To help with this effort, I have added a Cisco lab to my home network.
It is old equipment (2x 2610, 1x 2611, 3x 2950) running IOS 11 and 12, but that's okay, and it will soon be
upgraded: as I cut over to Ubiquiti equipment for my home network, the 2960s I am using today will move to the lab,
and I will add a layer 3 switch as well. The lab routers will also be upgraded to something running IOS 15, but I'm not sure what yet.</p>
<p>I like network diagrams, so for fun, here is a diagram of my current home network:</p>
<p><img alt="Home network 2021" src="/images/home_network_2021-03.jpg"></p>
<p>And here is my Cisco lab:</p>
<p><img alt="Cisco lab 2021-03" src="/images/cisco_lab_2021-03.jpg"></p>
<p>As you can probably imagine, I have been fun expanding my use of VLANs and BGP in my home network.</p>
<p>I have further plans as well:</p>
<ul>
<li>Introduce a MySQL cluster. This will use an anycast address (see earlier post on anycast) so that clients
can specify the anycast address and not care which server they talk to.</li>
<li>Introduce a router between my lab and my home network. Current plan is to use Cumulus VX for this, because
I want to learn Cumulus Linux as well.</li>
<li>I want the lab completely separated from my home network; as you can see from the diagrams above, the lab
is connected to my home network. This will change as I introduce Cumulus VX to the mix.</li>
</ul>
<p>Planned network upgrades:</p>
<ul>
<li>The 2960 in the middle will be replaced with a <a href="https://store.ui.com/collections/unifi-network-routing-switching/products/usw-lite-16-poe">Ubiquiti USW-Lite-16-PoE</a></li>
<li>The MikroTik hap AC in the entertainment center will be replaced with a <a href="https://store.ui.com/collections/unifi-network-routing-switching/products/usw-flex-mini">USW-Flex-Mini</a> and a <a href="https://store.ui.com/collections/unifi-network-access-points/products/unifi-6-long-range-access-point">U6-LR-US</a></li>
<li>The HP ProCurve at my desk will be replaced with a <a href="https://store.ui.com/collections/unifi-network-routing-switching/products/unifi-switch-8-150w">US-8</a></li>
</ul>
<p>At that point, all of the switches and APs will be PoE, meaning I will have only a single place for battery
backup of the entire network, and fewer wall warts.</p>FRR and Anycast2021-03-11T00:00:00-05:002021-03-11T00:00:00-05:00Danieltag:www.unixdude.net,2021-03-11:/posts/2021/Mar/11/frr-and-anycast/<p><a href="https://frrouting.org">FRR</a> is one of the things I have wanted to configure on my Raspberry Pi, specifically to enable
<a href="https://en.wikipedia.org/wiki/Anycast">anycast</a> for Pi-Hole. The reason to do this is that I do not want my Raspberry Pi to be a single
point of failure on the network, especially since it has been …<a class="label label-primary read-more" href="/posts/2021/Mar/11/frr-and-anycast/"><span>Continue reading</span></a></p><p><a href="https://frrouting.org">FRR</a> is one of the things I have wanted to configure on my Raspberry Pi, specifically to enable
<a href="https://en.wikipedia.org/wiki/Anycast">anycast</a> for Pi-Hole. The reason to do this is that I do not want my Raspberry Pi to be a single
point of failure on the network, especially since it has been connected via wifi, to date anyway.
Once I finish my transition to UniFi, it will be moved to ethernet.</p>
<p>Anycast is useful for services like DNS, DHCP, and MySQL,
and is easier, better in some cases, than using a load balancer. I wanted to use anycast on my home
network so that I can upgrade or reboot my Raspberry Pi, while ensuring that such maintenance
activity does not negatively affect users on my network.</p>
<p>I started out by creating a DNS entry:</p>
<p><code>pihole-anycast.unixdude.net has address 192.168.2.1</code></p>
<p>This entry is not strictly required, it's just a convenience.</p>
<p>I then installed FRR on multiple systems -- my Raspberry Pi and a VM
running Ubuntu 20.04.2 LTS, running on my ESXi server.</p>
<p>On each system, I added a loopback address; on the Ubuntu VM I added the following block
to <code>/etc/netplan/00-installer-config.yml</code>:</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nx">network</span><span class="p">:</span>
<span class="w"> </span><span class="nx">ethernets</span><span class="p">:</span>
<span class="w"> </span><span class="nx">lo</span><span class="p">:</span>
<span class="w"> </span><span class="nx">renderer</span><span class="p">:</span><span class="w"> </span><span class="nx">networkd</span>
<span class="w"> </span><span class="k">match</span><span class="p">:</span>
<span class="w"> </span><span class="nx">name</span><span class="p">:</span><span class="w"> </span><span class="nx">lo</span>
<span class="w"> </span><span class="nx">addresses</span><span class="p">:</span>
<span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m m-Double">192.168.2.1</span><span class="o">/</span><span class="mi">32</span>
</code></pre></div>
<p>On the Raspberry Pi, running Raspbian (stretch), I created a file with the following contents,
saved as <code>/etc/network/interfaces.d/lo</code>.</p>
<div class="highlight"><pre><span></span><code><span class="kt">auto</span><span class="w"> </span><span class="nx">lo</span>
<span class="nx">iface</span><span class="w"> </span><span class="nx">lo</span><span class="w"> </span><span class="nx">inet</span><span class="w"> </span><span class="nx">loopback</span>
<span class="kt">auto</span><span class="w"> </span><span class="nx">lo</span><span class="p">:</span><span class="mi">0</span>
<span class="nx">iface</span><span class="w"> </span><span class="nx">lo</span><span class="p">:</span><span class="mi">0</span><span class="w"> </span><span class="nx">inet</span><span class="w"> </span><span class="nx">static</span>
<span class="w"> </span><span class="nx">address</span><span class="w"> </span><span class="m m-Double">192.168.2.1</span>
<span class="w"> </span><span class="nx">netmask</span><span class="w"> </span><span class="m m-Double">255.255.255.255</span>
<span class="w"> </span><span class="kd">alias</span><span class="w"> </span><span class="nx">pihole</span>
</code></pre></div>
<p>To ensure that this file is loaded, you might need to confirm that <code>/etc/network/interfaces</code>
includes the following line:</p>
<div class="highlight"><pre><span></span><code>source-directory /etc/network/interfaces.d
</code></pre></div>
<p>There are two important things to note in the examples above:</p>
<ul>
<li>that the address is a /32,</li>
<li>and that it is added to the loopback interface.</li>
</ul>
<p>Next, configure FRR to publish that loopback address to the network. I do this using BGP,
and the relevant part of my configuration is:</p>
<div class="highlight"><pre><span></span><code><span class="nx">router</span><span class="w"> </span><span class="nx">bgp</span><span class="w"> </span><span class="mi">65007</span>
<span class="w"> </span><span class="nx">neighbor</span><span class="w"> </span><span class="m m-Double">192.168.1.254</span><span class="w"> </span><span class="nx">remote</span><span class="o">-</span><span class="k">as</span><span class="w"> </span><span class="mi">65007</span>
<span class="w"> </span><span class="p">!</span>
<span class="w"> </span><span class="nx">address</span><span class="o">-</span><span class="nx">family</span><span class="w"> </span><span class="nx">ipv4</span><span class="w"> </span><span class="nx">unicast</span>
<span class="w"> </span><span class="nx">redistribute</span><span class="w"> </span><span class="nx">connected</span><span class="w"> </span><span class="nx">route</span><span class="o">-</span><span class="nx">map</span><span class="w"> </span><span class="nx">anycast</span>
<span class="w"> </span><span class="nx">exit</span><span class="o">-</span><span class="nx">address</span><span class="o">-</span><span class="nx">family</span>
<span class="p">!</span>
<span class="nx">access</span><span class="o">-</span><span class="nx">list</span><span class="w"> </span><span class="mi">7</span><span class="w"> </span><span class="nx">seq</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="nx">permit</span><span class="w"> </span><span class="m m-Double">192.168.2.1</span><span class="o">/</span><span class="mi">32</span>
<span class="p">!</span>
<span class="nx">route</span><span class="o">-</span><span class="nx">map</span><span class="w"> </span><span class="nx">anycast</span><span class="w"> </span><span class="nx">permit</span><span class="w"> </span><span class="mi">1</span>
<span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="nx">ip</span><span class="w"> </span><span class="nx">address</span><span class="w"> </span><span class="mi">7</span>
<span class="p">!</span>
</code></pre></div>
<p>As you can see, I am using BGP ASN 65007 on my home network. My router's IP address is 192.168.1.254,
so I have FRR peer with that address. Note the route
map and access list configured to publish the loopback's /32 address.</p>
<p>We can take a look at my Ubiquiti USG's BGP routes for that address, and we see the two routes:</p>
<div class="highlight"><pre><span></span><code><span class="k">admin</span><span class="nv">@ubnt</span><span class="err">:</span><span class="o">~</span><span class="err">$</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">ip</span><span class="w"> </span><span class="n">bgp</span><span class="w"> </span><span class="mf">192.168.2.1</span><span class="o">/</span><span class="mi">32</span>
<span class="n">BGP</span><span class="w"> </span><span class="n">routing</span><span class="w"> </span><span class="nc">table</span><span class="w"> </span><span class="n">entry</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="mf">192.168.2.1</span><span class="o">/</span><span class="mi">32</span>
<span class="nl">Paths</span><span class="p">:</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="n">available</span><span class="p">,</span><span class="w"> </span><span class="n">best</span><span class="w"> </span><span class="n">#1</span><span class="p">,</span><span class="w"> </span><span class="nc">table</span><span class="w"> </span><span class="k">Default</span><span class="o">-</span><span class="n">IP</span><span class="o">-</span><span class="n">Routing</span><span class="o">-</span><span class="nc">Table</span><span class="p">)</span>
<span class="w"> </span><span class="ow">Not</span><span class="w"> </span><span class="n">advertised</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="ow">any</span><span class="w"> </span><span class="n">peer</span>
<span class="w"> </span><span class="k">Local</span>
<span class="w"> </span><span class="mf">192.168.2.249</span><span class="w"> </span><span class="p">(</span><span class="n">metric</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="mf">192.168.2.249</span><span class="w"> </span><span class="p">(</span><span class="mf">192.168.2.1</span><span class="p">)</span>
<span class="w"> </span><span class="n">Origin</span><span class="w"> </span><span class="n">incomplete</span><span class="p">,</span><span class="w"> </span><span class="n">metric</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">localpref</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="n">valid</span><span class="p">,</span><span class="w"> </span><span class="n">internal</span><span class="p">,</span><span class="w"> </span><span class="n">best</span>
<span class="w"> </span><span class="k">Last</span><span class="w"> </span><span class="k">update</span><span class="err">:</span><span class="w"> </span><span class="n">Sun</span><span class="w"> </span><span class="n">Mar</span><span class="w"> </span><span class="mi">7</span><span class="w"> </span><span class="mi">23</span><span class="err">:</span><span class="mi">03</span><span class="err">:</span><span class="mi">51</span><span class="w"> </span><span class="mi">2021</span>
<span class="w"> </span><span class="k">Local</span>
<span class="w"> </span><span class="mf">192.168.2.250</span><span class="w"> </span><span class="p">(</span><span class="n">metric</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="mf">192.168.2.250</span><span class="w"> </span><span class="p">(</span><span class="mf">192.168.2.1</span><span class="p">)</span>
<span class="w"> </span><span class="n">Origin</span><span class="w"> </span><span class="n">incomplete</span><span class="p">,</span><span class="w"> </span><span class="n">metric</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">localpref</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="n">valid</span><span class="p">,</span><span class="w"> </span><span class="n">internal</span>
<span class="w"> </span><span class="k">Last</span><span class="w"> </span><span class="k">update</span><span class="err">:</span><span class="w"> </span><span class="n">Sun</span><span class="w"> </span><span class="n">Mar</span><span class="w"> </span><span class="mi">7</span><span class="w"> </span><span class="mi">13</span><span class="err">:</span><span class="mi">29</span><span class="err">:</span><span class="mi">37</span><span class="w"> </span><span class="mi">2021</span>
<span class="k">admin</span><span class="nv">@ubnt</span><span class="err">:</span><span class="o">~</span><span class="err">$</span><span class="w"> </span>
</code></pre></div>
<p>And here is its routing table:</p>
<div class="highlight"><pre><span></span><code><span class="k">admin</span><span class="nv">@ubnt</span><span class="err">:</span><span class="o">~</span><span class="err">$</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">ip</span><span class="w"> </span><span class="n">route</span><span class="w"> </span>
<span class="nl">Codes</span><span class="p">:</span><span class="w"> </span><span class="n">K</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">kernel</span><span class="w"> </span><span class="n">route</span><span class="p">,</span><span class="w"> </span><span class="n">C</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">connected</span><span class="p">,</span><span class="w"> </span><span class="n">S</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="k">static</span><span class="p">,</span><span class="w"> </span><span class="n">R</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">RIP</span><span class="p">,</span><span class="w"> </span><span class="n">O</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">OSPF</span><span class="p">,</span>
<span class="w"> </span><span class="n">I</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">ISIS</span><span class="p">,</span><span class="w"> </span><span class="n">B</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">BGP</span><span class="p">,</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">selected</span><span class="w"> </span><span class="n">route</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">FIB</span><span class="w"> </span><span class="n">route</span>
<span class="n">S</span><span class="o">>*</span><span class="w"> </span><span class="mf">0.0.0.0</span><span class="o">/</span><span class="mi">0</span><span class="w"> </span><span class="o">[</span><span class="n">1/0</span><span class="o">]</span><span class="w"> </span><span class="n">via</span><span class="w"> </span><span class="mf">172.16.0.254</span><span class="p">,</span><span class="w"> </span><span class="n">eth0</span>
<span class="n">C</span><span class="o">>*</span><span class="w"> </span><span class="mf">192.168.2.0</span><span class="o">/</span><span class="mi">24</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">directly</span><span class="w"> </span><span class="n">connected</span><span class="p">,</span><span class="w"> </span><span class="n">eth1</span><span class="mf">.2</span>
<span class="n">B</span><span class="o">>*</span><span class="w"> </span><span class="mf">192.168.2.1</span><span class="o">/</span><span class="mi">32</span><span class="w"> </span><span class="o">[</span><span class="n">200/0</span><span class="o">]</span><span class="w"> </span><span class="n">via</span><span class="w"> </span><span class="mf">192.168.2.249</span><span class="p">,</span><span class="w"> </span><span class="n">eth1</span><span class="mf">.2</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="n">d17h41m</span>
<span class="k">admin</span><span class="nv">@ubnt</span><span class="err">:</span><span class="o">~</span><span class="err">$</span><span class="w"> </span>
</code></pre></div>
<p>(some data removed)</p>
<p>Now, anything on my home
network can reach 192.168.2.1/32, and it does not matter which Pi-Hole answers the request, because both
are configured identically. (I have to manually keep their configurations in sync today, but I am
working on that.)</p>
<p>As I mentioned earlier, a setup like this is useful for DNS, as I am doing here. It is also useful
for DHCP, where multiple DHCP servers use the same back end, and are all configured with the same
DHCP server identifier. Using this configuration, the DHCP service stays up as long as at least one
DHCP server is available; the backend database ensures consistency between the pools advertised by
the servers.</p>
<p>Speaking of databases, this solution works similarly with MySQL: one can build a Percona cluster, where
all servers share a single IP address via anycast. Clients connect to the anycast address, and the
cluster keeps all servers updated to the latest changes.</p>General update2021-03-10T00:00:00-05:002021-03-10T00:00:00-05:00Danieltag:www.unixdude.net,2021-03-10:/posts/2021/Mar/10/2021-03-general-update/<p>It has been a while since I did much of interest in the home lab, but that is changing this week.</p>
<p>I am starting to rework lots of things:</p>
<ul>
<li>This blog content is now tracked in a bitbucket repo</li>
<li>I am upgrading my home network to UniFi hardware</li>
<li>I am …</li><a class="label label-primary read-more" href="/posts/2021/Mar/10/2021-03-general-update/"><span>Continue reading</span></a></ul><p>It has been a while since I did much of interest in the home lab, but that is changing this week.</p>
<p>I am starting to rework lots of things:</p>
<ul>
<li>This blog content is now tracked in a bitbucket repo</li>
<li>I am upgrading my home network to UniFi hardware</li>
<li>I am moving to an IaC setup</li>
</ul>
<p>The first part was easy: create a repo, then push to it:</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="n">git</span><span class="w"> </span><span class="n">remote</span><span class="w"> </span><span class="k">add</span><span class="w"> </span><span class="n">origin</span><span class="w"> </span><span class="n">git</span><span class="nv">@bitbucket</span><span class="p">.</span><span class="nl">org</span><span class="p">:</span><span class="n">unixdude</span><span class="o">/</span><span class="n">blog</span><span class="p">.</span><span class="n">git</span>
<span class="w"> </span><span class="n">git</span><span class="w"> </span><span class="n">push</span><span class="w"> </span><span class="o">-</span><span class="n">u</span><span class="w"> </span><span class="n">origin</span><span class="w"> </span><span class="n">master</span>
</code></pre></div>
<p>I should have done that a long time ago, but at least I had it tracked in a repo. Now it is tracked in a
remote repo. I will eventually migrate this server to Linux so that I can use containers; right now it runs
FreeBSD.</p>
<p>The second part comes after much consideration of what I wanted to do. My 3-story house has many WiFi dead spots,
and I'm tired of that: it means that my Airport Express and Ring doorbell often don't work well. I decided to go
with a UniFi setup, mostly for the single management pane and the ease of adding and upgrading hardware. I should
have the new hardware sometime this week, and I hope to get it installed soon after I receive it.</p>
<p>Regarding the IaC setup, that is something several friends have done, and is something I should have done sooner.</p>
<p>I'm working toward having more cattle and fewer pets, and I'm working toward automated installs of that cattle.
I'm starting with Pi-Hole, which I use on a pet Raspberry Pi today. When I'm done, Pi-Hole will be installed
on the raspi as well as a VM, and those two systems will share an IP address via anycast, so either one can go
down without affecting users on my network.</p>
<p>I'm also working toward using containers for things like Pi-Hole. All of this will be installed and configured
via Ansible.</p>
<p>Speaking of Ansible, it is taking over some of the responsibilities of my PXE setup: my PXE setup now defaults to
a very basic install of Ubuntu 20.04.2 LTS, and Ansible will now be responsible for installing and configuring
packages such as NTP and other user- and system-configuration.</p>NFS automounter2019-09-16T00:00:00-04:002019-09-16T00:00:00-04:00Danieltag:www.unixdude.net,2019-09-16:/posts/2019/Sep/16/nfs-automounter/<p>In <a href="https://www.unixdude.net/posts/2019/Feb/20/scanner/">an earlier post</a>, I mentioned the NFS automounter.
I make use of the NFS automounter everywhere. And by "everywhere," I mean "nearly every Unix system I install."
FreeBSD, NeXT systems, Mac OS X, Linux -- they all get the configuration.</p>
<p>The NFS automounter is useful because of the ease of …<a class="label label-primary read-more" href="/posts/2019/Sep/16/nfs-automounter/"><span>Continue reading</span></a></p><p>In <a href="https://www.unixdude.net/posts/2019/Feb/20/scanner/">an earlier post</a>, I mentioned the NFS automounter.
I make use of the NFS automounter everywhere. And by "everywhere," I mean "nearly every Unix system I install."
FreeBSD, NeXT systems, Mac OS X, Linux -- they all get the configuration.</p>
<p>The NFS automounter is useful because of the ease of use: if I need a file, I simply cd to the correct
location and use the file.</p>
<p>I install my automounts in /auto, so I cd to /auto/documents or /auto/downloads or whatever. My ESXi server
uses files that on other systems are located under /auto/esx.</p>
<p>To make best use of this, I use the same user ID on all systems, and for security best practices, I set root squash
even though I'm the only user on the filer.</p>
<p>Also, because my filer is a Synology, I am able to share audio/video between iTunes and Synology's DS Audio/DS Video.
I do this by storing my iTunes library under /auto/media, and I set DS Audio and DS Video to use files found under
/volume1/media on the Synology.</p>
<p>The NFS filer is also useful remotely, because of <a href="https://www.unixdude.net/posts/2019/Sep/10/home-network-update-2019-09/">my use of ZeroTier</a>. The NFS automounter,
with its automatic disconnects, works particularly well in this type of situation.</p>Home Network update 2019-092019-09-10T00:00:00-04:002019-09-10T00:00:00-04:00Danieltag:www.unixdude.net,2019-09-10:/posts/2019/Sep/10/home-network-update-2019-09/<p>My home network has changed significantly in recent months.</p>
<p>First, I purchased three MikroTik devices, an <a href="https://mikrotik.com/product/RB260GS">RB260GS</a> for my office and two
<a href="https://mikrotik.com/product/RB962UiGS-5HacT2HnT">hAP AC</a>, one for my office and one for my garage. This allowed
me to simplify my network and it provides for several changes. My network now looks …<a class="label label-primary read-more" href="/posts/2019/Sep/10/home-network-update-2019-09/"><span>Continue reading</span></a></p><p>My home network has changed significantly in recent months.</p>
<p>First, I purchased three MikroTik devices, an <a href="https://mikrotik.com/product/RB260GS">RB260GS</a> for my office and two
<a href="https://mikrotik.com/product/RB962UiGS-5HacT2HnT">hAP AC</a>, one for my office and one for my garage. This allowed
me to simplify my network and it provides for several changes. My network now looks like this:</p>
<p><img alt="Home network 2019" src="/images/home_network_2019.jpg"></p>
<p>As a result, I can upgrade the Synology RT1900ac without taking down my entire home network. I can upgrade the
office switch or hAP AC without affecting my family's network access, and updates to the garage hAP AC will only
affect my R610-based home lab.</p>
<hr>
<p>A while ago I was introduced to <a href="http://www.zerotier.com/">Zerotier</a>. I gave it
a look and found an extremely useful networking solution. To use it, one need only do a few simple things:</p>
<ul>
<li>
<p>Create a Zerotier login</p>
</li>
<li>
<p>Create a network</p>
</li>
<li>
<p>Install the software on your systems/devices</p>
</li>
<li>
<p>Join those systems/devices to your network</p>
</li>
<li>
<p>Approve the join requests (if your network is private)</p>
</li>
</ul>
<p>The <a href="https://my.zerotier.com/">Zerotier admin UI</a> shows the IP addresses of each system, and it allows the
user to set a nickname for each device.</p>
<p>Using this solution, I was able to completely disable global SSH access to all of my systems:
now, the only way to SSH to my systems is to be on my Zerotier network or my home network.</p>
<hr>
<p>I created a services VM that sits on both ZeroTier and my home network; this VM forwards packets between
ZeroTier and my home network, so with a few simple routes added to a few devices, I can now access my entire home
network from anywhere:</p>
<ul>
<li>Synology RT1900ac has routes to the Zerotier subnet (via the services VM) and the lab subnet (via the hAP AC in the garage)</li>
<li>My laptop has routes to the home and lab subnets, via the services VM</li>
</ul>
<hr>
<p>macOS has long had a very cool feature where you can use per-domain DNS servers, so I use my home-based DNS
servers to resolve *.unixdude.net no matter where I am. This, coupled with these new routes, allows me to
resolve everything at home by hostname, and to connect to it. This is immensely useful.</p>SSH, screen, and tunnels - oh my2019-08-28T00:00:00-04:002019-08-28T00:00:00-04:00Danieltag:www.unixdude.net,2019-08-28:/posts/2019/Aug/28/ssh-screen-tunnels-oh-my/<p>I was recently asked about SSH, tunnels, and reconnecting disconnected sessions. Some people use
<a href="https://mosh.org/">mosh</a>, but I take a different approach, one that does not require any special
server software on the target server.</p>
<p>As with mosh, I recognize that sessions will become disconnected. As such, I run a
<a href="https://www.gnu.org/software/screen/">screen …</a><a class="label label-primary read-more" href="/posts/2019/Aug/28/ssh-screen-tunnels-oh-my/"><span>Continue reading</span></a></p><p>I was recently asked about SSH, tunnels, and reconnecting disconnected sessions. Some people use
<a href="https://mosh.org/">mosh</a>, but I take a different approach, one that does not require any special
server software on the target server.</p>
<p>As with mosh, I recognize that sessions will become disconnected. As such, I run a
<a href="https://www.gnu.org/software/screen/">screen</a> session
on a server in the data center, and I initiate all SSH connections from that screen session. I use
screen instead of tmux, mostly because it is extremely simple to create a new screen tab/window from
the command line. I have created aliases to run a command in a new screen tab.</p>
<p>Here is a sample ~/.screenrc file:</p>
<div class="highlight"><pre><span></span><code>escape ^Xx
startup_message off
vbell off
backtick 0 10 10 dfh
backtick 1 10 10 s_uptime
hardstatus alwayslastline "%{.bG}%H%{-}%{.bW} %-w%{.rW}%f%n %t%{-}%+w %=%{..G}[ %l ]%{..Y} %`"
defscrollback 5000
termcapinfo xterm* ti@:te@
</code></pre></div>
<p>In this screenrc file, we note that I run 2 commands every 10 seconds, and that the output of those
commands shows up in my status line. <code>dfh</code> simply prints the output of <code>df -h</code> for a certain
filesystem, usually /, in a short format, followed by the current time. <code>s_uptime</code> shows load average
for the box.</p>
<p>Early on, I used an alias to create new screen windows/tabs. I have switched to using a shell script
because I found that when the process terminated (e.g., when the remote server disconnected), the window/tab
disappeared without any explanation as to what happened. My script catches that and keeps the tab open
until I acknowledge it.</p>
<p>In this script, the variable <code>$MAIN_SCREENS</code> holds the hostnames of all systems on which I want to run screen (this
allows this script to function correctly even on systems without screen support), and <code>$SHORT_HOSTNAME</code>
is the name of the local system.</p>
<p>Here is the script I use: (the first line is a shebang, but that messes up markdown)</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span>!/bin/sh
COMMAND="$*"
lines=`echo<span class="w"> </span><span class="nv">$MAIN_SCREENS</span><span class="w"> </span>|<span class="w"> </span>grep<span class="w"> </span>-c<span class="w"> </span><span class="nv">$SHORT_HOSTNAME</span>`
if<span class="w"> </span>[<span class="w"> </span><span class="nv">$lines</span><span class="w"> </span>-eq<span class="w"> </span>0<span class="w"> </span>];<span class="w"> </span>then
<span class="w"> </span>#<span class="w"> </span>This<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>one<span class="w"> </span>of<span class="w"> </span>the<span class="w"> </span>systems<span class="w"> </span>running<span class="w"> </span>screen,<span class="w"> </span>so<span class="w"> </span>simply<span class="w"> </span>run<span class="w"> </span>the<span class="w"> </span>command
<span class="w"> </span><span class="cp">${</span><span class="n">COMMAND</span><span class="cp">}</span>
else
<span class="w"> </span>#<span class="w"> </span>This<span class="w"> </span>system<span class="w"> </span>runs<span class="w"> </span>screen,<span class="w"> </span>so<span class="w"> </span>proceed<span class="w"> </span>as<span class="w"> </span>normal
FILENAME=`mktemp<span class="w"> </span>~/.screen_tab.XXXXXX`
cat<span class="w"> </span>><span class="cp">${</span><span class="n">FILENAME</span><span class="cp">}</span><span class="w"> </span><span class="err"><</span><span class="nt"><EOF</span>
<span class="err">#!/bin/bash</span>
<span class="err">function</span> <span class="err">clean_up</span> <span class="err">{</span>
<span class="err">#</span> <span class="err">Perform</span> <span class="err">program</span> <span class="err">exit</span> <span class="err">housekeeping</span>
<span class="err">echo</span> <span class="err">"Command</span> <span class="err">exited</span> <span class="err">with</span> <span class="err">exitcode</span> <span class="err">\${EXITCODE}"</span>
<span class="err">echo</span>
<span class="err">echo</span> <span class="err">"Press</span> <span class="err">ENTER</span> <span class="err">to</span> <span class="err">continue..."</span>
<span class="err">echo</span>
<span class="err">read</span>
<span class="err">rm</span> <span class="err">-f</span> <span class="err">${FILENAME}</span>
<span class="err">exit</span>
<span class="err">}</span>
<span class="err">trap</span> <span class="err">clean_up</span> <span class="err">SIGINT</span>
<span class="err">${COMMAND}</span>
<span class="na">EXITCODE=</span><span class="s">\$?</span>
<span class="err">if</span> <span class="err">[</span> <span class="err">\$EXITCODE</span> <span class="err">-gt</span> <span class="err">0</span> <span class="err">];</span> <span class="err">then</span>
<span class="err">clean_up</span>
<span class="err">fi</span>
<span class="err">rm</span> <span class="err">-f</span> <span class="err">${FILENAME}</span>
<span class="err">EOF</span>
<span class="err">chmod</span> <span class="err">755</span> <span class="err">${FILENAME}</span>
<span class="na">COMMAND=</span><span class="s">`echo</span> <span class="err">${COMMAND}</span> <span class="err">|</span> <span class="err">sed</span> <span class="err">'s/.unixdude.net//'`</span>
<span class="na">TERMTYPE=</span><span class="s">""</span>
<span class="err">if</span> <span class="err">[</span> <span class="err">${TERM}</span> <span class="err">=</span> <span class="err">"screen.xterm-256color"</span> <span class="err">];</span> <span class="err">then</span>
<span class="na">TERMTYPE=</span><span class="s">"-T xterm"</span>
<span class="err">fi</span>
<span class="err">screen</span> <span class="err">${TERMTYPE}</span> <span class="err">-t</span> <span class="err">"${COMMAND}"</span> <span class="err">"${FILENAME}"</span>
<span class="err">fi</span>
</code></pre></div>
<p>Since I always run screen on systems I consider to be jump hosts, I use aliases
to connect to those systems. Here is an example:</p>
<div class="highlight"><pre><span></span><code>alias cjb "tabname jump-rdu-01; ssh jump-rdu-01.rdu.domain.com -t 'screen -Rd'; tabname LOCAL_SHELL"
</code></pre></div>
<p><code>tabname</code> is a short script (it could be an alias) that changes the name of the window/tab in my
terminal program: (again, the first line is a shebang)</p>
<div class="highlight"><pre><span></span><code> !/bin/bash
printf "\e]1;%s\a" $1
</code></pre></div>
<p>A big trick to all of this is reconnecting after a disconnection. My solution here is to use a common,
persistent, known value for <code>${SSH_AUTH_SOCKET}</code>. To do that, I modify <code>SSH_AUTH_SOCKET</code> variable
on login. First, I create a symlink called <code>~/.ssh_auth_socket_${SHORT_HOSTNAME}</code>
to the actual <code>$SSH_AUTH_SOCKET</code>, and then I set <code>$SSH_AUTH_SOCKET</code> with a value of
<code>~/.ssh_auth_socket_${SHORT_HOSTNAME}</code>.</p>
<p>The value here is that <code>$SSH_AUTH_SOCKET</code> never changes between logins, so any existing shell sessions, and any newly
created ones, all reference the same <code>$SSH_AUTH_SOCKET</code>, and they always work.</p>
<div class="highlight"><pre><span></span><code><span class="k">set</span><span class="n">env</span><span class="w"> </span><span class="n">SHORT_HOSTNAME</span><span class="w"> </span><span class="n n-Quoted">`hostname`</span>
<span class="n">find</span><span class="w"> </span><span class="p">.</span><span class="w"> </span><span class="o">-</span><span class="n">maxdepth</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">-</span><span class="k">name</span><span class="w"> </span><span class="s1">'.ssh_auth_sock*'</span><span class="w"> </span><span class="o">-</span><span class="n">mtime</span><span class="w"> </span><span class="o">+</span><span class="mi">1</span><span class="w"> </span><span class="o">-</span><span class="n">exec</span><span class="w"> </span><span class="n">rm</span><span class="w"> </span><span class="o">-</span><span class="n">f</span><span class="w"> </span><span class="err">{}</span><span class="w"> </span><span class="err">\</span><span class="p">;</span>
<span class="n">find</span><span class="w"> </span><span class="p">.</span><span class="w"> </span><span class="o">-</span><span class="n">maxdepth</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">-</span><span class="k">name</span><span class="w"> </span><span class="s1">'.screen_tab*'</span><span class="w"> </span><span class="o">-</span><span class="n">mtime</span><span class="w"> </span><span class="o">+</span><span class="mi">1</span><span class="w"> </span><span class="o">-</span><span class="n">exec</span><span class="w"> </span><span class="n">rm</span><span class="w"> </span><span class="o">-</span><span class="n">f</span><span class="w"> </span><span class="err">{}</span><span class="w"> </span><span class="err">\</span><span class="p">;</span>
<span class="k">if</span><span class="w"> </span><span class="n">$</span><span class="nv">?</span><span class="n">SSH_AUTH_SOCK</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n n-Quoted">`echo $SSH_AUTH_SOCK | grep $SHORT_HOSTNAME | wc -l`</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="k">set</span><span class="n">env</span><span class="w"> </span><span class="n">NEW_SSH_AUTH_SOCK</span><span class="w"> </span><span class="o">~</span><span class="n">$</span><span class="err">{</span><span class="n">SUDO_USER</span><span class="err">}</span><span class="o">/</span><span class="p">.</span><span class="n">ssh_auth_sock_$</span><span class="err">{</span><span class="n">SHORT_HOSTNAME</span><span class="err">}</span>
<span class="w"> </span><span class="n">ln</span><span class="w"> </span><span class="o">-</span><span class="n">sf</span><span class="w"> </span><span class="n">$</span><span class="err">{</span><span class="n">SSH_AUTH_SOCK</span><span class="err">}</span><span class="w"> </span><span class="n">$</span><span class="err">{</span><span class="n">NEW_SSH_AUTH_SOCK</span><span class="err">}</span>
<span class="w"> </span><span class="k">set</span><span class="n">env</span><span class="w"> </span><span class="n">SSH_AUTH_SOCK</span><span class="w"> </span><span class="n">$NEW_SSH_AUTH_SOCK</span>
<span class="w"> </span><span class="n">endif</span>
<span class="n">endif</span>
</code></pre></div>
<p>This is tcsh code, but bash can do the same thing of course.</p>macOS Mail issue with postfix2019-05-14T00:00:00-04:002019-05-14T00:00:00-04:00Danieltag:www.unixdude.net,2019-05-14:/posts/2019/May/14/macos-mail-postfix/<p>I recently got a new MacBook Pro, and as I was setting up the mail configuration on that laptop,
I found that I was unable to send mail through my mail server. I use Apple Mail, and usually
it works great for me, but in this case it was not …<a class="label label-primary read-more" href="/posts/2019/May/14/macos-mail-postfix/"><span>Continue reading</span></a></p><p>I recently got a new MacBook Pro, and as I was setting up the mail configuration on that laptop,
I found that I was unable to send mail through my mail server. I use Apple Mail, and usually
it works great for me, but in this case it was not working with my Postfix server.</p>
<p>I made several attempts at fixing this issue, but I always got the same error:</p>
<div class="highlight"><pre><span></span><code><span class="k">postfix</span><span class="o">/</span><span class="n">submission</span><span class="o">/</span><span class="n">smtpd</span><span class="o">[</span><span class="n">70940</span><span class="o">]</span><span class="err">:</span><span class="w"> </span><span class="nl">NOQUEUE</span><span class="p">:</span><span class="w"> </span><span class="nl">reject</span><span class="p">:</span><span class="w"> </span><span class="n">RCPT</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">REDACTED</span><span class="o">[</span><span class="n">X.X.X.X</span><span class="o">]</span><span class="err">:</span><span class="w"> </span><span class="mi">554</span><span class="w"> </span><span class="mf">5.7.1</span><span class="w"> </span><span class="o"><</span><span class="n">recipient</span><span class="nv">@anywhere</span><span class="p">.</span><span class="n">com</span><span class="o">></span><span class="err">:</span><span class="w"> </span><span class="n">Recipient</span><span class="w"> </span><span class="n">address</span><span class="w"> </span><span class="nl">rejected</span><span class="p">:</span><span class="w"> </span><span class="n">Access</span><span class="w"> </span><span class="n">denied</span><span class="p">;</span><span class="w"> </span><span class="k">from</span><span class="o">=<</span><span class="n">sender</span><span class="nv">@example</span><span class="p">.</span><span class="n">com</span><span class="o">></span><span class="w"> </span><span class="k">to</span><span class="o">=<</span><span class="n">recipient</span><span class="nv">@anywhere</span><span class="p">.</span><span class="n">com</span><span class="o">></span><span class="w"> </span><span class="n">proto</span><span class="o">=</span><span class="n">ESMTP</span><span class="w"> </span><span class="n">helo</span><span class="o">=<[</span><span class="n">REDACTED</span><span class="o">]></span>
</code></pre></div>
<p>After exhausting everything I could think of, and after much troubleshooting, I determined
that all settings were correct: other systems could send mail through my Postfix server,
but I still had the problem with the new laptop.</p>
<p>At one point I had the idea to disable Apple Mail's SMTP server checkbox that sets the
application to "Automatically manage connection settings," so I did that and I confirmed the
settings that it had been automatically managing -- the port and TLS/SSL setting (587/SSL)
and authentication configuration.</p>
<p>Once I disabled the automatic option, everything worked correctly. I didn't actually change any
configuration, and all of the settings were correct, it just doesn't work automatically.</p>
<p>I post this that others might find it useful, and that I might find it when I set up my
next system.</p>Scanner2019-02-20T00:00:00-05:002019-02-20T00:00:00-05:00Danieltag:www.unixdude.net,2019-02-20:/posts/2019/Feb/20/scanner/<p>I own a CanoScan LiDE 30. This scanner is about 15 years old, and back
when I got it new, it was supported on Mac OS X. Canon stopped updating
the drivers a couple years later, so I switched to <a href="http://www.ellert.se/twain-sane/">TWAIN-SANE</a>.</p>
<p>When I did that, I wrote a "scan" shell …<a class="label label-primary read-more" href="/posts/2019/Feb/20/scanner/"><span>Continue reading</span></a></p><p>I own a CanoScan LiDE 30. This scanner is about 15 years old, and back
when I got it new, it was supported on Mac OS X. Canon stopped updating
the drivers a couple years later, so I switched to <a href="http://www.ellert.se/twain-sane/">TWAIN-SANE</a>.</p>
<p>When I did that, I wrote a "scan" shell script that uses scanimage to
grab a TIFF from the scanner, and then mogrify (part of ImageMagick) to
convert the TIFF to JPEG. My scan script saves scans to an NFS automounted
directory, so it doesn't matter what computer I scan with, I just happened
to do it on my iMac. Or my MacBook Pro - it didn't matter.</p>
<p>Unfortunately, Mattias stopped updating his drivers a few years ago, so
I wanted to find another way to use my perfectly good LiDE 30.</p>
<p>I own a Raspberry Pi 3, and I knew that the TWAIN-SANE drivers that Mattias
used were simply ports of the open source Linux counterparts, so I installed
the SANE and ImageMagick packages on my Raspi and used that for scanning. I
used the same scan script that I had on the Mac, and with the NFS
automount, everything worked as it had before.</p>
<p>Until this week, that is, when I relocated my Raspi to my living room to connect it
to my TV.</p>
<p>Once again, I was looking for a scanning solution. I can't use my Macs (the only
computers in my office). My R610 is in my garage, which is not convenient to
scanning. I don't want to carry my scanner to the living room to connect to the
Raspi, so what can I do?</p>
<p>The answer is easy: I run a single FreeBSD VM on my Synology DS1618+. A quick search, and I
discovered sane-backends and sane-fronends are ported to FreeBSD. The question
remained: Can my Synology connect a USB page scanner to one of its VMs, and will
that work properly? In mere moments, I had the SANE packages and ImageMagick
installed on the Synology-hosted FreeBSD VM. And, moments later, I was scanning
successfully using exactly the same script I started with on the iMac a decade ago.</p>
<p>Ah, the magic of open source and NFS automounts.</p>
<p>The FreeBSD port has some problems, but it's fine as long as I don't run "scanimage -L" --
that crashes the VM, either a full lockup or a kernel panic and reboot. But, since
my scanner works with scanimage, that's all I care about. I just avoid the "-L" parameter,
and all is well.</p>Postfix relay configuration2019-02-14T00:00:00-05:002019-02-14T00:00:00-05:00Danieltag:www.unixdude.net,2019-02-14:/posts/2019/Feb/14/postfix_relay/<p>Like most people in my industry, I use multiple email addresses at multiple providers.
Used to be, I could configure all of my email addresses in my mail client and just send
through my mail server and -- voila -- my email would go out as whatever address I
wanted to use …<a class="label label-primary read-more" href="/posts/2019/Feb/14/postfix_relay/"><span>Continue reading</span></a></p><p>Like most people in my industry, I use multiple email addresses at multiple providers.
Used to be, I could configure all of my email addresses in my mail client and just send
through my mail server and -- voila -- my email would go out as whatever address I
wanted to use.</p>
<p>Then along came Sender Policy Framework (SPF). SPF is a great email-related technology
that is designed to protect the integrity of sending domains. Unfortunately for me,
adoption of SPF meant that I could no longer send mail through my mail server using one
of my other email addresses. Specifically, I could no longer send mail through my
server as my gmail.com or mac.com addresses. I couldn't complain, because my server is
an origination point for gmail.com or mac.com.</p>
<p>Fortunately, there's a fix for this: <a href="http://www.postfix.org/postconf.5.html#sender_dependent_relayhost_maps" title="Postfix's sender_dependent_relayhost_maps feature">Postfix's "sender_dependent_relayhost_maps" feature</a>.</p>
<p>The idea: configure all of my devices to send mail through my mail server, and to have
the mail server itself relay as necessary depending on sending address. My goal: send
mail directly if it originates from my domain; relay gmail mail through smtp.gmail.com,
and relay iCloud mail through smtp.mail.me.com.</p>
<p>To use that, I defined a few settings in my postfix main.cf file:</p>
<div class="highlight"><pre><span></span><code>smtp_sender_dependent_authentication = yes
sender_dependent_relayhost_maps = hash:/usr/local/etc/postfix/sender_relay
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/usr/local/etc/postfix/sasl_passwd
smtp_tls_note_starttls_offer = yes
smtp_tls_policy_maps = hash:/usr/local/etc/postfix/tls_policy
smtp_tls_security_level = encrypt
smtp_use_tls = yes
smtp_enforce_tls = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_mechanism_filter = plain
smtp_sasl_tls_security_options = noanonymous
</code></pre></div>
<p>For my server, smtp_sender_dependent_authentication is not strictly necessary, because
I am the only one sending through my mail server. I include it here because it is
needed for a more general solution.</p>
<p>The real magic occurs as a result of sender_dependent_relayhost_maps. This setting
configures the different relays based on sending address. My sender_relay file looks like this:</p>
<div class="highlight"><pre><span></span><code><span class="nx">address</span><span class="err">@</span><span class="nx">gmail</span><span class="p">.</span><span class="nx">com</span><span class="w"> </span><span class="p">[</span><span class="nx">smtp</span><span class="p">.</span><span class="nx">gmail</span><span class="p">.</span><span class="nx">com</span><span class="p">]</span>
<span class="nx">address</span><span class="err">@</span><span class="nx">mac</span><span class="p">.</span><span class="nx">com</span><span class="w"> </span><span class="p">[</span><span class="nx">smtp</span><span class="p">.</span><span class="nx">mail</span><span class="p">.</span><span class="nx">me</span><span class="p">.</span><span class="nx">com</span><span class="p">]:</span><span class="nx">submission</span>
</code></pre></div>
<p>The smtp_sasl_password_maps configuration is also needed because both
Gmail and iCloud require authentication before an email can be relayed through their
servers. To do this, I have configured application-specific passwords in both systems,
and the sasl_passwd file looks like this:</p>
<div class="highlight"><pre><span></span><code><span class="nx">address</span><span class="err">@</span><span class="nx">gmail</span><span class="p">.</span><span class="nx">com</span><span class="w"> </span><span class="nx">address</span><span class="err">@</span><span class="nx">gmail</span><span class="p">.</span><span class="nx">com</span><span class="p">:</span><span class="nx">PASSWORD</span>
<span class="nx">address</span><span class="err">@</span><span class="nx">mac</span><span class="p">.</span><span class="nx">com</span><span class="w"> </span><span class="nx">address</span><span class="err">@</span><span class="nx">mac</span><span class="p">.</span><span class="nx">com</span><span class="p">:</span><span class="nx">PASSWORD</span>
</code></pre></div>
<p>The TLS policy maps are required because the relays require encryption; the tls_policy file's
contents are:</p>
<div class="highlight"><pre><span></span><code>[smtp.gmail.com]:587 encrypt
[smtp.mail.me.com]:587 encrypt
</code></pre></div>
<p>Of course, you must run postmap on each of these files prior to using them.</p>
<p>On FreeBSD, which is my OS of choice, things were slightly more complicated than they
otherwise might have been. I was not able to use the default postfix package, because it is
not compiled with Cyrus SASL client support. A quick <code>make configure</code> in
<code>/usr/ports/mail/postfix</code> allowed me to enable Cyrus SASL client support. After a
<code>make</code> and <code>make install</code> I was ready to test. Many thanks to <a href="https://blog.bradlab.tech/">Brad</a> for
helping me test.</p>
<p>Now I can eliminate all of the extra accounts on all of my devices.</p>Network upgrade2018-12-28T00:00:00-05:002018-12-28T00:00:00-05:00Danieltag:www.unixdude.net,2018-12-28:/posts/2018/Dec/28/network_update/<p>I have a 3-story house, and my office is on the third floor. Wifi connectivity in the bonus
room over the garage was terrible, so I brainstormed ideas with coworkers and friends about
how to fix this. As a temporary measure I ran a long ethernet cable from the office …<a class="label label-primary read-more" href="/posts/2018/Dec/28/network_update/"><span>Continue reading</span></a></p><p>I have a 3-story house, and my office is on the third floor. Wifi connectivity in the bonus
room over the garage was terrible, so I brainstormed ideas with coworkers and friends about
how to fix this. As a temporary measure I ran a long ethernet cable from the office to the
bonus room, but this had a very low WAF (wife acceptance factor).</p>
<p>Another idea was to try a wifi extender off of my AT&T fiber Internet router. This has
higher WAF, but it didn't really seem to help, so I returned it.</p>
<p>A coworker and I came up with a neat idea: I have a conduit running between the 3rd-floor attic
space and the garage. What about running a long ethernet cable through that conduit, then
moving my router down there? Great idea, but how do I get the Internet signal to the router?</p>
<p>Another coworker had the solution for that: put a managed switch at both ends of the conduit.
He just happened to have two spare HP ProCurve 1810-24G switches, so I ran an ethernet cable
through the conduit, and voila.</p>
<p>More specifically: I created a VLAN on the switches for the connection between the Fiber/Ethernet
transceiver to the router. Then I trunked that VLAN over the conduit-cable. I also trunked my
"Internal" VLAN over the same cable to get it back up to the office.</p>
<p>As a bonus, I got to move my noisy R610 to the garage, making my office much quieter. Storage
is still in the office, but the server is now remote. It's nice to have iDRAC, now that my server
is remote to me.</p>
<p>Now I have excellent wifi service on the first floor and bonus room. So, what about the office
and other parts of the second floor? I installed my Synology RT1900ac as a wifi access point
in the office; it is now my office switch, and it serves wifi to the parts of the house that
the AT&T router does not reach.</p>testssl.sh2018-07-02T00:00:00-04:002018-07-02T00:00:00-04:00Danieltag:www.unixdude.net,2018-07-02:/posts/2018/Jul/02/testssl_sh/<p>Last week, I needed to make some changes to my employer's production website, but
before I made those changes in production, I wanted to test them. Unfortunately
for me, our dev server is unreachable by Qualys, so I had to come up with another
way to test those changes.</p>
<p>Enter …<a class="label label-primary read-more" href="/posts/2018/Jul/02/testssl_sh/"><span>Continue reading</span></a></p><p>Last week, I needed to make some changes to my employer's production website, but
before I made those changes in production, I wanted to test them. Unfortunately
for me, our dev server is unreachable by Qualys, so I had to come up with another
way to test those changes.</p>
<p>Enter <a href="http://www.testssl.sh/">testssl.sh</a>. testssl.sh is a shell script that
can be used to do testing very simliar to what Qualys does, from a Unix system.</p>
<p>To get started, clone the testssl.sh repo:</p>
<p><code>git clone https://github.com/drwetter/testssl.sh.git</code></p>
<p>I was able to make and test the changes I needed on our dev system, before going
to production. I was able to confirm that my changes were correct, in a non-production environment.</p>
<p>Many thanks to Dirk Wetter for this excellent tool.</p>
<p>Check it out.</p>Lab update2018-06-28T00:00:00-04:002018-06-28T00:00:00-04:00Danieltag:www.unixdude.net,2018-06-28:/posts/2018/Jun/28/lab_update/<p>As mentioned in <a href="/posts/2017/Dec/03/homelab/">an earlier post</a>, my ESXi server has been
running in a VMware Fusion VM on my iMac. My iMac has 24 GB RAM and 4 logical CPUs; I
dediated 10 GB RAM and 2 vCPUs to the ESXi VM.</p>
<p>Last week, I upgraded to a Dell R610 …<a class="label label-primary read-more" href="/posts/2018/Jun/28/lab_update/"><span>Continue reading</span></a></p><p>As mentioned in <a href="/posts/2017/Dec/03/homelab/">an earlier post</a>, my ESXi server has been
running in a VMware Fusion VM on my iMac. My iMac has 24 GB RAM and 4 logical CPUs; I
dediated 10 GB RAM and 2 vCPUs to the ESXi VM.</p>
<p>Last week, I upgraded to a Dell R610 with 96GB RAM and dual Xeon 3 GHz CPUs (16 vCPUs).
Although ESXi 6.7 is unsupported on the R610, I installed it and so far it is working great.
I am really enjoying the breathing room that so many more CPUs and so much more RAM gives me.
Now I can create VMs pretty much as big as I would ever want.</p>
<p>I also made two changes to my PXE configuration.</p>
<p>First, I added a default boot option to the PXE config. Now, it automatically
starts a CentOS 7 install after 10 seconds, complete with a kickstart file that adds my user and
installs my SSH keys. Mere minutes after I boot a new VM, it is up and running
CentOS 7 and I can SSH into it.</p>
<p>Second, I updated my PXE environment to add <a href="http://www.netboot.xyz/">netboot.xyz</a>.
Previously I had the ability to install a half-dozen or so OSes via PXE. Now I can install many more
OSes just as easily.</p>
<p>My next update will be to add a managed switch, so that I can use link aggregation
for my DS415+ and R610.</p>I love SSH tunnels2018-03-15T00:00:00-04:002018-03-15T00:00:00-04:00Danieltag:www.unixdude.net,2018-03-15:/posts/2018/Mar/15/ssh_tunnels/<p>Everyone who knows me well knows that tunnels in SSH represent one of my all-time-favorite features, ever. Why? Simple: They
are so immensely useful.</p>
<p>Say you are debugging an issue on server, as I am today, and you need a mail server running on that server. But, that server is …<a class="label label-primary read-more" href="/posts/2018/Mar/15/ssh_tunnels/"><span>Continue reading</span></a></p><p>Everyone who knows me well knows that tunnels in SSH represent one of my all-time-favorite features, ever. Why? Simple: They
are so immensely useful.</p>
<p>Say you are debugging an issue on server, as I am today, and you need a mail server running on that server. But, that server is a
production system, and you cannot install a mail server on it. Want a quick "mail server"? Use an SSH tunnel:</p>
<p><code>sudo ssh -L 25:mail.domain.com:25 daniel@localhost</code></p>
<p>This will ssh back to localhost as user daniel, but will set up a listener on port 25 that points to mail.domain.com, port 25.</p>
<p>Voila... a temporary "mail server" on your system, torn down when you no longer need it.</p>
<p>You can also use tunnels for many other things. I also use SSH to tunnel VNC, RDP, Synergy, Video Station from my Synology -- and I even use it to access my ESXi console.</p>
<p>SSH tunnels are easy to implement in an SSH config file:</p>
<div class="highlight"><pre><span></span><code>host demo
hostname demo.example.com
user daniel
ForwardAgent yes
LocalForward 5022 192.168.0.2:22
LocalForward 5947 192.168.0.47:5900
LocalForward 24800 192.168.0.2:24800
</code></pre></div>Home network/home lab2017-12-03T00:00:00-05:002017-12-03T00:00:00-05:00Danieltag:www.unixdude.net,2017-12-03:/posts/2017/Dec/03/homelab/<p>I am frequently asked about my home network/home lab setup. The summary is that I currently
use a Synology RT1900ac router, and two Synology NAS devices: a DS415+ as primary storage,
and a DS214se as backup.</p>
<p>For a number of years, I had a first-generation Drobo; later I added …<a class="label label-primary read-more" href="/posts/2017/Dec/03/homelab/"><span>Continue reading</span></a></p><p>I am frequently asked about my home network/home lab setup. The summary is that I currently
use a Synology RT1900ac router, and two Synology NAS devices: a DS415+ as primary storage,
and a DS214se as backup.</p>
<p>For a number of years, I had a first-generation Drobo; later I added a
second generation Drobo S. I liked those, but I wanted something
beefier and I wanted to switch from DAS to NAS. I researched the
available options, and since I am a ZFS fan, I considered rolling my own server running FreeNAS.
I settled on Synology, in large part because of the user interface.</p>
<p>I sold the Drobos and bought a Synology DS415+ NAS, and today I use that
for my main storage. My DS415+ is configured with 3x WD Red 3TB and 1x WD Red 4TB, in SHR;
this gives about 9TB of usable space. I have a dozen or so NFS exports from the DS415+, and
three iSCSI LUNs.</p>
<p>All of my systems, both physical and virtual, are configured for the same user ID, and all Unix
systems use the NFS automounter. My iMac also mounts the iSCSI LUNs, for things like
iMovie and iPhoto that require block-level storage.</p>
<p>Due to the size of the backup need, I chose a Synology as a backup device. Since this is only for
backup, I chose a low-end DS214se; that unit is loaded with 2x WD Red 4TB configured as an 8TB JBOD.</p>
<p>When I first bought the DS415+, I moved data from my iMac's hard drive over to the NAS, but
that wasn't really fully utilizing it. I later learned two things that completely changed my world:</p>
<ul>
<li>First, ESXi can be virtualized inside of VMware Fusion. This was exciting because I did not have
money or space for a virtualization lab -- but I did have an iMac with 32GB RAM in it. I prefer
ESXi to Fusion for virtualization, but to that point had not been running ESXi at home, and was at
that time using Fusion for all of my virtualization needs.</li>
<li>Second, ESXi can use NFS datastores. Fusion requires block-level storage, so I was excited to learn
that ESXi can use NFS storage. (At this point, I was very new to ESXi administration.)</li>
</ul>
<p>Ever since learning these two things about ESXi, I have run an ESXi server at home. I started running
ESXi inside a VMware Fusion VM, and today that VM is running ESXi 6.5.0 Update 1. Eventually I would
like to migrate this to an Intel NUC, but for now it runs as a VM in VMware Fusion. ESXi sees 10GB of RAM
and 2 out of my iMac's 4 CPUs. I connect to my ESXi server two ways: either via Fusion Pro, or via
ESXi's built-in web client.</p>
<p>Back to the Synology: The Synology NAS can act as a PXE server, so I have configured pxelinux with a
menu that can boot several Linux distros, ESXi, and other things. When I want to install an operating
system into a VM, I either use PXE, or I attach an ISO directly to the VM.</p>
<p>Nearly all of my virtualization has now been moved to ESXi. I currently have more than 20 unique OSes
installed in VMs in ESX, mostly BSD variants or Linux distros. In VMware Fusion, I run NeXTSTEP
3.3 and OPENSTEP 4.2; these do not seem to run in ESXi, and are the only OSes I run in Fusion anymore.
Well, other than ESXi itself, that is...</p>
<p>Once I do buy real hardware for it, the migration will be easy: I will install ESXi on the new
system, point it to the existing NFS exports, add the VMs, and start them up.</p>How to build a Pelican contact form2017-11-29T00:00:00-05:002017-11-29T00:00:00-05:00Danieltag:www.unixdude.net,2017-11-29:/posts/2017/Nov/29/pelican-contact-form/<p>One of the first things I wanted to configure on this site was a contact form. I searched and found that <a href="https://iainhouston.com/blog/make-contact-form.html" target="_blank">Iain Houston documented how he configured one</a>. I took his ideas and implemented my own slightly different version.</p>
<p>Where he used a separate template, I chose to use the …<a class="label label-primary read-more" href="/posts/2017/Nov/29/pelican-contact-form/"><span>Continue reading</span></a></p><p>One of the first things I wanted to configure on this site was a contact form. I searched and found that <a href="https://iainhouston.com/blog/make-contact-form.html" target="_blank">Iain Houston documented how he configured one</a>. I took his ideas and implemented my own slightly different version.</p>
<p>Where he used a separate template, I chose to use the base template and create a Pelican page for my contact form.</p>
<p>The contents of my <code>content/pages/contact.md</code> file are shown here. This results in a page being created at <code>/pages/contact/index.html</code>, which I then added to MENUITEMS.</p>
<div class="highlight"><pre><span></span><code>Title:<span class="w"> </span>Contact<span class="w"> </span>me
Slug:<span class="w"> </span>contact
<span class="nt"><div</span><span class="w"> </span><span class="na">class=</span><span class="s">"col-md-12"</span><span class="nt">></span>
<span class="w"> </span><span class="nt"><header</span><span class="w"> </span><span class="na">class=</span><span class="s">jumbotron</span><span class="nt">></span>
<span class="w"> </span><span class="nt"><h1></span>Contact<span class="w"> </span>me<span class="nt"></h1></span>
<span class="w"> </span><span class="nt"><p></span>I'd<span class="w"> </span>love<span class="w"> </span>to<span class="w"> </span>hear<span class="w"> </span>from<span class="w"> </span>you.<span class="nt"></p></span>
<span class="w"> </span><span class="nt"></header></span>
<span class="w"> </span><span class="nt"><form</span><span class="w"> </span><span class="na">method=</span><span class="s">"post"</span><span class="w"> </span><span class="na">action=</span><span class="s">"/php/submit.php"</span><span class="nt">></span>
<span class="w"> </span><span class="nt"><div</span><span class="w"> </span><span class="na">class=</span><span class="s">"form-group col-md-6"</span><span class="nt">></span>
<span class="w"> </span><span class="nt"><input</span><span class="w"> </span><span class="na">type=</span><span class="s">"text"</span><span class="w"> </span><span class="na">class=</span><span class="s">"form-control"</span><span class="w"> </span><span class="na">id=</span><span class="s">"name"</span><span class="w"> </span><span class="na">name=</span><span class="s">"name"</span><span class="w"> </span><span class="na">placeholder=</span><span class="s">"Name"</span><span class="w"> </span><span class="err">required</span><span class="nt">></span>
<span class="w"> </span><span class="nt"></div></span>
<span class="w"> </span><span class="nt"><div</span><span class="w"> </span><span class="na">class=</span><span class="s">"form-group col-md-6"</span><span class="nt">></span>
<span class="w"> </span><span class="nt"><input</span><span class="w"> </span><span class="na">type=</span><span class="s">"email"</span><span class="w"> </span><span class="na">class=</span><span class="s">"form-control validate email"</span><span class="w"> </span><span class="na">id=</span><span class="s">"email"</span><span class="w"> </span><span class="na">name=</span><span class="s">"email"</span><span class="w"> </span><span class="na">placeholder=</span><span class="s">"Enter email"</span><span class="w"> </span><span class="err">required</span><span class="nt">></span>
<span class="w"> </span><span class="nt"></div></span>
<span class="w"> </span><span class="nt"><p</span><span class="w"> </span><span class="na">class=</span><span class="s">"antispam"</span><span class="nt">></span>
<span class="w"> </span>Leave<span class="w"> </span>this<span class="w"> </span>empty:
<span class="w"> </span><span class="nt"><br</span><span class="w"> </span><span class="nt">/></span>
<span class="w"> </span><span class="nt"><input</span><span class="w"> </span><span class="na">name=</span><span class="s">"url"</span><span class="w"> </span><span class="nt">/></span>
<span class="w"> </span><span class="nt"></p></span>
<span class="w"> </span><span class="nt"><div</span><span class="w"> </span><span class="na">class=</span><span class="s">"form-group col-md-12"</span><span class="nt">></span>
<span class="w"> </span><span class="nt"><textarea</span><span class="w"> </span><span class="na">class=</span><span class="s">"form-control"</span><span class="w"> </span><span class="na">rows=</span><span class="s">"5"</span><span class="w"> </span><span class="na">name=</span><span class="s">"message"</span><span class="w"> </span><span class="na">placeholder=</span><span class="s">"Message"</span><span class="w"> </span><span class="err">required</span><span class="nt">></textarea></span>
<span class="w"> </span><span class="nt"></div></span>
<span class="w"> </span><span class="nt"><div</span><span class="w"> </span><span class="na">class=</span><span class="s">"form-group col-md-3 offset-md-3"</span><span class="nt">></span>
<span class="w"> </span><span class="nt"><button</span><span class="w"> </span><span class="na">type=</span><span class="s">"submit"</span><span class="w"> </span><span class="na">class=</span><span class="s">"btn btn-primary"</span><span class="nt">></span>Submit<span class="nt"></button></span>
<span class="w"> </span><span class="nt"></div></span>
<span class="w"> </span><span class="nt"></form></span>
<span class="nt"></div></span>
</code></pre></div>
<p>The <code>content/php/submit.php</code> file is similarly simple:</p>
<div class="highlight"><pre><span></span><code><span class="o"><</span><span class="vm">?</span><span class="n">php</span>
<span class="k">if</span><span class="p">(</span><span class="n">isset</span><span class="p">(</span><span class="err">$</span><span class="n">_POST</span><span class="o">[</span><span class="n">'url'</span><span class="o">]</span><span class="p">)</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="err">$</span><span class="n">_POST</span><span class="o">[</span><span class="n">'url'</span><span class="o">]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s1">''</span><span class="p">)</span><span class="err">{</span>
<span class="w"> </span><span class="o">//</span><span class="n">The</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">was</span><span class="w"> </span><span class="n">submitted</span>
<span class="w"> </span><span class="err">$</span><span class="n">ouremail</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'your@email.com'</span><span class="p">;</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="nl">Important</span><span class="p">:</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="k">add</span><span class="w"> </span><span class="ow">any</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">fields</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">HTML</span><span class="p">,</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">want</span><span class="w"> </span><span class="n">them</span><span class="w"> </span><span class="n">included</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">email</span><span class="p">,</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="n">need</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="k">add</span><span class="w"> </span><span class="n">them</span><span class="w"> </span><span class="n">here</span><span class="w"> </span><span class="n">also</span>
<span class="w"> </span><span class="err">$</span><span class="n">body</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ss">"This is the form that was just submitted:</span>
<span class="ss"> Name: $_POST[name]</span>
<span class="ss"> E-Mail: $_POST[email]</span>
<span class="ss"> Message: $_POST[message]"</span><span class="p">;</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="k">From</span><span class="err">:</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="k">Use</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">submitters</span><span class="w"> </span><span class="n">email</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">they</span><span class="w"> </span><span class="n">supplied</span><span class="w"> </span><span class="n">one</span>
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="p">(</span><span class="ow">and</span><span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="n">isn</span><span class="s1">'t trying to hack our form).</span>
<span class="s1"> // Otherwise send from our email address.</span>
<span class="s1"> if( $_POST['</span><span class="n">email</span><span class="s1">'] && !preg_match( "/[\r\n]/", $_POST['</span><span class="n">email</span><span class="s1">']) ) {</span>
<span class="s1"> $headers = "From: $_POST[email]";</span>
<span class="s1"> } else {</span>
<span class="s1"> $headers = "From: $ouremail";</span>
<span class="s1"> }</span>
<span class="s1"> // finally, send the message</span>
<span class="s1"> mail($ouremail, '</span><span class="n">Contact</span><span class="w"> </span><span class="n">Form</span><span class="w"> </span><span class="n">submitted</span><span class="s1">', $body, $headers );</span>
<span class="s1"> header('</span><span class="nl">Location</span><span class="p">:</span><span class="w"> </span><span class="o">/</span><span class="n">pages</span><span class="o">/</span><span class="n">thank</span><span class="o">-</span><span class="n">you</span><span class="o">/</span><span class="err">'</span><span class="p">);</span>
<span class="err">}</span>
<span class="vm">?</span><span class="o">></span>
</code></pre></div>
<p>You will also need a thank-you page. Here's mine, saved as <code>content/pages/thankyou.md</code>:</p>
<div class="highlight"><pre><span></span><code><span class="n">Title</span><span class="o">:</span><span class="w"> </span><span class="n">Thank</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">contacting</span><span class="w"> </span><span class="n">me</span>
<span class="n">slug</span><span class="o">:</span><span class="w"> </span><span class="n">thank</span><span class="o">-</span><span class="n">you</span>
<span class="n">Your</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="n">has</span><span class="w"> </span><span class="n">been</span><span class="w"> </span><span class="n">sent</span><span class="o">.</span>
</code></pre></div>
<p>Iain's script implements simple spam control. To use it properly, you need to add some custom CSS so that the antispam field is hidden from view:</p>
<div class="highlight"><pre><span></span><code><span class="na">.antispam</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="no">display</span><span class="p">:</span><span class="no">none</span><span class="c1">;}</span>
</code></pre></div>Sharing Unix shell config between systems2017-11-11T00:00:00-05:002017-11-11T00:00:00-05:00Danieltag:www.unixdude.net,2017-11-11:/posts/2017/Nov/11/sharing-unix-shell-config-between-systems/<p>Like most people who work on Unix, I have shell accounts on hundreds of systems. Very few
of these systems share a home directory (e.g., via NFS), so I need another way to maintain
a common shell configuration between systems.</p>
<p>Based on the design of a former coworker, I …<a class="label label-primary read-more" href="/posts/2017/Nov/11/sharing-unix-shell-config-between-systems/"><span>Continue reading</span></a></p><p>Like most people who work on Unix, I have shell accounts on hundreds of systems. Very few
of these systems share a home directory (e.g., via NFS), so I need another way to maintain
a common shell configuration between systems.</p>
<p>Based on the design of a former coworker, I have built such a system using git, symlinks, and
scripts.</p>
<p>I use four git repos for my Unix account configuration:</p>
<ul>
<li>a "bin" directory for home</li>
<li>a "bin" directory for work</li>
<li>a "bin" directory for everywhere</li>
<li>an environment directory</li>
</ul>
<p>All of those directories are private git repos, stored on Bitbucket. I will
eventually host my own git repo, but for now I use Atlassian's excellent
service.</p>
<p>Of the four repos listed above, the "home" bin directory exists only on personal systems,
the "work" directory exists only on employer systems, and the "everywhere" directory is
shared on all systems.</p>
<p>The environment directory includes such things as:</p>
<ul>
<li>tcsh config</li>
<li>bash config (for systems on which tcsh is not available)</li>
<li>ssh config</li>
<li>screenrc</li>
<li>vimrc</li>
<li>a script to configure my environment on a new host</li>
</ul>
<p>I use three additional scripts in conjunction with the repos:</p>
<ul>
<li>one to inform me when a sync is required</li>
<li>one to perform the git sync</li>
<li>one to copy an existing config to a new host (such as one that does not have git installed)</li>
</ul>
<p>Obviously I can run "git push" and "git pull" manually, but since there are four
repos here, it was easier to script it.</p>
<p>My "update_repos.sh" script looks like this:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/sh</span>
<span class="nb">echo</span><span class="w"> </span><span class="s2">"Update my_env"</span>
<span class="nb">cd</span><span class="w"> </span>~/.my_env
git<span class="w"> </span>pull
git<span class="w"> </span>push
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-d<span class="w"> </span>~/bina<span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="nb">echo</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Update bina"</span>
<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>~/bina
<span class="w"> </span>git<span class="w"> </span>pull
<span class="w"> </span>git<span class="w"> </span>push
<span class="k">fi</span>
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-d<span class="w"> </span>~/binh<span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="nb">echo</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Update binh"</span>
<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>~/binh
<span class="w"> </span>git<span class="w"> </span>pull
<span class="w"> </span>git<span class="w"> </span>push
<span class="k">fi</span>
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-d<span class="w"> </span>~/binw<span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="nb">echo</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Update binw"</span>
<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>~/binw
<span class="w"> </span>git<span class="w"> </span>pull
<span class="w"> </span>git<span class="w"> </span>push
<span class="k">fi</span>
</code></pre></div>
<p>I use symlinks from main files, into my environment directory:</p>
<div class="highlight"><pre><span></span><code>.bash_profile@<span class="w"> </span>-><span class="w"> </span>.my_env/tcsh/bash_profile
.bashrc@<span class="w"> </span>-><span class="w"> </span>.my_env/tcsh/bashrc
.cshrc@<span class="w"> </span>-><span class="w"> </span>.my_env/tcsh/cshrc
.login@<span class="w"> </span>-><span class="w"> </span>.my_env/tcsh/login
.logout@<span class="w"> </span>-><span class="w"> </span>.my_env/tcsh/logout
.ssh@<span class="w"> </span>-><span class="w"> </span>.my_env/ssh
.vimrc@<span class="w"> </span>-><span class="w"> </span>.my_env/vimrc
</code></pre></div>
<p>To bootstrap a host, I check out the applicable repos on the host, then run my setup script, which looks like this:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/sh</span>
ln<span class="w"> </span>-sf<span class="w"> </span>.my_env/tcsh/bashrc<span class="w"> </span>~/.bashrc
ln<span class="w"> </span>-sf<span class="w"> </span>.my_env/tcsh/cshrc<span class="w"> </span>~/.cshrc
ln<span class="w"> </span>-sf<span class="w"> </span>.my_env/tcsh/login<span class="w"> </span>~/.login
ln<span class="w"> </span>-sf<span class="w"> </span>.my_env/tcsh/logout<span class="w"> </span>~/.logout
ln<span class="w"> </span>-sf<span class="w"> </span>.my_env/vimrc<span class="w"> </span>~/.vimrc
ln<span class="w"> </span>-sf<span class="w"> </span>.my_env/screenrc<span class="w"> </span>~/.screenrc
rm<span class="w"> </span>~/.ssh
ln<span class="w"> </span>-s<span class="w"> </span>.my_env/ssh<span class="w"> </span>~/.ssh
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-e<span class="w"> </span><span class="s1">'/auto/downloads/Set up new host/ssh_config/id_rsa'</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span>cp<span class="w"> </span><span class="s1">'/auto/downloads/Set up new host/ssh_config/id_rsa'</span><span class="w"> </span>~/.ssh
<span class="k">fi</span>
</code></pre></div>
<p>Two comments about this script:</p>
<ol>
<li>~/.ssh is not removed if it is a directory. This does complicate matters on systems that already have a ~/.ssh
directory. In those cases I blow away ~/.ssh and rebuild it as a link.</li>
<li>/auto is where my NFS automounter lives. /auto/downloads is on my NAS and in keeping with best-practices
my private key only exists on appropriate systems.</li>
</ol>
<p>Comment below if you have any questions or comments about this setup.</p>Welcome2017-10-26T10:21:00-04:002017-10-26T10:21:00-04:00Danieltag:www.unixdude.net,2017-10-26:/posts/2017/Oct/26/welcome/<p>A while ago, a friend suggested that I should start a blog. Around that
same time, a coworker introduced me to DigitalOcean. I quickly set up a
VM at DigitalOcean (that VM is serving this web page), and installed
FreeBSD 11.</p>
<p>I looked at WordPress and other popular blogging tools …<a class="label label-primary read-more" href="/posts/2017/Oct/26/welcome/"><span>Continue reading</span></a></p><p>A while ago, a friend suggested that I should start a blog. Around that
same time, a coworker introduced me to DigitalOcean. I quickly set up a
VM at DigitalOcean (that VM is serving this web page), and installed
FreeBSD 11.</p>
<p>I looked at WordPress and other popular blogging tools, but all were
overkill for what I want to do, and all would overtax the small VM I am
running. I finally settled on Pelican, after a coworker showed me <a href="http://blog.bradlab.tech/" target="_blank">his blog</a>.</p>
<p>I played around with all of the Pelican themes, and wanted one that
would look good on all screen sizes, so it had to be a
<a href="http://www.getbootstrap.com" target="_blank">Bootstrap</a> derivative.</p>
<p>I use a second FreeBSD system for the development version of this blog.
That system is a VM running on my ESXi server. I develop and deploy
there, then I deploy the production version here via rsync.</p>
<p>I won't get into the details of everything this VM does,
but it functions as a:</p>
<ul>
<li>Mail server, running Postfix + Dovecot + SpamAssassin + Sieve + Roundcube</li>
<li>Web server, running Apache serving a dozen or so virtual hosts, all of which use <a href="https://www.letsencrypt.org/" target="_blank">Let's Encrypt certificates</a></li>
</ul>