<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ishuah Kariuki</title>
    <description>The latest articles on DEV Community by Ishuah Kariuki (@ishuah).</description>
    <link>https://dev.to/ishuah</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F98573%2F6cce9500-8ab8-4cd4-b0c4-744cfa3c64fb.jpeg</url>
      <title>DEV Community: Ishuah Kariuki</title>
      <link>https://dev.to/ishuah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ishuah"/>
    <language>en</language>
    <item>
      <title>Three Pis, One Network</title>
      <dc:creator>Ishuah Kariuki</dc:creator>
      <pubDate>Fri, 15 Jan 2021 13:04:44 +0000</pubDate>
      <link>https://dev.to/ishuah/three-pis-one-network-126h</link>
      <guid>https://dev.to/ishuah/three-pis-one-network-126h</guid>
      <description>&lt;p&gt;Most of my projects start from a point of curiosity. This one is no different. I've tinkered with Raspberry Pis over the years, but I've never tried anything on this scale. Three Raspberry Pis connected via a network switch to create a tiny home lab. The perfect environment to run destructive experiments.&lt;/p&gt;

&lt;h2&gt;
  
  
  cluster design
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KzLP_ldt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ishuah.com/images/rpi-network-topology.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KzLP_ldt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ishuah.com/images/rpi-network-topology.jpg"&gt;&lt;/a&gt;My Raspberry Pi Network Topology&lt;/p&gt;

&lt;p&gt;As illustrated above, the Raspberry Pi network is a private subnet. One Raspberry Pi serves as a router, leasing out IP addresses to any hosts connected to the private subnet switch. Additionally, the router acts as an internet gateway by forwarding internet from its Wi-Fi interface (&lt;code&gt;wlan0&lt;/code&gt;, connected to my home Wi-Fi) to its Ethernet interface (&lt;code&gt;eth0&lt;/code&gt;, connected to the private subnet).&lt;/p&gt;

&lt;h2&gt;
  
  
  components list
&lt;/h2&gt;

&lt;p&gt;This is the list of the components that make up my RPi network.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Raspberry Pi 4B x3&lt;/li&gt;
&lt;li&gt;Raspberry Pi 4B  heatsinks x3&lt;/li&gt;
&lt;li&gt;32 GB MicroSD x3&lt;/li&gt;
&lt;li&gt;30 cm Cat6 ethernet cable with RJ45 connectors x3&lt;/li&gt;
&lt;li&gt;USB Type C charging cable x3&lt;/li&gt;
&lt;li&gt;USB charging hub x1&lt;/li&gt;
&lt;li&gt;8-port 10/100Mbps unmanaged switch x1&lt;/li&gt;
&lt;li&gt;MicroHDMI to HDMI cable x1&lt;/li&gt;
&lt;li&gt;USB Keyboard x1&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hkry-ZaX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ishuah.com/images/rpi-network-components.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hkry-ZaX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ishuah.com/images/rpi-network-components.jpg"&gt;&lt;/a&gt;The components&lt;/p&gt;

&lt;p&gt;I couldn't find a local supplier with 1 ft flexible Cat6 Ethernet cables, so I had to get a 1 m Giganet Cat6 UTP Pure Copper Ethernet Cable, cut it up into 4 pieces and attach RJ45 connectors to each of them. Functionally, they serve their purpose but aesthetically, my Pi layout is restricted.&lt;/p&gt;

&lt;p&gt;I bought a generic USB charging hub which, shocker, could not power more than two Pis at a go. I'm currently powering each Pi independently using old phone charging heads. Not the best setup, but it's the best I could do for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  setting up the raspberry pi router
&lt;/h2&gt;

&lt;p&gt;The first step is installing an Operating System on the Raspberry Pi that I intend to use as my router. There are a number of options available but based on my use case Ubuntu Server 20.04 was an obvious choice. I recently found out that they have an arm64 release built specifically for the Raspberry Pi. &lt;/p&gt;

&lt;p&gt;Ubuntu has a &lt;a href="https://ubuntu.com/tutorials/how-to-install-ubuntu-on-your-raspberry-pi#1-overview"&gt;well written tutorial&lt;/a&gt; on how to install the OS on a Raspberry Pi. For the sake of brevity, I will not rewrite the steps on this post, It's easier to follow the documentation as is on the Ubuntu support page. I set up a Wi-Fi connection on the Pi and skipped step 5 because I won't be needing a desktop environment on the RPi router.&lt;/p&gt;

