<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>cilium on Luis Logs</title>
    <link>https://luislogs.com/tags/cilium/</link>
    <description>Recent content in cilium on Luis Logs</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 11 Feb 2024 14:40:30 +0900</lastBuildDate><atom:link href="https://luislogs.com/tags/cilium/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Security and observability with Cilium on my 5G network</title>
      <link>https://luislogs.com/posts/security-and-observability-with-cilium-on-my-5g-network/</link>
      <pubDate>Sun, 11 Feb 2024 14:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/security-and-observability-with-cilium-on-my-5g-network/</guid>
      <description>Last June I shared a post about deploying a 4G core network, exploring containerization of 4G telco applications at home with GNS3. That time GNS3 was acting as another layer of virtualization since it was running as a VM on top of my NAS. This time I&amp;rsquo;ve decided to convert my main server from a NAS-first equipment to a hypervisor-first solution, allowing me to spin up VMs faster and more efficiently with the help of Terraform and Ansible.</description>
      <content:encoded><![CDATA[<p>Last June I shared a post about deploying a 4G core network, exploring containerization of 4G telco applications at home with GNS3. That time GNS3 was acting as another layer of virtualization since it was running as a VM on top of my NAS. This time I&rsquo;ve decided to convert my main server from a NAS-first equipment to a hypervisor-first solution, allowing me to spin up VMs faster and more efficiently with the help of Terraform and Ansible.</p>
<p>Today I also want to share how I started exploring Cilium&rsquo;s other features along side open5gs&rsquo; 5G core. Luckily there is a link in the official documentation to available helm charts created and uploaded to github by <a href="https://github.com/Gradiant/5g-charts">Gradiant</a>. Their tutorial includes existing working values.yaml files to integrate the open5gs applications together with the UERANSIM simulator that also comes in a separate helm chart provided by them.</p>
<p>In this demonstration we will gradually build up our network policies based on the information we will get from Hubble. We will start by applying a DNS rule to see the connections the NFs are trying to make, then we apply a cilium network policy to whitelist that flow. We will keep doing the same thing (check hubble, define CNP, repeat) until we are able to see a successful UE attach.</p>
<h2 id="preparing-the-environment">Preparing the environment</h2>
<p>I recently completed the README of my HA K3S project utilizing Terraform and Ansible but overall the project is a work in progress. I still a have a lot of shell commands that I would need to convert to a module and later on clean up the scripts with roles. You can check the <a href="https://github.com/luifrancisco/k3s-ha">github project here</a> or you can follow ahead.</p>
<p>You can also skip this part if you alread have a working k8s cluster, but here I also noted some points that you might find helpful if you are hosting your cluster at home with VMs.</p>
<p>First we will spin up VMs on Proxmox with the help of Terraform. Each VM will have 4 CPU and 4GB RAM with the CPU type set to <strong>host</strong> to pass-through the required feature set to run the latest version of mongodb. Each will have a 50GB disk and a single vNIC with the IP address and default gateway automatically configured with cloud-init.</p>
<p>Next we execute a preflight script to install some packages, do a couple of other stuff to prepare the environment for use with Ansible.</p>
<p>Then, we execute <code>ansible-playbook -i inventory.yaml k3s-kubevip-helm-ciliumInstallHelmCli.yaml</code> to install K3S, Kube-vip, helm, and Cilium. If you will be using the same ansible script in the k3s-ha repository, do note that Cilium&rsquo;s <code>kube-proxy</code> replacement doesn&rsquo;t support SCTP yet, though I have created the playbook for general K3S use. In this case you have to remove or comment this out in the playbook.</p>
<h2 id="cilium-hubble-and-open5gs">Cilium, Hubble, and Open5gs</h2>
<p>Before we start deploying open5gs helm charts, let&rsquo;s make it a prerequisite to install hubble. You can install cilium cli and hubble from your management host where you normally execute your kubectl commands. Note that by default hubble will not monitor the packets. For that to happen we can either annotate the pods or enforce a CiliumNetworkPolicy. We will go with the latter.</p>
<p>Apply the following CiliumNetworkPolicy, l4-egress-to-dns.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-egress-to-dns&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/instance</span><span class="p">:</span><span class="w"> </span><span class="l">open5gs</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;53&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">ANY</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">dns</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="nt">matchPattern</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;*&#34;</span><span class="w">
</span></span></span></code></pre></div><p>Here we will allow DNS traffic on port 53 for resolving the FQDNs of the services to be used with SBI. For now we will not configure the CNP (CiliumNetworkPolicy) for any other port. Note that the L4 DNS rule is also set on the egress of each pod. After the successful DNS resolution by any labeled pod, the succeeding connection attempts should be filtered and dropped by Cilium.</p>
<p>In another terminal window let&rsquo;s run our hubble command to start monitoring TCP requests that will be initiated for the sending out HTTP POST messages for the NRF registration.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cilium hubble port-forward<span class="p">&amp;</span>
</span></span><span class="line"><span class="cl">hubble observe -n default -f --protocol TCP --port <span class="m">7777</span>
</span></span></code></pre></div><p>Now that is set, let&rsquo;s deploy our 5G core network:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">helm install open5gs oci://registry-1.docker.io/gradiant/open5gs --version 2.2.0 --values https://gradiant.github.io/5g-charts/docs/open5gs-ueransim-gnb/5gSA-values.yaml
</span></span></code></pre></div><p>Soon enough we should start seeing TCP messages filtered by hubble with the verdict <strong>DROPPED</strong> since we only allowed DNS until this point. We should be able to see all core components are trying to connect to SCP on port 7777 and SCP trying to connect to NRF on the same port.</p>
<p>
    <img src="/posts/security-and-observability-with-cilium-on-my-5g-network/hubble1/1_hubble.png" alt="">
  </p>
<p>Let&rsquo;s then proceed to create a new L7 CNP that allows this communication. We again configure this on the egress. Note that although CiliumNetworkPolicies are stateful, it only means you can expect the return path of the packet to be allowed as well. Here we need to ensure that the forward path will be allowed.</p>
<p>But just right before that let&rsquo;s define a label for each core component that is expected to have an SBI. We can then match this label when we apply our network policy. Let&rsquo;s patch each of those component with the label <code>sbi: enabled</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl patch deployment open5gs-amf --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-ausf --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-bsf --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-nrf --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-nssf --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-pcf --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-scp --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-smf --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-udm --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-udr --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span><span class="line"><span class="cl">kubectl patch deployment open5gs-upf --patch <span class="s1">&#39;{&#34;spec&#34;: {&#34;template&#34;: {&#34;metadata&#34;: {&#34;labels&#34;: {&#34;sbi&#34;: &#34;enabled&#34;}}}}}&#39;</span>
</span></span></code></pre></div><p>Let&rsquo;s start monitoring HTTP POST requests this time with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hubble observe -n default -f --http-method POST
</span></span></code></pre></div><p>Apply the following CNP, l7-egress-to-scp.yaml, l7-egress-scp-to-nrf.yaml, making use of the label we just defined:</p>
<p>l7-egress-to-scp.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l7-egress-to-scp&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">sbi</span><span class="p">:</span><span class="w"> </span><span class="l">enabled</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w"> 
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">scp</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For SBI communication</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;7777&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">http</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{}<span class="p">]</span><span class="w">
</span></span></span></code></pre></div><p>l7-egress-scp-to-core.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l7-egress-scp-to-core&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">scp</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w"> 
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">sbi</span><span class="p">:</span><span class="w"> </span><span class="l">enabled</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For SBI communication</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;7777&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">http</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{}<span class="p">]</span><span class="w">
</span></span></span></code></pre></div><p>Let&rsquo;s also configure another CNP to allow ingress HTTP traffic only if it&rsquo;s coming from the SCP.</p>
<p>l7-ingress-from-scp.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l7-ingress-from-scp&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">sbi</span><span class="p">:</span><span class="w"> </span><span class="l">enabled</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ingress</span><span class="p">:</span><span class="w"> 
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">fromEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">scp</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">sbi</span><span class="p">:</span><span class="w"> </span><span class="l">enabled</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For SBI communication</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;7777&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">http</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{}<span class="p">]</span><span class="w">
</span></span></span></code></pre></div><p>We will also allow port 27017 used by mongodb to be accessed by UDR, PCF, and WebUI.</p>
<p>l4-egress-populate-webui-udr-pcf-to-mongodb.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-egress-populate-to-mongodb&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/component</span><span class="p">:</span><span class="w"> </span><span class="l">populate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mongodb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For mongodb access</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;27017&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-egress-webui-to-mongodb&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">webui</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mongodb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For mongodb access</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;27017&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-egress-udr-to-mongodb&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">udr</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mongodb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For mongodb access</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;27017&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-egress-pcf-to-mongodb&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">pcf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mongodb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For mongodb access</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;27017&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f l7-egress-to-scp.yaml -f l7-ingress-from-scp.yaml -f l7-egress-scp-to-nrf.yaml -f l4-egress-to-mongodb.yaml
</span></span></code></pre></div><p>HTTP2 messages look fine. Here we can see each application performing NF registration with NRF via SCP (acting as proxy).

    <img src="/posts/security-and-observability-with-cilium-on-my-5g-network/hubble2/2_hubble.png" alt="">
  </p>
<p>We can also check the heartbeat messages by changing the http method to PATCH.

    <img src="/posts/security-and-observability-with-cilium-on-my-5g-network/hubble3/3_hubble.png" alt="">
  </p>
<p>If you also want to see the DNS flow you can execute <code>hubble observe -n kube-system -f --port 53</code>.

    <img src="/posts/security-and-observability-with-cilium-on-my-5g-network/hubble4/4_hubble.png" alt="">
  </p>
<p>Next we deploy the UERANSIM GNB and UE pods.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm install ueransim-gnb oci://registry-1.docker.io/gradiant/ueransim-gnb --version 0.2.6 --values https://gradiant.github.io/5g-charts/docs/open5gs-ueransim-gnb/gnb-ues-values.yaml
</span></span></code></pre></div><p>If you follow the instructions you should expect a new tunnel interface inside the UE pod. But you most likely won&rsquo;t and if you check the gNB logs, you should see an error <strong>&ldquo;Cell selection failure, no suitable or acceptable cell found&rdquo;</strong>.</p>
<p>We can check first if gNB is able to connect successfully to the AMF. gNB connects via SCTP so we can filter that with hubble:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hubble observe -n default -f --protocol sctp
</span></span></code></pre></div><p>
    <img src="/posts/security-and-observability-with-cilium-on-my-5g-network/hubble5/5_hubble.png" alt="">
  </p>