&lt;h3&gt;
  
  
  wlan0 static ip address
&lt;/h3&gt;

&lt;p&gt;The RPi router will serve as a jump server, connecting the private subnet to my home network. For this reason I needed to assign it a static IP address on my home network. This was my process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get the RPi's MAC address on wlan0 by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip addr show wlan0 | &lt;span class="nb"&gt;grep link&lt;/span&gt;/ether | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a Network-DHCP binding on my home network router's admin portal (192.168.1.1). I bound the RPi's MAC address to the static IP address &lt;code&gt;192.168.1.10&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could now &lt;code&gt;ssh&lt;/code&gt; into my RPi router on &lt;code&gt;ubuntu@192.168.1.10&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  eth0 static ip address
&lt;/h3&gt;

&lt;p&gt;I'll be running a DHCP server listening on &lt;code&gt;eth0&lt;/code&gt;. Before setting that up I need to configure a static IP address for the RPi router's &lt;code&gt;eth0&lt;/code&gt; on the private network.&lt;/p&gt;

&lt;p&gt;Ubuntu 17.10 and later uses &lt;a href="https://netplan.io/"&gt;Netplan&lt;/a&gt; as the default network management tool. Netplan's configuration are stored in the &lt;code&gt;/etc/netplan&lt;/code&gt; directory. Ubuntu Server 20.04 is provisioned with &lt;code&gt;cloud-init&lt;/code&gt;, I needed to disable it first before assigning a static IP address to &lt;code&gt;eth0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I created the file &lt;code&gt;/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg&lt;/code&gt; and added the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I assigned the static IP address &lt;code&gt;10.0.0.1/8&lt;/code&gt; to &lt;code&gt;eth0&lt;/code&gt; by creating the file &lt;code&gt;/etc/netplan/01-netplan.yaml&lt;/code&gt; and adding the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ethernets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;10.0.0.1/8&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
            &lt;span class="na"&gt;nameservers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;8.8.4.4&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;8.8.8.8&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  dnsmasq
&lt;/h3&gt;

&lt;p&gt;Next, I installed dnsmasq (short for dns masquerade) to serve as my DHCP server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install dnsmasq&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;dnsmasq
&lt;span class="c"&gt;# backup the default dnsmasq config&lt;/span&gt;
&lt;span class="nb"&gt;sudo mv&lt;/span&gt; /etc/dnsmasq.conf /etc/dnsmasq.conf.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created a new dnsmasq config &lt;code&gt;sudo vim /etc/dnsmasq.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# DHCP should lease addresses over eth0&lt;/span&gt;
&lt;span class="s"&gt;interface=eth0&lt;/span&gt;
&lt;span class="c1"&gt;# Listen on the static IP address 10.0.0.1&lt;/span&gt;
&lt;span class="s"&gt;listen-address=10.0.0.1&lt;/span&gt;
&lt;span class="c1"&gt;# Enable dnsmasq's integrated DHCP server&lt;/span&gt;
&lt;span class="c1"&gt;# and define 96 available address leases&lt;/span&gt;
&lt;span class="s"&gt;dhcp-range=10.0.0.32,10.0.0.128,12h&lt;/span&gt;
&lt;span class="c1"&gt;# declare name servers&lt;/span&gt;
&lt;span class="s"&gt;server=8.8.8.8&lt;/span&gt;
&lt;span class="s"&gt;server=8.8.4.4&lt;/span&gt;
&lt;span class="c1"&gt;# Bind dnsmasq to eth0&lt;/span&gt;
&lt;span class="s"&gt;bind-interfaces&lt;/span&gt;
&lt;span class="c1"&gt;# prevent packets with malformed domain names from forwarding&lt;/span&gt;
&lt;span class="s"&gt;domain-needed&lt;/span&gt;
&lt;span class="c1"&gt;# prevent packets from non-routed address spaces from forwarding&lt;/span&gt;
&lt;span class="s"&gt;bogus-priv&lt;/span&gt;
&lt;span class="c1"&gt;# Use the hosts file on this machine&lt;/span&gt;
&lt;span class="s"&gt;expand-hosts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;sudo service dnsmasq status&lt;/code&gt; returns status &lt;code&gt;failed&lt;/code&gt; because &lt;code&gt;eth0&lt;/code&gt; is not connected, yet. &lt;code&gt;dnsmasq&lt;/code&gt; will fail to assign an IP address to an interface if it's not up. That's okay, we'll get to the networking part later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  forward internet from wlan0 to eth0
&lt;/h2&gt;

&lt;p&gt;In my topology description above, the RPi hosts in my private network have access to the internet through the Router Pi. To achieve this, I'll be setting up internet forwarding on the Router Pi.&lt;/p&gt;

&lt;p&gt;First, I enabled IP forwarding by uncommenting the following line in &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;net.ipv4.ip_forward=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I added a masquerade rule to packets leaving the &lt;code&gt;wlan0&lt;/code&gt; interface. This means that traffic through &lt;code&gt;wlan0&lt;/code&gt; can be rerouted without disruption.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; POSTROUTING &lt;span class="nt"&gt;-o&lt;/span&gt; wlan0 &lt;span class="nt"&gt;-j&lt;/span&gt; MASQUERADE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next command forwards ESTABLISHED and RELATED packets from &lt;code&gt;wlan0&lt;/code&gt; to &lt;code&gt;eth0&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; FORWARD &lt;span class="nt"&gt;-i&lt;/span&gt; wlan0 &lt;span class="nt"&gt;-o&lt;/span&gt; eth0 &lt;span class="nt"&gt;-m&lt;/span&gt; state &lt;span class="nt"&gt;--state&lt;/span&gt; RELATED,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This last command forwards all packets from &lt;code&gt;eth0&lt;/code&gt; to &lt;code&gt;wlan0&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; FORWARD &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="nt"&gt;-o&lt;/span&gt; wlan0 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, my &lt;code&gt;iptables&lt;/code&gt; rules looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;Chain&lt;/span&gt; &lt;span class="n"&gt;INPUT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;ACCEPT&lt;/span&gt; &lt;span class="m"&gt;30335&lt;/span&gt; &lt;span class="n"&gt;packets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1580&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;pkts&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;     &lt;span class="n"&gt;prot&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;     &lt;span class="n"&gt;out&lt;/span&gt;     &lt;span class="n"&gt;source&lt;/span&gt;               &lt;span class="n"&gt;destination&lt;/span&gt;                   

&lt;span class="n"&gt;Chain&lt;/span&gt; &lt;span class="n"&gt;FORWARD&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;ACCEPT&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="n"&gt;packets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;pkts&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;     &lt;span class="n"&gt;prot&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;     &lt;span class="n"&gt;out&lt;/span&gt;     &lt;span class="n"&gt;source&lt;/span&gt;               &lt;span class="n"&gt;destination&lt;/span&gt;                   
    &lt;span class="m"&gt;0&lt;/span&gt;     &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="n"&gt;ACCEPT&lt;/span&gt;     &lt;span class="n"&gt;all&lt;/span&gt;  &lt;span class="o"&gt;--&lt;/span&gt;  &lt;span class="n"&gt;wlan0&lt;/span&gt;  &lt;span class="n"&gt;eth0&lt;/span&gt;    &lt;span class="m"&gt;0.0.0.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;            &lt;span class="m"&gt;0.0.0.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;            &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="n"&gt;RELATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ESTABLISHED&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt;     &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="n"&gt;ACCEPT&lt;/span&gt;     &lt;span class="n"&gt;all&lt;/span&gt;  &lt;span class="o"&gt;--&lt;/span&gt;  &lt;span class="n"&gt;eth0&lt;/span&gt;   &lt;span class="n"&gt;wlan0&lt;/span&gt;   &lt;span class="m"&gt;0.0.0.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;            &lt;span class="m"&gt;0.0.0.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;           

&lt;span class="n"&gt;Chain&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;ACCEPT&lt;/span&gt; &lt;span class="m"&gt;30665&lt;/span&gt; &lt;span class="n"&gt;packets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1624&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;pkts&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;     &lt;span class="n"&gt;prot&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;     &lt;span class="n"&gt;out&lt;/span&gt;     &lt;span class="n"&gt;source&lt;/span&gt;               &lt;span class="n"&gt;destination&lt;/span&gt;                    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I installed &lt;code&gt;iptables-persistent&lt;/code&gt; to save the iptables rules permanently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;iptables-persistent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Remember to &lt;code&gt;sudo reboot&lt;/code&gt; before proceeding to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  raspberry pi hosts
&lt;/h2&gt;