<p>It&rsquo;s getting dropped due to the L7 ingress rule we applied. You might have thought for a while why it&rsquo;s dropping the packet if it&rsquo;s an L7 rule even if SCTP is on L4. Network policies actually work as a whitelist, so everything else not part of the policies we are applying gets dropped by default the moment you apply the rule on either the ingress or egress direction.</p>
<p>Let&rsquo;s go apply the SCTP rule!</p>
<p>l4-ingress-amf-from-gnb-sctp.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-ingress-from-gnb-sctp&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">amf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ingress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">fromEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">ueransim-gnb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;38412&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">SCTP</span><span class="w">
</span></span></span></code></pre></div><p>OK, the SCTP association looks established now.

    <img src="/posts/security-and-observability-with-cilium-on-my-5g-network/hubble6/6_hubble.png" alt="">
  </p>
<p>We can also see successful UE registration:

    <img src="/posts/security-and-observability-with-cilium-on-my-5g-network/hubble7/7_hubble.png" alt="">
  </p>
<p>But still no tunneled interface. Let&rsquo;s check for other dropped packets:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hubble observe -n default -f --verdict DROPPED
</span></span></code></pre></div><p>What&rsquo;s getting dropped this time? We forgot about the SMF to UPF communication. From UPF we should also allow all ports to any destination at least on the egress side. Apart from this the gNB should also be allowed to communicate with the UPF for use traffic.</p>
<p>l4-egress-smf-to-upf.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-egress-smf-to-upf&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">smf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">upf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For SMF-UPF internet traffic</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;8805&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">UDP</span><span class="w">
</span></span></span></code></pre></div><p>l4-ingress-upf-from-smf.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-ingress-upf-from-smf&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">upf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ingress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">fromEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">smf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For SMF-UPF internet traffic</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;8805&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">UDP</span><span class="w">
</span></span></span></code></pre></div><p>l4-ingress-upf-from-gnb.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l4-ingress-upf-from-gnb&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">upf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ingress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">fromEndpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">app.kubernetes.io/component</span><span class="p">:</span><span class="w"> </span><span class="l">gnb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">toPorts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># For SMF-UPF internet traffic</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;2152&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">UDP</span><span class="w">
</span></span></span></code></pre></div><p>l3-egress-upf-to-any.yaml:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumNetworkPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;l3-egress-upf-to-any&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">endpointSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">upf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">egress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">toCIDRSet</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">cidr</span><span class="p">:</span><span class="w"> </span><span class="m">0.0.0.0</span><span class="l">/0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="c"># except private CIDR</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">except</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="m">10.0.0.0</span><span class="l">/8</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="m">172.16.0.0</span><span class="l">/12</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="m">192.168.0.0</span><span class="l">/16</span><span class="w">
</span></span></span></code></pre></div><p>For the UPF we allow all IP addresses except for the private CIDR blocks.</p>
<p>Let&rsquo;s try one more time. It should be successful now. This time let&rsquo;s monitor all traffic related to 8.8.8.8: <code>hubble observe -f --ip 8.8.8.8</code>. From the UE we initiate with <code>ping -I uesimtun0 8.8.8.8</code>.</p>
<p>
    <img src="/posts/security-and-observability-with-cilium-on-my-5g-network/hubble8/8_hubble.png" alt="">
  </p>