&lt;p&gt;I wrote the Ubuntu Server 20.04 image for the Pi hosts in almost the same way I did it for the Router Pi, except setting up a Wi-Fi connection. These hosts will access the internet via forwarded connections on the Router Pi.&lt;/p&gt;

&lt;h2&gt;
  
  
  putting it all together
&lt;/h2&gt;

&lt;p&gt;The final stage is connecting all the Pis to the network switch and powering up the switch. All that toil has to amount to something.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;sudo service dnsmasq status&lt;/code&gt; should return &lt;code&gt;Active: active(running)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded &lt;span class="o"&gt;(&lt;/span&gt;/lib/systemd/system/dnsmasq.service&lt;span class="p"&gt;;&lt;/span&gt; enabled&lt;span class="p"&gt;;&lt;/span&gt; vendor preset: enabled&lt;span class="o"&gt;)&lt;/span&gt;
     Active: active &lt;span class="o"&gt;(&lt;/span&gt;running&lt;span class="o"&gt;)&lt;/span&gt; since Tue 2021-01-12 12:12:11 UTC&lt;span class="p"&gt;;&lt;/span&gt; 20min ago
    Process: 1249 &lt;span class="nv"&gt;ExecStartPre&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/sbin/dnsmasq &lt;span class="nt"&gt;--test&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited, &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0/SUCCESS&lt;span class="o"&gt;)&lt;/span&gt;
    Process: 1356 &lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/init.d/dnsmasq systemd-exec &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited, &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0/SUCCESS&lt;span class="o"&gt;)&lt;/span&gt;
    Process: 1379 &lt;span class="nv"&gt;ExecStartPost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/init.d/dnsmasq systemd-start-resolvconf &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited, &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0/SUCCESS&lt;span class="o"&gt;)&lt;/span&gt;
   Main PID: 1377 &lt;span class="o"&gt;(&lt;/span&gt;dnsmasq&lt;span class="o"&gt;)&lt;/span&gt;
      Tasks: 1 &lt;span class="o"&gt;(&lt;/span&gt;limit: 4436&lt;span class="o"&gt;)&lt;/span&gt;
     Memory: 2.7M
     CGroup: /system.slice/dnsmasq.service
             └─1377 /usr/sbin/dnsmasq &lt;span class="nt"&gt;-x&lt;/span&gt; /run/dnsmasq/dnsmasq.pid &lt;span class="nt"&gt;-u&lt;/span&gt; dnsmasq &lt;span class="nt"&gt;-7&lt;/span&gt; /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new &lt;span class="nt"&gt;--local-service&lt;/span&gt; &lt;span class="nt"&gt;--trust-anchor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.,20326,8,2,e06d44b80b8f1d39a95c0b0d7c65d08458e880409&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;cat /var/lib/misc/dnsmasq.leases&lt;/code&gt; should list two DHCP leases, assigned to the two hosts in the private network. I tested internet forwarding by &lt;code&gt;ssh&lt;/code&gt;ing into each Pi using the IPs on their leases and checking that they have internet access.&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  pains
&lt;/h2&gt;

&lt;p&gt;I had a hard time figuring out how to assign a static IP address to &lt;code&gt;eth0&lt;/code&gt; on the Router Pi. The last time I did that was before netplan, when all network configs were on one file: &lt;code&gt;/etc/network/interfaces&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The hardware currently sits in a disorganized state on my work desk. I ordered a Raspberry Pi &lt;a href="https://www.amazon.com/GeeekPi-Raspberry-Cluster-Stackable-4-Layers/dp/B083FDHPBH/ref=sr_1_3?dchild=1&amp;amp;keywords=raspberry%2Bpi%2B4%2Btower&amp;amp;qid=1610457918&amp;amp;sr=8-3&amp;amp;th=1"&gt;cluster case&lt;/a&gt; and &lt;a href="https://www.amazon.com/Cable-Matters-160021-Snagless-Ethernet/dp/B00E5I7T9I/ref=sr_1_2_sspa?dchild=1&amp;amp;keywords=raspberry%2Bpi%2B4%2Bcat6%2Bcable&amp;amp;qid=1610458153&amp;amp;sr=8-2-spons&amp;amp;spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUExMUVHVDdPU0FWVTdZJmVuY3J5cHRlZElkPUEwNTMwMDk5MU5aODRBNzhQVDAzVSZlbmNyeXB0ZWRBZElkPUEwNDgxOTc2RU9BNjhDTldGVDE4JndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ&amp;amp;th=1"&gt;flexible Cat6 Ethernet cables&lt;/a&gt; so that I can have a better organized home lab.&lt;/p&gt;

&lt;p&gt;The next article in this series will be about installing Kubernetes on my private Raspberry Pi network.&lt;/p&gt;

&lt;p&gt;Originally posted on &lt;a href="https://ishuah.com/2021/01/12/three-pis-one-network/"&gt;ishuah.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>networking</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Design Patterns: A Retrospective</title>
      <dc:creator>Ishuah Kariuki</dc:creator>
      <pubDate>Sat, 30 May 2020 16:03:56 +0000</pubDate>
      <link>https://dev.to/ishuah/design-patterns-a-retrospective-51gd</link>
      <guid>https://dev.to/ishuah/design-patterns-a-retrospective-51gd</guid>
      <description>&lt;p&gt;Design patterns are like the instruction manuals that come with your dining table, you’ll never need them unless you want to enjoy your meals on a flat surface. Based on that statement, you can tell that I’m not entirely impartial when it comes to software design patterns. I’ve been on both ends of the spectrum, from indifference to the type of fondness you only have for comfort food.&lt;/p&gt;

&lt;p&gt;If you’re like me you probably implemented design patterns without really understanding them. I only fully appreciated the concept after (an admitted struggle) reading the GoF Design Patterns book. Legend has it that, after writing copious amounts of code, the “Gang of Four” realized that they were solving recurring problems. For the good of the &lt;a href="https://blog.codinghorror.com/how-to-be-lazy-dumb-and-successful/"&gt;lazy&lt;/a&gt;, they published &lt;a href="https://www.amazon.com/Design-patterns-elements-reusable-object-oriented/dp/0201633612"&gt;&lt;em&gt;Design Patterns: Elements of Reusable Object-Oriented Software&lt;/em&gt;&lt;/a&gt; (&lt;em&gt;aka&lt;/em&gt; GoF Design Patterns), a book that catalogs 23 solutions to common programming problems.&lt;/p&gt;

&lt;p&gt;The Design Patterns concept was adapted from Christopher Alexanders’ &lt;a href="https://www.amazon.com/Pattern-Language-Buildings-Construction-Environmental/dp/0195019199"&gt;&lt;em&gt;A Pattern Language&lt;/em&gt;&lt;/a&gt;. In the book, Alexander et al. describe reusable solutions to architectural problems. Architectural as in buildings and doors and all that stuff. The Gang of Four applied the same concept to software engineering, specifically object-oriented software design. They used practical solutions implemented in C++ and Smalltalk, both popular languages at the time (the early 1990s). Initially the term “Design Patterns” almost exclusively referred to the patterns described in this book. Over the years more patterns have been developed but the core definition remains the same: A design pattern is a repeatable solution to a commonly occurring problem.&lt;/p&gt;

&lt;p&gt;Design patterns are not simple, they were never meant to be because the problems they solve are not simple. As with all non-simple things, there’s a danger of misinterpretation which leads to misuse, abuse, and bad code reviews. I went through a ‘design pattern frenzy’ where I tried to implement patterns in everything until everything was an untidy ball of regrets. There are no shortcuts with design patterns. They come with complexity and consequences which have to be taken into consideration.&lt;/p&gt;

&lt;p&gt;Some design patterns encourage bad practice. I’m looking at you, Singleton. Quoting Kent Beck in his book &lt;a href="https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530"&gt;&lt;em&gt;Test-Driven Development By Example&lt;/em&gt;&lt;/a&gt;, he says, “&lt;em&gt;How do you provide global variables in languages without global variables? Don’t. Your programs will thank you for taking the time to think about design instead.&lt;/em&gt;” Singletons are considered harmful because they create globals and &lt;a href="http://wiki.c2.com/?GlobalVariablesAreBad"&gt;globals are bad&lt;/a&gt;. There are cases where Singletons are useful, for instance, application logs. It’s much easier to have one instance of your log class which you can use throughout your application without worrying about initialization. It’s considered acceptable because logging shouldn’t affect the execution of your application.&lt;/p&gt;