<p>For the last part we try to attach an unprovisioned SUPI. This time we can filter http status code 404 to verify:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hubble observe -f -n default --http-status <span class="m">404</span>
</span></span></code></pre></div><p>Deploy the UE pods with unknown SUPI:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm install -n default ueransim-ues-not-defined oci://registry-1.docker.io/gradiant/ueransim-ues <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set gnb.hostname<span class="o">=</span>ueransim-gnb <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">count</span><span class="o">=</span><span class="m">2</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">initialMSISDN</span><span class="o">=</span><span class="s2">&#34;0000000003&#34;</span> <span class="se">\
</span></span></span></code></pre></div><p></p>
<p>We can see here 404 response received at UDM from UDR, AUSF from UDM, and at AMF back from AUSF.</p>
<p>To sum up all the cilium network policies we applied, you can refer to the below list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">NAME
</span></span><span class="line"><span class="cl">l3-egress-upf-to-any
</span></span><span class="line"><span class="cl">l4-egress-pcf-to-mongodb
</span></span><span class="line"><span class="cl">l4-egress-populate-to-mongodb
</span></span><span class="line"><span class="cl">l4-egress-smf-to-upf
</span></span><span class="line"><span class="cl">l4-egress-to-dns
</span></span><span class="line"><span class="cl">l4-egress-udr-to-mongodb
</span></span><span class="line"><span class="cl">l4-egress-webui-to-mongodb
</span></span><span class="line"><span class="cl">l4-ingress-amf-from-gnb-sctp
</span></span><span class="line"><span class="cl">l4-ingress-upf-from-gnb
</span></span><span class="line"><span class="cl">l4-ingress-upf-from-smf
</span></span><span class="line"><span class="cl">l7-egress-scp-to-core
</span></span><span class="line"><span class="cl">l7-egress-to-scp
</span></span><span class="line"><span class="cl">l7-ingress-from-scp
</span></span></code></pre></div><h2 id="cilium-with-hubble-is-awesome">Cilium (with Hubble) is awesome</h2>
<p>To conclude, we have demonstrated how we can make use of network policies to add another layer of security to our network. We managed to create new policies, applying them on the ingress or egress wherever necessary, making use of L3, L4, and L7 rules to whitelist the traffic. At the same time we also utilized hubble to have a good peek into the network layer, allowing us to understand how network policies work and see which packets are getting allowed, dropped, or rejected. We made use of different filters working on protocol level, http method, http status, port. This demonstration doesn&rsquo;t cover the full capability of hubble and there is a ton of other different filters you can use for observability. And the good thing is the project is in very active development and the guys over at Isovalent seem to add cool new features every time they have a new release. I will not be surprised if Cilium becomes the primary choice for CNI for any new project. Stay tuned for upcoming posts as we explore more other Cilium features!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Re-engineering the Homelab with IaC and Kubernetes: An overview</title>
      <link>https://luislogs.com/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/</link>
      <pubDate>Sat, 23 Sep 2023 12:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/</guid>
      <description>In relation to my previous post where I mentioned that I will be starting a new journey learning IaC or Infrastructure-as-Code, today I am very happy to record this milestone of finally achieving a stable kubernetes cluster created with the help of Ansible and Terraform.