&lt;p&gt;There are harsher criticisms that question the existence of design patterns. In 1996, Peter Norvig made a presentation titled &lt;a href="http://norvig.com/design-patterns/"&gt;&lt;em&gt;Design Patterns in Dynamic Languages&lt;/em&gt;&lt;/a&gt; where he demonstrated that most design patterns become simpler or even unnecessary in Lisp. Paul Graham, in his blog post &lt;a href="http://www.paulgraham.com/icad.html"&gt;&lt;em&gt;Revenge of the Nerds&lt;/em&gt;&lt;/a&gt;, says: &lt;em&gt;“I wonder if these patterns are not sometimes evidence of case ( c ), the human compiler, at work. When I see patterns in my programs, I consider it a sign of trouble.”&lt;/em&gt; These two arguments lead to a broader question, &lt;a href="http://wiki.c2.com/?AreDesignPatternsMissingLanguageFeatures"&gt;are design patterns missing language features?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite the criticism, Design Patterns have made an impact. User interface designers have a cataloged list of &lt;a href="http://ui-patterns.com/patterns"&gt;100+ design patterns&lt;/a&gt;, documented very similarly to the GoF design patterns. React developers have a free ebook that addresses &lt;a href="https://github.com/krasimir/react-in-patterns"&gt;react in patterns&lt;/a&gt;. There are countless collections of design pattern implementations in multiple languages (&lt;a href="https://github.com/faif/python-patterns"&gt;Python&lt;/a&gt;, &lt;a href="https://github.com/tmrts/go-patterns"&gt;Golang&lt;/a&gt;, &lt;a href="https://github.com/iluwatar/java-design-patterns"&gt;Java&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Back when I started learning Golang, I wanted to see how tests were written in production. That’s how I began contributing to &lt;a href="https://github.com/ncw/rclone"&gt;ncw/rclone&lt;/a&gt;. The &lt;a href="https://github.com/ncw/rclone/pull/1648/files#diff-76e951d9b2563014f8f5449cda9313e4R908"&gt;first contribution&lt;/a&gt; I ever made loosely revolved around the Abstract Factory Pattern.&lt;/p&gt;

&lt;p&gt;Rclone is a command line utility that syncs files and directories to and from different cloud storage providers. In this PR I implemented a way of emptying the trash on Google Drive. For some context on the code above, &lt;em&gt;Fs&lt;/em&gt; is an interface that represents a file system and must be implemented by each cloud storage object. To implement an interface in Go, a struct has to implement all the interface methods. Cloud storage providers don’t have similar functionality so @&lt;a href="https://github.com/ncw"&gt;ncw&lt;/a&gt; wrote a &lt;a href="https://github.com/ncw/rclone/commit/1fa258c2b45cdd81f245104608285fc7df81e5b5#diff-f6c6bb8a6ec40b9f63d107bc7070f708"&gt;&lt;em&gt;Features&lt;/em&gt;&lt;/a&gt;&lt;em&gt;()&lt;/em&gt; method that returns all the optional features in a file system. CleanUp is one of those features. I didn’t have to write the empty trash method in the &lt;em&gt;Google Drive struct&lt;/em&gt;, it’s already available in the official &lt;a href="https://godoc.org/google.golang.org/api/drive/v3#FilesService.EmptyTrash"&gt;drive package&lt;/a&gt;. All I had to do was implement the CleanUp feature in the &lt;em&gt;Google Drive struct&lt;/em&gt;, calling the empty trash method from within. The PR had 28 lines added, most of which was documentation. It took me less than 2 hours to put the whole thing together.&lt;/p&gt;

&lt;p&gt;That’s the power behind Design Patterns. They have a steep learning curve but the benefits are worth it, especially with large engineering teams working on expansive codebases. Better coordinated engineers means less technical debt, more productivity and better cycles.&lt;/p&gt;

&lt;p&gt;Design Patterns are a subjective choice in an objective discipline. If all else fails, &lt;a href="https://en.wikipedia.org/wiki/KISS_principle"&gt;KISS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By &lt;a href="https://medium.com/@ishuah"&gt;Ishuah&lt;/a&gt; on &lt;a href="https://medium.com/p/ea32e77f968a"&gt;March 25, 2019&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@ishuah/design-patterns-a-retrospective-ea32e77f968a"&gt;Canonical link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>designpatterns</category>
    </item>
  </channel>
</rss>