At this time of writing, so far only two services have been migrated from the docker environment into the new K8S cluster. That is my DNS which is also replaced now by AdguardHome (sorry Pihole!</description>
      <content:encoded><![CDATA[<p>In relation to my previous post where I mentioned that I will be starting a new journey learning IaC or Infrastructure-as-Code, today I am very happy to record this milestone of finally achieving a stable kubernetes cluster created with the help of Ansible and Terraform.</p>
<p>At this time of writing, so far only two services have been migrated from the docker environment into the new K8S cluster. That is my DNS which is also replaced now by AdguardHome (sorry Pihole!), and Traefik, which terminates all external HTTP communication incoming to my Homelab. And for the rest of the other services, those will be migrated in the coming weeks. For now what is important is I have HTTPS running perfectly fine with valid CA certificates even with dockerized services in the backend.</p>
<p>I also want to note here that during the learning process, whatever services that were running in the docker setup, all of those were nearly untouched since I was working inside a staging environment, keeping rigorous activities of countless create-delete of resources in isolation from my home production network.</p>
<p>That said, I received zero complaints from the wife except for the time I lost connectivity to the cluster when I thought everything was already stable. Lol more on this later.</p>
<p>In summary below opensource tools have been used for this project.</p>
<ul>
<li><a href="https://www.proxmox.com/">Proxmox</a> - Hypervisor (installed in 3 devices)</li>
<li><a href="https://www.terraform.io/">Terraform</a> - VM instantiation</li>
<li><a href="https://www.ansible.com/">Ansible</a> - VM configuration, Kubernetes installation</li>
<li><a href="https://k3s.io/">K3s</a> - Kubernetes platform</li>
<li><a href="https://kube-vip.io/">Kube-vip</a> - LB for Kube API</li>
<li><a href="https://longhorn.io/">Longhorn</a> - Block storage</li>
<li><a href="https://cilium.io/">Cilium</a> - CNI</li>
<li><a href="https://traefik.io/traefik/">Traefik</a> - Reverse Proxy</li>
<li><a href="https://cert-manager.io/">Cert-manager</a> - SSL certificate manager</li>
</ul>
<p><strong>Terraform</strong> and <strong>Ansible</strong> play the major role of orchestrators allowing for quick creation and deletion of virtual resources putting Infrascture-as-Code into practice. The rest are supplementary to learning IaC.</p>
<p><em>Side node: Unfortunately Terraform will be transitioning away from a completely opensource license and soon will be sitting behind a BSL. It might be a good idea to switch to <a href="https://opentofu.org/">OpenTF</a> (or OpenTofu) which is a fork of Terraform and now officially a CNCF project as well.</em></p>
<h2 id="the-setup">The setup</h2>
<p>All development activities were done in a staging environment. This is where the unaccounted events of VM creation-deletion have transpired from testing out the Terraform scripts until the very end when installing Longhorn and Cilium with Ansible, and even when playing around with Traefik in K8s.</p>
<p>The staging environment was deployed on my Unraid box and as for the production build, I am using Tiny PCs that house a total of 32GB RAM each allowing more room for other VMs that I might need in the long run. To give a glimpse of the production HW:</p>
<table>
<thead>
<tr>
<th>PVE host</th>
<th>CPU</th>
<th>Memory</th>
<th>Disk</th>
</tr>
</thead>
<tbody>
<tr>
<td>PVE1</td>
<td>6</td>
<td>32GB</td>
<td>512GB</td>
</tr>
<tr>
<td>PVE2</td>
<td>4</td>
<td>32GB</td>
<td>480GB</td>
</tr>
<tr>
<td>PVE3</td>
<td>4</td>
<td>32GB</td>
<td>480GB</td>
</tr>
</tbody>
</table>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/hardware2.jpeg" alt="Hardware diagram">
      <figcaption>Actual hardware</figcaption>
    </figure>
  </p>
<p>I was also able to source data center-grade Intel S3510 SSD drives from the second-hand market. These should help in the reliability department of these nodes that will be running data replication because of Longhorn. Currently, these are housed in 2 of 3 nodes. Still looking to get another one and pop it in PVE1.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/hp_1200px.jpg" alt="Intel DC S3510 SSDs">
      <figcaption>Intel DC S3510 SSDs in PVE2 and PVE3</figcaption>
    </figure>
  </p>
<h2 id="one-container-to-rule-them-all">One container to rule them all</h2>
<p>Both the Terraform and Ansible controllers are running from a LXC container deployed on PVE1. This is where I do all of the development work. I am running a docker instance of VS code on the same container which enables me to create and modify code easily. The same container is used to communicate with the production environment.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/portainer5.png" alt="Portainer as Ansible and Terraform control node">
      <figcaption>Portainer LXC as Ansible and Terraform control node</figcaption>
    </figure>
  </p>
<p>One of the best advantages of using an LXC container is that it&rsquo;s so lightweight you can easily backup the environment anytime. I confirm Terraform and Ansible works well with the Ubuntu 22.04 template that comes with Proxmox.</p>
<h2 id="terraform-and-ansible">Terraform and Ansible</h2>
<p>Learning Terraform wasn&rsquo;t so bad. The Proxmox provider documentation from Telmate is enough to get you started and with the declarative style that is used to write Terraform language, it just makes the learning process a lot more easier to deal with. This is also the part where I had the least modifications and time spent the least.</p>
<p>Once I got the VMs up and working, next thing I worked on is Ansible. Now this is the part where I spent the biggest chunk of my time. I literally am unable to count the times I had to execute <code>terraform destroy</code> and <code>terraform apply</code> to re-create the VMs and test out my Asnbile playbooks. To be fair starting with Ansible wasn&rsquo;t really hard. It was the amount of automation I wanted to go with that later on will prove useful. Though I have to admit the playbooks are rather simple and which others might find lacking in terms of best practices. But hey, I have to start from somewhere!</p>
<p>The five commands to have a complete working k3s cluster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">terraform apply
</span></span><span class="line"><span class="cl">ansible-playbook -i inventory.yaml preflight.yaml
</span></span><span class="line"><span class="cl">ansible-playbook -i inventory.yaml logical-volume-create.yaml
</span></span><span class="line"><span class="cl">ansible-playbook -i inventory.yaml k3s-kubevip-helm-ciliumInstallHelmCli.yaml
</span></span><span class="line"><span class="cl">ansible-playbook -i inventory.yaml longhorn-install.yaml
</span></span></code></pre></div><p>The whole process takes about more or less 15 minutes. Terraform creates 3 VMs, one on each proxmox node. Each VM is set 4 vCPU, 12GB RAM, 50GB boot disk + 200GB longhorn disk. <code>preflight.yaml</code> defines the ssh keys for passwordless authentication and installs the necessarry packages to run k3s-related software. <code>logical-volume-create.yaml</code> as its name suggests, creates the logical volume to be used for longhorn. <code>k3s-kubevip-helm-ciliumInstallHelmCli.yaml</code> installs k3s, kube-vip, helm, and Cilium altogether sequentially. And last but not the least, <code>longhorn-install.yaml</code> installs longhorn via helm.</p>
<h2 id="cilium">Cilium</h2>
<p>While Calico would be the go-to CNI for most, I opted to go for Cilium. The main reason for this is to start learning eBPF and have a grasp how things move within the kernel space. The installation was straightforward but to get it working in properly was a challenge.</p>
<p>I was able to make BGP control plane work during the initial phase and when I thought everything was already stable, I then started seeing BGP peers getting dropped and at some point even lost connectvivity to my DNS hosted in the cluster. After adding into the parameters one by one, I was able to make it work without disconnects.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">helm install cilium cilium/cilium --version 1.14.2 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--namespace kube-system <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set bgpControlPlane.enabled<span class="o">=</span><span class="nb">true</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">tunnel</span><span class="o">=</span>disabled <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set ipam.operator.clusterPoolIPv4PodCIDRList<span class="o">=</span>10.42.0.0/16 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">kubeProxyReplacement</span><span class="o">=</span><span class="nb">true</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">k8sServiceHost</span><span class="o">={{</span> _k8sServiceHost <span class="o">}}</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">k8sServicePort</span><span class="o">=</span><span class="m">6443</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">routingMode</span><span class="o">=</span>native <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">autoDirectNodeRoutes</span><span class="o">=</span><span class="nb">true</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">ipv4NativeRoutingCIDR</span><span class="o">=</span>10.42.0.0/16 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set loadBalancer.mode<span class="o">=</span>dsr <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set ipv4.enabled<span class="o">=</span><span class="nb">true</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set prometheus.enabled<span class="o">=</span><span class="nb">true</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set operator.prometheus.enabled<span class="o">=</span><span class="nb">true</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set hubble.enabled<span class="o">=</span><span class="nb">true</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set hubble.metrics.enabled<span class="o">=</span><span class="s2">&#34;{dns,drop,tcp,flow,port-distribution,icmp,http}&#34;</span>
</span></span></code></pre></div><p><code>kubeProxyReplacement</code> is enabled to make use of eBPF instead of the traditional way of iptables. <code>loadBalancerMode</code> is also set to DSR making it possible for nodes to respond directly to the sender, avoiding the need to go via the return path. <code>routingMode</code> set to native and <code>autoDirectNodeRoutes</code> is set to true since all nodes are connected via the same L2 network. You can read more on Cilium routing <a href="https://docs.cilium.io/en/stable/network/concepts/routing/#routing">here</a>. Prometheus and Hubble are also enabled so I can touch on them later once I get more time.</p>
<p><strong>cilium-bgp-peering-policy.yaml</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2alpha1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumBGPPeeringPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="m">00</span>-<span class="l">bgp-peering-policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span><span class="c"># CiliumBGPPeeringPolicySpec</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">nodeSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kubernetes.io/hostname</span><span class="p">:</span><span class="w"> </span><span class="l">k8s-master-0-dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">virtualRouters</span><span class="p">:</span><span class="w"> </span><span class="c"># []CiliumBGPVirtualRouter</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">localASN</span><span class="p">:</span><span class="w"> </span><span class="m">65090</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">exportPodCIDR</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">serviceSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">exposedExternal</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;yes&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">neighbors</span><span class="p">:</span><span class="w"> </span><span class="c"># []CiliumBGPNeighbor</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">peerAddress</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;10.20.0.1/32&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">peerASN</span><span class="p">:</span><span class="w"> </span><span class="m">65000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">eBGPMultihopTTL</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">connectRetryTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">120</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">holdTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">90</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">keepAliveTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">30</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">gracefulRestart</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">restartTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">120</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2alpha1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumBGPPeeringPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="m">01</span>-<span class="l">bgp-peering-policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span><span class="c"># CiliumBGPPeeringPolicySpec</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">nodeSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kubernetes.io/hostname</span><span class="p">:</span><span class="w"> </span><span class="l">k8s-master-1-dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">virtualRouters</span><span class="p">:</span><span class="w"> </span><span class="c"># []CiliumBGPVirtualRouter</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">localASN</span><span class="p">:</span><span class="w"> </span><span class="m">65091</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">exportPodCIDR</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">serviceSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">exposedExternal</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;yes&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">neighbors</span><span class="p">:</span><span class="w"> </span><span class="c"># []CiliumBGPNeighbor</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">peerAddress</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;10.20.0.1/32&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">peerASN</span><span class="p">:</span><span class="w"> </span><span class="m">65000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">eBGPMultihopTTL</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">connectRetryTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">120</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">holdTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">90</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">keepAliveTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">30</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">gracefulRestart</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">restartTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">120</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;cilium.io/v2alpha1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumBGPPeeringPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="m">02</span>-<span class="l">bgp-peering-policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span><span class="c"># CiliumBGPPeeringPolicySpec</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">nodeSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kubernetes.io/hostname</span><span class="p">:</span><span class="w"> </span><span class="l">k8s-master-2-dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">virtualRouters</span><span class="p">:</span><span class="w"> </span><span class="c"># []CiliumBGPVirtualRouter</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">localASN</span><span class="p">:</span><span class="w"> </span><span class="m">65092</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">exportPodCIDR</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">serviceSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">exposedExternal</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;yes&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">neighbors</span><span class="p">:</span><span class="w"> </span><span class="c"># []CiliumBGPNeighbor</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">peerAddress</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;10.20.0.1/32&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">peerASN</span><span class="p">:</span><span class="w"> </span><span class="m">65000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">eBGPMultihopTTL</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">connectRetryTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">120</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">holdTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">90</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">keepAliveTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">30</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">gracefulRestart</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">restartTimeSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">120</span><span class="w">
</span></span></span></code></pre></div><p>As for configuring BGP on OPNsense, all I had to do was download the <code>os-frr</code> plugin and apply the configuration according to the Cilium BGP resources.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/opnsense.png" alt="OPNsense BGP neighbor configuration">
      <figcaption>OPNsense BGP neighbor configuration</figcaption>
    </figure>
  </p>
<p>Once BGP is configured, I then configured <code>CiliumLoadBalancerIPPool</code> and configured the <code>serviceSelector</code> there so any service with a matching label will be assigned an external IP.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">cilium.io/v2alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CiliumLoadBalancerIPPool</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">externalpool</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cidrs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">cidr</span><span class="p">:</span><span class="w"> </span><span class="m">192.168.100.0</span><span class="l">/27</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">disabled</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">serviceSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">exposedExternal</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;yes&#34;</span><span class="w">
</span></span></span></code></pre></div><p>To assign services with external IPs, all you have to do is ensure two things (third one is optional):</p>
<ol>
<li>The service should have a label matching what you defined in your <code>CiliumLoadBalancerIPPool</code>.</li>
<li>The service should be of type <code>LoadBalancer</code>.</li>
<li>For static external IP assignment, the service should have an annotation of <code>io.cilium/lb-ipam-ips</code> followed by the IP address.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">io.cilium/lb-ipam-ips</span><span class="p">:</span><span class="w"> </span><span class="m">192.168.100.7</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">exposedExternal</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;yes&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">adguard-ui</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">adguard</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">LoadBalancer</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">...</span><span class="w">
</span></span></span></code></pre></div><p>When all of the above are applied, you should now see an external IP assigned to your service.
e.g.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">❯ k -n adguard get svc
</span></span><span class="line"><span class="cl">NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
</span></span><span class="line"><span class="cl">adguard      LoadBalancer   10.43.254.122   192.168.100.8   53:30176/UDP   27d
</span></span><span class="line"><span class="cl">adguard-ui   LoadBalancer   10.43.38.101    192.168.100.7   80:32108/TCP   27d
</span></span></code></pre></div><p>You should also be able to check the BGP peering status as well as the learned routes in OPNsense.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/opnsense_2.png" alt="BGP peering status">
      <figcaption>OPNsense BGP status</figcaption>
    </figure>
  </p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/opnsense_3.png" alt="BGP routes">
      <figcaption>OPNsense BGP routes</figcaption>
    </figure>
  </p>
<h2 id="longhorn">Longhorn</h2>
<p>Before deciding to go for Longhorn, I was was trying to make <a href="https://piraeus.io/">Piraeus</a> datastore work (FOSS version of Linstor storage for Kubernetes). I got it to work with ReadWriteOnce but the moment I tried to test ReadWriteMany(RWX), it just wouldn&rsquo;t. On top of this it also felt that there was a steep learning curve to understand how Piraeus work on a deeper level in case I had to do extra troubleshooting in the future.</p>
<p>Longhorn on the other hand worked well out of the box. Testing out RWX by re-creating a pod on a different node worked well too and since Longhorn seems to use NFS to support this feature, accessing the volume from something external to the cluster e.g. from a VM works out of the box. The only thing is that there seems to be an <a href="https://github.com/cilium/cilium/issues/21541">open issue with Cilium</a> when exposing the volume externally. When I try to mount the share to a VM, I do experience slowdowns when opening a file with vim or even when just browsing through the directories.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/longhorn.png" alt="OPNsense BGP neighbor configuration">
      <figcaption>Longhorn Dashboard</figcaption>
    </figure>
  </p>
<p>Longhorn makes use of a 200Gi second volume that was declared in the Terraform script. The above snapshot shows the amount of available volume.</p>
<p>For reference, below is the playbook task to install longhorn:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">install longhorn</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">kubernetes.core.helm</span><span class="p">:</span><span class="w"> 
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">longhorn</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">chart_ref</span><span class="p">:</span><span class="w"> </span><span class="l">longhorn/longhorn</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">release_namespace</span><span class="p">:</span><span class="w"> </span><span class="l">longhorn-system</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">create_namespace</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">update_repo_cache</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">set_values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">service.ui.type=LoadBalancer</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">defaultSettings.defaultDataPath=/longhorn_vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">defaultSettings.defaultReplicaCount=3</span><span class="w">
</span></span></span></code></pre></div><h2 id="exposing-services">Exposing services</h2>
<p>I took more time than expected when I was trying to expose the services with Traefik now running in Kubernetes. In the docker setup, the configuration was pretty straightforward with minimal reading required of the documentation. Whereas when running treafik in k8s, it came to the point that it already felt like I was digging my own grave with all the research and testing.</p>
<p>Eventually I got it to work by going with the base installation and slowly inching my way through the custom values in the yaml file. Once I got the middleware (for additional security headers) and TLS working via cert-manager, all I had to do was create individual ingress resources for each of the services I wanted to expose. The certificates are automatically created and managed by Cert-manager.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/traefik_2.png" alt="Network diagram">
      <figcaption>HTTP flow with Traefik and Cert-Manager</figcaption>
    </figure>
  </p>
<p>One thing to note, in Docker, the certificates can be managed by Traefik. But when using Traefik in a K8s environment, to make use of Let&rsquo;s Encrypt, the only option is to use Cert-manager which can only be paired up with the default Kubernetes Ingress resource. Traefik&rsquo;s Ingress CRD doesn&rsquo;t support this at the moment.</p>
<p>Below is a sample Ingress resource to reach the Adguard GUI from external to the cluster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">networking.k8s.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Ingress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">adguard-ui</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">adguard</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="nt">cert-manager.io/cluster-issuer</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;letsencrypt-cluster-issuer&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">tls</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span>- <span class="nt">hosts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span>- <span class="l">adguard.su-root.net</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="nt">secretName</span><span class="p">:</span><span class="w"> </span><span class="l">tls-adguard-ui-ingress-dns</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span>- <span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">adguard.su-root.net</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="nt">http</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span>- <span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">           </span><span class="nt">pathType</span><span class="p">:</span><span class="w"> </span><span class="l">Prefix</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">           </span><span class="nt">backend</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">             </span><span class="nt">service</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">               </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">adguard-ui</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">               </span><span class="nt">port</span><span class="p">:</span><span class="w"> 
</span></span></span><span class="line"><span class="cl"><span class="w">                 </span><span class="nt">number</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span></code></pre></div><p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/adguard.png" alt="Traefik UI">
      <figcaption>Adguard UI with valid CA certificate from LE</figcaption>
    </figure>
  </p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/traefik_ui.png" alt="Traefik UI">
      <figcaption>Traefik Dashboard</figcaption>
    </figure>
  </p>
<p>
    <figure>
      <img loading="lazy" src="/posts/re-engineering-the-homelab-with-iac-and-kubernetes-an-overview/traefik_ui2.png" alt="Traefik UI">
      <figcaption>Routers-Services Mapping in Traefik Dashboard</figcaption>
    </figure>
  </p>
<h2 id="the-journey-continues">The journey continues</h2>
<p>The learning doesn&rsquo;t stop here. What I have achieved so far is a basic understanding of how IaC integrates and can be made to work in different environments depending on the requirement. Another good thing is that this adds up to my confidence knowingly I can spin up my cluster in a matter of minutes even in the event that I have to physically migrate to another environment.</p>
<p>Going further I will continue to enhance the Ansible playbooks and try to make use of industry best practices even if this is only intended for Homelab use. I&rsquo;m also looking into integrating this with some kind of CICD tool like Jenkins or ArgoCD in the near future.</p>
<p>If you are interested to see more of this project, feel free to check out the repository over at my <a href="https://github.com/luifrancisco/k3s-ha">Github page</a>. A disclaimer though, the README is not updated yet! I will be updating this sooner or later and together with that will try to explain in detail the idea behind each step of the installation process.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
