<?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>Posts on Luis Logs</title>
    <link>https://luislogs.com/posts/</link>
    <description>Recent content in Posts on Luis Logs</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 30 Mar 2026 19:45:30 +0900</lastBuildDate><atom:link href="https://luislogs.com/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>OpenClaw and LLMs: Building my tool stack for testing and analyzing network performance</title>
      <link>https://luislogs.com/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/</link>
      <pubDate>Mon, 30 Mar 2026 19:45:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/</guid>
      <description>Consumer use of AI has been around for a few years now. While I use it daily at work, at home I&amp;rsquo;ve never really had the chance to play with it for a couple of reasons. First, priorities have shifted to allow more time with family. Second, I&amp;rsquo;ve always told myself I would only start heavy use of AI if I could self-host LLMs. But times have changed (and so have RAM and GPU prices!</description>
      <content:encoded><![CDATA[<p>Consumer use of AI has been around for a few years now. While I use it daily at work, at home I&rsquo;ve never really had the chance to play with it for a couple of reasons. First, priorities have shifted to allow more time with family. Second, I&rsquo;ve always told myself I would only start heavy use of AI if I could self-host LLMs. But times have changed (and so have RAM and GPU prices!). With the recent price hike in PC hardware (ironically driven by AI, lol), I no longer find it economical to go the self-hosted route, especially if I&rsquo;ll only be using it for general purposes like researching and light coding on weekends.</p>
<p>So last week, I once and for all let go of that mindset and started deploying OpenClaw. OpenClaw has been a hot topic in the tech space since the beginning of this year. It&rsquo;s been all over the internet —on every tech news site, every forum, and every computer-related reddit thread. To be honest, I mostly ignored the articles I saw, though I did google it once out of curiosity. But the AI trend was real, and fast forward to today —I&rsquo;ve managed to convert my shell scripts into a complete end-to-end stack of tools for measuring and analyzing network performance.</p>
<h2 id="openclaw">OpenClaw</h2>
<p>If you&rsquo;re not already aware, OpenClaw is an AI agent that can interact directly with your files, browse the web, execute shell commands (and yes, even create cron jobs!), read your entire email inbox (and respond when you like), manage your calendar, and more. I haven&rsquo;t gone too far with it yet, but I&rsquo;ve been using it for light coding —converting shell scripts and assembling the full stack of tools needed to publish network test results to a database with visualization.</p>
<p>There are many concerns on security and the extensive permissions it&rsquo;s been granted to do almost anything. But from my perspective, OpenClaw is open-source anyway, and I do trust the open-source community. That doesn&rsquo;t mean I don&rsquo;t value security. But I believe anyone who wants and is able to deploy the software, should also have the capability to assess how much data and what level of confidentiality they are willing to risk. Fo my use case of light coding and research, it fits the bill!</p>
<p>That being said, there are also plenty of recommendations online to have it running on an isolated environment such as a VPS or separate machine. For some people, they have it deployed on the base model of a Mac mini. In my environment, it&rsquo;s deployed on my Proxmox cluster, and on an isolated VLAN where only access to the internet and another VM (sandbox) are allowed. I haven&rsquo;t faced issues allocating 2 CPU and 4GB RAM for it, but I would suggest having sufficient disk space in case you intend to build applications within the same environment.</p>
<p>
    <img src="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/oc2.png" alt="OpenClaw">
  </p>
<p>Openclaw is just the agent —a carriage without a horse. I still needed a model that will do the thinking and suggest the next actions. There was an option to use the subscription Oauth token from Claude or Gemini, but the companies behind them started cracking down on users who use this approach insteading of paying for the API access. So I hopped onto <a href="https://openrouter.ai/rankings">Openrouter&rsquo;s rankings</a> and found Step 3.5 Flash (Free) sitting there on the Top 10. Since then I&rsquo;ve been using the same model and even signed up for the actual API subscription from the company&rsquo;s official website after exhausting the limits from Openrouter.</p>
<h2 id="design">Design</h2>
<p>The project idea was very simple and there&rsquo;s nothing special about the design. It follows the same flow as any other download speed test app: users perform the test, results are sent to an API endpoint. But I&rsquo;m a sucker for visually appealing diagrams, so I made one anyway!</p>
<p>
    <img src="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/design2.png" alt="Call flow and design">
  </p>
<p>The infrastructure already existed. The only new components here are OpenClaw, the sandbox environment, and the applications —the Android app, FastAPI, PostgreSQL, and Apache Superset. During development, I had OpenClaw access the sandbox environment to polish the configuration. FastAPI, PostgreSQL, and Superset were all initially deployed on a single VM, but the plan from the start for the &ldquo;prod&rdquo; setup was to isolate them in containers. FastAPI wasn&rsquo;t dockerized yet, so it was deployed in an LXC container. This will definitely be migrated to the k8s cluster, but for now I just wanted to beat the clock and get something working quickly. Superset, from the beginning, was already running in Docker. However, I wanted more processing horsepower for a snappy visualization experience —and for that, only my Unraid host was suitable. As for PostgreSQL, it&rsquo;s deployed in my k8s cluster using the <a href="https://github.com/cloudnative-pg/charts">cnpg helm chart</a> with a Ceph RBD backend storage.</p>
<h2 id="building-the-apk-and-bringing-life-into-my-shell-scripts">Building the APK and bringing life into my shell scripts</h2>
<p>I&rsquo;m not a developer, but I can work with shell scripts. Before all this my tool set consisted of separate shell scripts I made in the past. My challenge is that not everyone understands shell, and it&rsquo;s quite difficult to keep explaining to anyone who wants to perform tests how to execute shell scripts from a mobile phone. Performance-wise, for most of the time they did a good job. But there were times when one of the tests would deviate a little from the expectation, and I would only find out after analyzing the trace. The idea was to have an app that will run natively on the device&rsquo;s OS that brings along a whole different ball game of flexibility and usability. Then came the idea of building an actual app in Android Studio with the help of AI. I definitely hit the API limits of Gemini and Claude for this one. So the first project I used OpenClaw for was to assist in reviewing and modifying the code. The app is written in Kotlin, and its main goal is to perform network throughput tests. The conditions are simple:</p>
<ol>
<li>There are three different types of test: download throughput, TCP latency, and UDP latency.</li>
<li>For download test, user should be able to specify the download URL.</li>
<li>User should also be able to specify how many URLs it wants to be part of the download test.</li>
<li>For UDP and TCP latency test, user should be able to specify the IP address or FQDN.</li>
<li>There should be an option to group these tests and perform multiple iterations.</li>
<li>Network conditions are necessary to be captured at the beginning of every test.</li>
<li>Timestamps are to be captured as well before and after each test.</li>
<li>There should be a field to specify an API URL endpoint.</li>
<li>At the end of every global iteration, the test results should be captured in JSON format and should be sent to the specified API URL.</li>
<li>There should also be an option to save the JSON output locally.</li>
<li>The start button should be greyed out whenever there is an ongoing test.</li>
</ol>
<p>There are more conditions specific to each test but I&rsquo;ll save that for later.</p>
<h3 id="antt-automated-network-testing-tool">ANTT: Automated Network Testing Tool</h3>
<p>I was working on this during my free time at home, and, after a few days, I was able to tick off everything in the checklist! You can see the sample snaps of the app below:</p>




	




































  	
	

	
		<script src="/shortcode-gallery/jquery-3.7.0.min.js"></script>
	
	
	
		<script src="/shortcode-gallery/lazy/jquery.lazy.min.js"></script>
	

	<script src="/shortcode-gallery/swipebox/js/jquery.swipebox.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/swipebox/css/swipebox.min.css">

	<script src="/shortcode-gallery/justified_gallery/jquery.justifiedGallery.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/justified_gallery/justifiedGallery.min.css"/>


<style>
	

	
</style>





<div id="gallery-76dbf13cb5a652869faea4dc43257a5f-0-wrapper" class="gallery-wrapper">
<div id="gallery-76dbf13cb5a652869faea4dc43257a5f-0" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/antt/antt_1.png" 
					class="galleryImg"
					
					>
					<img			
						width="277" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAADwMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APMJB5iDBK4A4ycVDhgCoY4z&#43;BrYS/sFgQNoNo7Koyxkmyxx164pst7Yz2sipotnbOMYcyS7uozjnH/6615V3MeZ9vyM1mwBuiGcdxj&#43;tNLAjHlge/P&#43;NKXcYVWB&#43;lNMjngt&#43;FSWf//Z"
							
							class="lazy"
							data-src="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/antt/antt_1_hu9e1daa5775493e7287df7da528e8858a_137924_600x600_fit_q100_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/antt/antt_2.png" 
					class="galleryImg"
					
					>
					<img			
						width="277" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAADwMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APL2jkKj5sqegJxTSjqhGRt7jdWkl1o6wpv0V3YKNzfbsbj3ONvFI8&#43;lT20vkaS0Ei4w7XvTnsCvNa8q7mPM&#43;35f5j0v9MSFA&#43;jbyqgMwvGG4&#43;uO1NkutMuLeXydJSCQYIdrtievYHrTl1LTY440OjRSsFALLdS8n1xniobjUdNkidYtEjhkPST7RIxXn0Jqr&#43;a&#43;7/gEW12f3/8ABP/Z"
							
							class="lazy"
							data-src="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/antt/antt_2_hu5f1e55791598660486f37e580d91a28a_136386_600x600_fit_q100_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/antt/antt_3.png" 
					class="galleryImg"
					
					>
					<img			
						width="277" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAADwMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APP2sozZxyySx8gYjEoU/UhiKz7qNYSFjkyrckBlI/RjVz7fOtkbYPEiuvPy8n8c1mlSBnI/AirILJD7QwyBgdnqKRmC8/zb&#43;tJ5zsAuM&#43;3JqMk55oA//9k="
							
							class="lazy"
							data-src="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/antt/antt_3_hue0fbf45062784f80e47c8ae01f16892d_264179_600x600_fit_q100_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-76dbf13cb5a652869faea4dc43257a5f-0");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<p> </p>
<h3 id="json-output">JSON output</h3>
<p>The app has the feature to run multiple iterations so there are two UUIDs generated. The true UUID across all tests is printed as the first key-value pair. The secondary UUID or run_id is only unique for each press of the start button. It will persist on multiple iterations. Timestamps are also recorded before and after every iteration for easier mapping of traces. The rest are pretty much self-explanatory.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;uuid&#34;</span><span class="p">:</span> <span class="s2">&#34;d81ba2d4-a21e-4da8-9657-f2f2fdc42366&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;run_id&#34;</span><span class="p">:</span> <span class="s2">&#34;8a6f53de-42f4-472b-95aa-c60f07db6f88&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;iteration&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;timestamp_start&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-03-28T20:57:19&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;timestamp_end&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-03-28T20:57:38&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;location&#34;</span><span class="p">:</span> <span class="s2">&#34;36.11950402, 140.00541702&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_Timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-03-28T20:57:19&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_Coordinates&#34;</span><span class="p">:</span> <span class="s2">&#34;40.61950402, 123.60541702&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_Connection&#34;</span><span class="p">:</span> <span class="s2">&#34;4G/LTE&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_IPv4&#34;</span><span class="p">:</span> <span class="s2">&#34;172.16.0.31&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_IPv6&#34;</span><span class="p">:</span> <span class="s2">&#34;2020:dead:beef:188d:cd48:3c9e:e489:2cd&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_MSISDN&#34;</span><span class="p">:</span> <span class="s2">&#34;N/A&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_IMSI&#34;</span><span class="p">:</span> <span class="s2">&#34;N/A (Restricted)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_Operator&#34;</span><span class="p">:</span> <span class="s2">&#34;REDACTED&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_Cell_ID&#34;</span><span class="p">:</span> <span class="s2">&#34;71989000&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_RSRP&#34;</span><span class="p">:</span> <span class="s2">&#34;-89 dBm&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;netinfo_RSRQ&#34;</span><span class="p">:</span> <span class="s2">&#34;-7 dB&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest1_url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://jiyugaoka.ensaymada.net/800MB.bin&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest1_streams&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest1_downloaded_mb&#34;</span><span class="p">:</span> <span class="mi">35</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest1_avg_mbps&#34;</span><span class="p">:</span> <span class="mf">28.93510269426006</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest1_peak_mbps&#34;</span><span class="p">:</span> <span class="mf">70.07809900990098</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest2_url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://jiyugaoka.ensaymada.net/3MB.bin&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest2_streams&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest2_downloaded_mb&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest2_avg_mbps&#34;</span><span class="p">:</span> <span class="mf">21.3559266802444</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest2_peak_mbps&#34;</span><span class="p">:</span> <span class="mf">38.55058823529412</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest3_url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://jiyugaoka.ensaymada.net/1MB.bin&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest3_streams&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest3_downloaded_mb&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest3_avg_mbps&#34;</span><span class="p">:</span> <span class="mf">20.140715486194477</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest3_peak_mbps&#34;</span><span class="p">:</span> <span class="mf">27.252594059405936</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest4_url&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest4_streams&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest4_downloaded_mb&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest4_avg_mbps&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest4_peak_mbps&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest5_url&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest5_streams&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest5_downloaded_mb&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest5_avg_mbps&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadtest5_peak_mbps&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;tcptest_avg_ms&#34;</span><span class="p">:</span> <span class="mf">37.4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;tcptest_median_ms&#34;</span><span class="p">:</span> <span class="mi">37</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;tcptest_min_ms&#34;</span><span class="p">:</span> <span class="mi">34</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;tcptest_max_ms&#34;</span><span class="p">:</span> <span class="mi">41</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;udptest_success_percent&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;udptest_avg_ms&#34;</span><span class="p">:</span> <span class="mf">33.96</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;udptest_median_ms&#34;</span><span class="p">:</span> <span class="mi">32</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;udptest_min_ms&#34;</span><span class="p">:</span> <span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;udptest_max_ms&#34;</span><span class="p">:</span> <span class="mi">81</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="fastapi-postgresql-and-apache-superset-for-visualization">FastAPI, PostgreSQL, and Apache Superset for visualization</h2>
<p>I heard about fastAPI at the time of building a static website for another automation project. From a beginner&rsquo;s standpoint, the learning curve wasn&rsquo;t really that steep. Or maybe it&rsquo;s easy to say that because my use cases were pretty simple. But overall dealing with FastAPI was pretty straight forward. You define the paths and the corresponding python functions you want to call. It was a fairly easy task for OpenClaw to create the required files to parse the JSON data and publish to a postgres backend.</p>
<p>Talking about Postgres, as I mentioned earlier, the CNPG helm chart was used to deploy this in my k8s cluster. Though it was deployed through ArgoCD since I always keep practice of infrastructure-as-code. First I had to deploy the cnpg operator. Below is a sample snip of the ArgoCD manifest:</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">argoproj.io/v1alpha1</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">Application</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">cnpg</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">argocd</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">destination</span><span class="p">:</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">cnpg-system</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</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">sources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;https://cloudnative-pg.github.io/charts&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">chart</span><span class="p">:</span><span class="w"> </span><span class="l">cloudnative-pg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="m">0.27.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">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">releaseName</span><span class="p">:</span><span class="w"> </span><span class="l">cnpg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">valueFiles</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="l">$values/cnpg/values.yaml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:mygituser/tokyo-prod.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="l">HEAD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">ref</span><span class="p">:</span><span class="w"> </span><span class="l">values</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:mygituser/tokyo-prod.git</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">cnpg/manifests</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="l">HEAD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CreateNamespace=true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">ServerSideApply=true</span><span class="w">
</span></span></span></code></pre></div><p>Then the actual cnpg 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">argoproj.io/v1alpha1</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">Application</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">cnpg-cluster-dev</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">argocd</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">destination</span><span class="p">:</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">cnpg-cluster-dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</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">sources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:mygituser/tokyo-prod.git</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">cnpg-cluster-dev/manifests</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="l">HEAD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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">selfHeal</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">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CreateNamespace=true</span><span class="w">
</span></span></span></code></pre></div><p>As for Apache Superset, this might be a little bit overkill for the job but it&rsquo;s still nice to have nevertheless. I faced a few hiccups here and there but so far there&rsquo;s nothing AI couldn&rsquo;t fix for this setup. What I actually did was demand OpenClaw to have Superset docker instance running and gave a requirement to have the compose files to connect to my remote postgresql database. It asked for the connection information then proceeded to create the yaml file. It also identified that the setup required the installation of required packages to connect to a postgres backend so it also provided a Dockerfile.</p>




	





































<style>
	

	
</style>





<div id="gallery-76dbf13cb5a652869faea4dc43257a5f-1-wrapper" class="gallery-wrapper">
<div id="gallery-76dbf13cb5a652869faea4dc43257a5f-1" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/ss/superset.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="228"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAAwAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APN7bTru&#43;tTPAmQCVJ3AchS3f2BqiOpBZj6VbstYurG2MEOzYzFjuXJyVK/yJqvcXLXFy8xSNCwHyxrtUfQVT5baELmu77DMYIyeDSqcJu3AFuxBJH6Uwscj60E8VJZ//9k="
							
							class="lazy"
							data-src="/posts/openclaw-and-llms-building-my-tool-stack-for-analyzing-network-performance/ss/superset_hu401a3838d75823e8a848dd38bbcec7a5_215066_600x600_fit_q100_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-76dbf13cb5a652869faea4dc43257a5f-1");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<p> </p>
<p>From within the superset dashboard, the queries were failing at first. After sharing the error message to OpenClaw, it then identified a minor misconfiguration on the RedisDB. I was surprised it proactively tried to fix the configuration &ndash; restarting the containers as well. For every change and restart, it also verified whether the error has been cleared and if the query would function. After a few container restarts, a bit more of modification requests, everything was working perfectly.</p>
<p>Since it performed the changes proactively, it was a pain to keep track of them from the chat window. I asked it to create a README.md file of all the changes it made with a short simple description for the reason. It did a pretty good job covering everything from the creation of the custom Docker image until the SQL data type revisions I had towards the end.</p>
<h2 id="the-wrap-up-and-whats-in-the-roadmap">The wrap up and what&rsquo;s in the roadmap</h2>
<p>The past few late night work I&rsquo;ve had for this project was all filled in with curiousity and excitement. Every press of the enter button for each prompt always had me thinking if the agent would give proper results. It did and even exceeded my expectations! Those times I would share the error message asking for a fix, it performed proactively without asking, and verifying whether the changes worked. When it hit a new error during verification, it looped back to trying to find the next solution. Yes it&rsquo;s pretty scary especially if you can&rsquo;t keep track of the changes in real time. Definitely, a risk. But really, it just works. It&rsquo;s up to the user how to manage this. And apart from version control, there should already be common and general practices how to deal with this. Also, Openclaw is not just the only autonomous agent out there. There are other options like nanobot built with a smaller footprint of python that can be easier for the user to understand and perform modifications on top of. For those cocnerned with privacy, then there&rsquo;s also the self-hosted option.</p>
<p>It&rsquo;s just the start, but I can&rsquo;t wait to see what else and others can build with AI!</p>
<p>Talking about the app and the complete stack, these are in the very initial phase. However, there are still a few things I want to cover later on. Off the top of my head:</p>
<ol>
<li>Containerize FastAPI and migrate it to the Kubernetes cluster.</li>
<li>Maybe migrate Superset to the Kubernetes cluster as well.</li>
<li>Run tcpdump while tests are running (not even sure if this is possible, lol)</li>
<li>Add the ability to import and save configuration (maybe using XML).</li>
<li>Add a scheduling or interval feature for the global iteration loop.</li>
<li>Add authentication to the API requests.</li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>Upgrading our home internet to 10 Gbps</title>
      <link>https://luislogs.com/posts/upgrading-our-home-internet-to-10-gbps/</link>
      <pubDate>Mon, 23 Mar 2026 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/upgrading-our-home-internet-to-10-gbps/</guid>
      <description>I have finally decided to move our internet subscription up from 1Gbps to 10Gbps. While 1G is already more than sufficient for home use, speed is not all there is. Beyond just raw speed, our old setup was held back by the overhead of PPPoE and the debatable limitation it brings when paired with a FreeBSD-based router OS like OPNsense. The new connection uses IPoE (IP over Ethernet) which doesn&amp;rsquo;t have the same hiccups the former is known to have.</description>
      <content:encoded><![CDATA[<p>I have finally decided to move our internet subscription up from 1Gbps to 10Gbps. While 1G is already more than sufficient for home use, speed is not all there is. Beyond just raw speed, our old setup was held back by the overhead of PPPoE and the debatable limitation it brings when paired with a FreeBSD-based router OS like OPNsense. The new connection uses IPoE (IP over Ethernet) which doesn&rsquo;t have the same hiccups the former is known to have.</p>
<p>Second, the old subscription only had IPv4. Don&rsquo;t get me wrong, IPv4 isn&rsquo;t a bad thing. In fact, a good majority of the internet still runs on IPv4. But recently, I&rsquo;ve gained interest in exploring the capabilities and other features that come with IPv6. More on this later, but the main trigger point for this is because IPv6 is already widely used in Japan. If I&rsquo;m not mistaken, IPv6 adoption has been enforced by the Japan Ministry of Communications for some time now.</p>
<p>Third, since the beginning of last year, I&rsquo;ve taken on a new role focused on optimizing customer experience performance, where speed is one of the main concerns. To better support this and overcome the limitations of my current toolset, having a speed test server (with multi-gigabit bandwidth) that I have full control of is a fundamental first step!</p>
<h2 id="hardware">Hardware</h2>
<p>Certain Lenovo Tiny PCs are popular machines for having a PCIe slot that can be fitted with 10G NICs, so that&rsquo;s what I went with. I was able to get a secondhand one with an Intel i5-8500T 6C CPU. This should be more than enough for my needs, even if I wanted to host other services on this machine. As for the NIC, I had the option of going with Mellanox or Intel ones. To be more specific, I was choosing between a Mellanox ConnectX-4 and an Intel X710-DA2. Mellanox seems to be more popular in terms of compatibility and stability, but it&rsquo;s also known to have elevated power consumption since it&rsquo;s not able to achieve higher C-states even when idle. The X710-DA2, on the other hand, was known to have compatibility issues, especially when using the OEM-branded ones. I went with Intel to have peace of mind knowing that I tried my best to save electricity costs (as if all the other machines in the homelab justify all the power they require!).</p>




	




































  	
	

	
		<script src="/shortcode-gallery/jquery-3.7.0.min.js"></script>
	
	
	
		<script src="/shortcode-gallery/lazy/jquery.lazy.min.js"></script>
	

	<script src="/shortcode-gallery/swipebox/js/jquery.swipebox.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/swipebox/css/swipebox.min.css">

	<script src="/shortcode-gallery/justified_gallery/jquery.justifiedGallery.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/justified_gallery/justifiedGallery.min.css"/>


<style>
	

	
</style>





<div id="gallery-0efe21febd1ec25b8e9ee6c88aecf11b-0-wrapper" class="gallery-wrapper">
<div id="gallery-0efe21febd1ec25b8e9ee6c88aecf11b-0" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/lenovo_nic/1_lenovo.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 6.765mm f/1.78<br/>6.764999865652793mm f/1.7799999713880652 1/25sec ISO 500"
						

						
					
					>
					<img			
						width="600" height="450"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AEmYDJP4Vk3PLVrDX7Z7WWwkE1osjBnUAHkeu4A4/GqUtnHPzbXkMmeitlGP4Gs7FXMlxgVDnBq9c2V3Bnzbd1A74yPzqiwGaYHZahCkjFZY1fHZh0rDksY42/dFoxkEgHIOO3Pauh1D/j5f/eNZE3ekV5lSCa6snzFKxT&#43;5uIH5UXl8t1C6NZRGRhhZMYYH1yKG6GoT/rBVXJe9z//Z"
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/lenovo_nic/1_lenovo_huc60cc3890e3e167b3b80c1e9ea5fec25_235863_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/lenovo_nic/7_nic1.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 2.22mm f/2.2<br/>2.220000028611935mm f/2.2 1/50sec ISO 160"
						

						
					
					>
					<img			
						width="600" height="336"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABIAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AK9xr8SsY7QedJ65wo/x/Cqerw66NGF3FzcTMFjiPylgeyL1Y06TT1XyXUIXj&#43;5sQcdMH/8AWKsR63PZiRjGJb4gKtxMSfl44Unpx2x&#43;fbGNJR1NZOTRyFm/iO8uka4klGx/I8qRcMSOqhePxP5&#43;/aJpyrY4ludshXLbRlfpU1vBdiH7TdSK8si4Py4IU87QeuMk59T1zVa6ucjjJGew6&#43;1auCe6JjcnsQPtXToVxS62q/2hEu0YYjIx15pLH/j6/Fadrf8AyEoPqP51Zb3AfLo7beMMQMem41kQ8gA/3v61r/8AMIf/AHz/AOhGsiHoP97&#43;tDBbn//Z"
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/lenovo_nic/7_nic1_hu71ac048810912ab87477b57d18108726_326997_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/lenovo_nic/8_nic2.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 2.22mm f/2.2<br/>2.220000028611935mm f/2.2 1/50sec ISO 200"
						

						
					
					>
					<img			
						width="600" height="385"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABUAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AK8NvOx2yK0bIdrLIhQqe4q3daalvD9oZNsJOA7LnOewz1qvpDi3vLoandP9oJ8wzzndux1ABPXv71k6pqk99eM8kzzIhITc3GO2Bxj8qiEI8raCrWcdCa7mDyf6xhATjAPP4jvSWWoXEMpFujOCcum7gj6euOOPTpVWOKa6jCi1lkDcFk/h98d/pV6XZbQG3ijKp1ldurAc8mpdzOnGbfMyDxCBBdwuoyZRz9Rj8&#43;DTrGKOZl3Rr98L09v/AK1N8U/8fFn/AMC/pUumfeX/AK6j&#43;tRQfuo6KkU6iubd6YtM06QwQjcELEk9cH/69cnPdOthuYs5kw7Ang98V1Wv/wDINn/64t/OuOuv&#43;Qcn/XMfyrZjkf/Z"
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/lenovo_nic/8_nic2_hu704a4dffdcd91781673407f0d3ec4de6_400167_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/lenovo_nic/9_riser.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 6.765mm f/1.78<br/>6.764999865652793mm f/1.7799999713880652 1/50sec ISO 100"
						

						
					
					>
					<img			
						width="600" height="354"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABMAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/ADUVhgO2aUSnHzqgLHH06VzV1qTWjtDYLKEdQ00YXKsoOTjng8dsd66e80p7sfJbsD/eyR/OsHUNK1iwu0vgqXaRnJRT8wHce/8AnisacoJcppVjNu6LjBZYI5bTZLG6BtxyCM/Wq2&#43;dSCzMq9iOlQ6U8cN15PlMbeRTLbRvjlSfmjOe6n&#43;VaDlDMwCSCPaB&#43;9fdg&#43;wpypwUeZEUqs5PkluduwyCT2FZ9wAwfI6DitE/dP0rPm6SfSuU7DmNTjRthK8xXKshHG0lWz/IflWVbXEs8LPI2WzjoB/KtfUeh/67p/6C9Ylj/wAerfWun/l2cyX75n//2Q=="
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/lenovo_nic/9_riser_hu895237cd8829db7ecb56f54b8adc3baa_317968_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-0efe21febd1ec25b8e9ee6c88aecf11b-0");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<p>Unfortunately, I assumed the compatibility issue was something generic and could easily be fixed by cross-flashing the original Intel firmware. While this might have worked for others, this isn&rsquo;t the case when specifically using a Dell-branded X710 NIC with a Lenovo M920q Tiny PC. So, if anyone intends to follow this route, take note! I learned this the hard way because I purchased a Dell-branded one and went through the whole cross-flashing process only to find out that my machine will only boot up successfully if it&rsquo;s coming from an unplugged state. Yup, that means it will only boot up after unplugging and plugging the power cable. Anyway, I ended up purchasing an Intel-branded one and just flashed it with the latest firmware. For anyone interested, the articles I followed are also shared at the end of this post!</p>
<p>Lenovo M920q Tiny PCs require a PCIe riser for plugging in your cards. This can easily be bought for about ¥2000~3000 online. There are different part numbers in terms of the supported bus speed, so I had to ensure I got the one with P/N: 01AJ940, which supports x8.</p>
<p>As for the 10G switch, for the time being, I got a 4x2.5 + 2x10G switch from Horaco (AliExpress). I already have an 8x2.5G switch from the same brand and it&rsquo;s been rock stable. There was no really good reason for me to try out another brand. If there was one thing where you have to manage your risk, it&rsquo;s when purchasing networking equipment from AliExpress!</p>




	





































<style>
	

	
</style>





<div id="gallery-0efe21febd1ec25b8e9ee6c88aecf11b-1-wrapper" class="gallery-wrapper">
<div id="gallery-0efe21febd1ec25b8e9ee6c88aecf11b-1" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/network/2_horaco.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 2.22mm f/2.2<br/>2.220000028611935mm f/2.2 1/50sec ISO 500"
						

						
					
					>
					<img			
						width="600" height="311"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABEAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AKekaDpmkRQJepCdQt2YtNknqeB6HtW&#43;t7p6jm6j/Osi/GmatkNNtDYykqZ/qKiHhTS5FDvcQpvLHcYsDLcN/FWUZKxUou5v/wBo6cP&#43;XyHnsWqimj6dqQ&#43;y6PNbQ3KKzwlP4G9ePwrBufBlrPJui1aDYi&#43;WuMqAPzrf8OWWm&#43;HY8JqEbz95PMH5cmnJpoIxaZzEv3zVLUP9XB/13j/9Cq7L981S1D/Vwf8AXeP/ANCrlW50MnP3qsWX/H1F/vCq5&#43;9Viy/4&#43;ov94Uo7oGf/2Q=="
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/network/2_horaco_huda0d695b44ae1bd295d76d07d7427684_167972_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/network/3_sfp.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 6.765mm f/1.78<br/>6.764999865652793mm f/1.7799999713880652 1/100sec ISO 80"
						

						
					
					>
					<img			
						width="600" height="386"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABUAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/ALhkEchkc4jDjcfbjNXZ1mhmMdrYIwwCJpXyDzz8vtVAr5ti6yEEMGxx6nikfT7nYsmo6m8gYZEMJwW9u2f881zU15G02advdyQM6X13bhGQgQQrgj3A6&#43;vrVQSB0RlYsCwIY9waWygvYmT7Bpkdva5BkkkOWde/IPp9R70sxgLsbaRHRGBOw8Lg8itKkXZMiDK0mfsYGf8Aln/Sq0WoCLQ476aBZntSVQE44HHXtxVqT/j0H&#43;5/Ssh/&#43;RRn/wB9v51NLdlVCWfWNR11/LuLkxwkgeTEMLj39fxrVisUs7F1Rs/KSePaud0r/Wr9RXWT/wDHo/8AuH&#43;VOTbeoopJaH//2Q=="
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/network/3_sfp_hu814eb0653470a35e31f2734fad4d4ec1_223230_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/network/4_dac.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 6.765mm f/1.78<br/>6.764999865652793mm f/1.7799999713880652 1/50sec ISO 100"
						

						
					
					>
					<img			
						width="600" height="450"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/ANG81gwqmQoVnAODzjvVu30mxtYIp9S1Bo5JWL4knCrz/CPUCuV8TyNDZPGoViysSwGSBWp4cvTZWIRXnaFlUonDhRjnGSMde1S0os0d7GheQxwyg6fNHeryTHHdDzB9F7/zpq6ovloUiKsmQwfOR/I1qx6tashkUyIxByJGHA&#43;grmtP83U7i4v55RiZl2DqQgyB/LNLR77CQmqxQTeZGsagMhB2jrmsHT76&#43;0JTYtZSXlvtPlTRj5l9Af0reuf9a/0/rVI9V&#43;orCMu5bRUDahqkqj7PNaW&#43;7JZ&#43;rc/hjiumtBshSBpNxRQoJOSQOmaqx/8AHmPr/hU0H/H0v0H9K0b0I6n/2Q=="
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/network/4_dac_hu685d8aca29d5fc1d840947359a322285_285071_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-0efe21febd1ec25b8e9ee6c88aecf11b-1");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<p>10G SFP+ to copper transceivers are known to run hot. And because I keep my networking equipment in an unventilated closet, I had to shed an extra couple of bucks for the premium ones—the version that supports up to 80m of copper. For the transceivers, I got them from a brand called ZYOPM.</p>
<p>Managing the temperature inside an enclosed space is a priority, so I had to make sure the temps are at a minimum whenever possible. For the upstream connection of my switch to the router, I used a DAC cable. I&rsquo;ve never used one before and actually thought of using optical transceivers in the beginning.</p>




	





































<style>
	

	
</style>





<div id="gallery-0efe21febd1ec25b8e9ee6c88aecf11b-2-wrapper" class="gallery-wrapper">
<div id="gallery-0efe21febd1ec25b8e9ee6c88aecf11b-2" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/ap/5_tplink.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 6.765mm f/1.78<br/>6.764999865652793mm f/1.7799999713880652 1/100sec ISO 64"
						

						
					
					>
					<img			
						width="600" height="453"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AI9HhivbhS0aqicsT0roP7Ol126k8ry0t4hs82RMkHvgcc0y204WGjB3J8x&#43;SAAQSRwDnj/J/DR8O30ENubOV1jcMSu44DA/1qIQ5I2LqS55XKNx4GUxk21/ukA&#43;7ImFP5HiuQuoZbGdobmMRTKxUrux&#43;VeryzxW8ZklkWNB3Y4FeV&#43;LrxdT8RxPECIySV46hRjNTJLlbaKg3zJXKraf4is2Y2Erywg/IvmZIH0PFV28Qa7ZHbqFlvA6s8RU/mOP0rtLXtXPeLv9S1CbQ2kynF4wsXIE1rImePkIYD&#43;VVLi&#43;hvtXE1nPhUi4ypHJPTB&#43;lcxH3&#43;taGl/8fL/7gonJtNBCKUkz/9k="
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/ap/5_tplink_hu1052ddbb5c4b39337bd4edbccd50c8c0_245108_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/upgrading-our-home-internet-to-10-gbps/ap/6_injector.jpg" 
					class="galleryImg"
					
						

						
							data-description="iPhone 15 Pro Max + iPhone 15 Pro Max back triple camera 2.22mm f/2.2<br/>2.220000028611935mm f/2.2 1/100sec ISO 250"
						

						
					
					>
					<img			
						width="600" height="383"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABQAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AE8P2QvJzM8OyKI8kH7zeldBfaja2KxC7lLM&#43;Qq7AeBWRa&#43;I7Cwijtpre4hAH3gAyt6n1q5Lf6Fq0IimvYiOxfMbL9CQK0oSpQhZSVzixKrTq80ovl8jOv8AxLpUTYOnLIO5O0Gob3&#43;y7zQJtUsUKhXC42bWU5Ge&#43;D1rVs/C&#43;hgb40F1/tNJuH6cVLrVpHD4fuIraJIlG0hVUAZ3CtJ0&#43;aDk7GcKyjUjGF90VLKwtryARXEYkTrg9qdqfhXTYYN8QlQ&#43;z5/nU&#43;k/dFaWr/8AHp&#43;FcTimtUexd3PLJpZILtkjdhtzg55qSbW9SQQ2/wBrleKU4ZXYsMfjUN7/AMf8v1NVrn/X2v8AvmuPapZGrScdT//Z"
							
							class="lazy"
							data-src="/posts/upgrading-our-home-internet-to-10-gbps/ap/6_injector_hudadc7a1cc8e69714db852c4d3a521dca_241276_600x600_fit_q100_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-0efe21febd1ec25b8e9ee6c88aecf11b-2");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<p>On to the Wireless AP, I also upgraded from my not-so-old TP-Link EAP610 to an EAP773. The former only supported Wi-Fi 6 over 2.4/5 GHz bands with 1G upstream. For &ldquo;future-proofing,&rdquo; at least for the next few years, and to make use of the upgraded upstream, I needed something which at least supported Wi-Fi 6E over 6 GHz.</p>
<p>I also had to get a PoE++ 90W injector since the new switch didn&rsquo;t support PoE+.</p>
<h2 id="software">Software</h2>
<p>There are mixed opinions on whether you should virtualize your router or not. But if you live somewhere where space is a luxury, then you already have the best reason to go with virtualization (but regardless of that, I just really like complicating things, lol). So of course, we go back to our good old friend, Proxmox. The last time I had to spin up a new Proxmox machine was about 2 years ago when I started using CEPH as my default backend storage. A lot of good things must have been added within these 2 years, but I just really needed an easily managed KVM host. No one can beat Proxmox on that.</p>
<p>As for the router itself, I made the big switch from OPNsense to OpenWRT. And I couldn&rsquo;t be happier. I&rsquo;ve had OPNsense for about 3 years now and while I didn&rsquo;t really complain about anything since it was more of a &ldquo;set and forget&rdquo; setup, whenever I had to modify something in the configuration, sometimes it just felt like I had to explore and familiarize myself again. With OpenWRT, the GUI just felt a bit more natural and warm to my eyes. Maybe it&rsquo;s because of fewer sections or tabs to go through, or, I don&rsquo;t know, configuration just seemed a little more straightforward this time compared to when I just started with the other.</p>
<p>But in fact, I don&rsquo;t think I would have bothered checking out OpenWRT if only OPNsense supported MAP-E connections used by my new ISP. There is a way to get it working with OPNsense, but it was more of a workaround than a natively supported feature.</p>
<h2 id="my-choice-of-isp">My choice of ISP</h2>
<p>The primary factor in choosing the ISP is the monthly cost. The switch to 10G wouldn&rsquo;t be justifiable if we had to pay 50% more than our old subscription. Second would be the option to have a fixed IPv4 address since I host some services for family and friends. Fortunately, I came across a post on Reddit suggesting <a href="https://enhikari.jp/">En Hikari</a>. En Hikari uses NTT FLET&rsquo;S HIKARI as the backbone provider, so you can be assured of the same quality of connectivity used by most ISPs. At the time of writing, the monthly cost is about JPY 4,917 (tax included) plus an additional JPY 770 for the optional fixed IPv4 address. That&rsquo;s a total of JPY 5,687, which comes out even cheaper than our current basic 1G subscription at JPY 5,720 (dynamic IPv4 address only).</p>
<h2 id="preparations">Preparations</h2>
<p>When I got the Intel X710 card, it only had v6.01 installed. I didn&rsquo;t find a way to upgrade directly from v6.01 to the latest one (v9.56 as of this writing) and had to go through it stepwise. The upgrade path in my case was from 6.01 &ndash;&gt; 8.6 &ndash;&gt; 9.0 &ndash;&gt; 9.10 &ndash;&gt; 9.56. All done under Debian (Proxmox).</p>
<p>My Intel card came with unlocked vendor support, so I didn&rsquo;t have to run the unlocker script. I guess this is only applicable to the OEM versions.</p>
<p>I actually ran into some errors initially that prevented the second Ethernet port from functioning. Honestly, I hit a wall and gave up on it for the night. Then, I&rsquo;m not sure how, but it just got resolved the following morning after a restart.</p>
<p>After a few more rounds of restarts and going through kernel dmesg logs, I finally decided to go with a fresh install of OpenWRT. For some reason, I couldn&rsquo;t figure out how to create the VLAN interfaces from the GUI. I ended up with a successful attempt when I tried to do it from the text file configuration. To those who are in the same boat, below is a sample <code>/etc/config/network</code> for configuring VLANs.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">config interface &#39;loopback&#39;
</span></span><span class="line"><span class="cl">	option device &#39;lo&#39;
</span></span><span class="line"><span class="cl">	option proto &#39;static&#39;
</span></span><span class="line"><span class="cl">	option ipaddr &#39;127.0.0.1&#39;
</span></span><span class="line"><span class="cl">	option netmask &#39;255.0.0.0&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config globals &#39;globals&#39;
</span></span><span class="line"><span class="cl">	option ula_prefix &#39;fd00:7808:88c3::/48&#39;
</span></span><span class="line"><span class="cl">	option packet_steering &#39;1&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config device
</span></span><span class="line"><span class="cl">	option name &#39;br-lan&#39;
</span></span><span class="line"><span class="cl">	option type &#39;bridge&#39;
</span></span><span class="line"><span class="cl">	list ports &#39;eth0&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config interface &#39;lan&#39;
</span></span><span class="line"><span class="cl">	option device &#39;br-lan.1&#39;
</span></span><span class="line"><span class="cl">	option proto &#39;static&#39;
</span></span><span class="line"><span class="cl">	option ipaddr &#39;192.168.0.1&#39;
</span></span><span class="line"><span class="cl">	option netmask &#39;255.255.255.0&#39;
</span></span><span class="line"><span class="cl">	option ip6assign &#39;64&#39;
</span></span><span class="line"><span class="cl">	list dns &#39;192.168.0.1&#39;
</span></span><span class="line"><span class="cl">	option ip6hint &#39;00&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config interface &#39;wan&#39;
</span></span><span class="line"><span class="cl">	option device &#39;eth1&#39;
</span></span><span class="line"><span class="cl">	option proto &#39;dhcp&#39;
</span></span><span class="line"><span class="cl">	option keepalive &#39;5 10&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config interface &#39;wan6&#39;
</span></span><span class="line"><span class="cl">	option device &#39;eth1&#39;
</span></span><span class="line"><span class="cl">	option proto &#39;dhcpv6&#39;
</span></span><span class="line"><span class="cl">	option reqaddress &#39;try&#39;
</span></span><span class="line"><span class="cl">	option reqprefix &#39;auto&#39;
</span></span><span class="line"><span class="cl">	option norelease &#39;1&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config bridge-vlan
</span></span><span class="line"><span class="cl">	option device &#39;br-lan&#39;
</span></span><span class="line"><span class="cl">	option vlan &#39;1&#39;
</span></span><span class="line"><span class="cl">	list ports &#39;eth0&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config bridge-vlan
</span></span><span class="line"><span class="cl">	option device &#39;br-lan&#39;
</span></span><span class="line"><span class="cl">	option vlan &#39;10&#39;
</span></span><span class="line"><span class="cl">	list ports &#39;eth0:t&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config bridge-vlan
</span></span><span class="line"><span class="cl">	option device &#39;br-lan&#39;
</span></span><span class="line"><span class="cl">	option vlan &#39;20&#39;
</span></span><span class="line"><span class="cl">	list ports &#39;eth0:t&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config interface &#39;vlan10&#39;
</span></span><span class="line"><span class="cl">	option proto &#39;static&#39;
</span></span><span class="line"><span class="cl">	option device &#39;br-lan.10&#39;
</span></span><span class="line"><span class="cl">	option ipaddr &#39;192.168.10.1&#39;
</span></span><span class="line"><span class="cl">	option netmask &#39;255.255.255.0&#39;
</span></span><span class="line"><span class="cl">	list dns &#39;192.168.10.0.1&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config interface &#39;vlan20&#39;
</span></span><span class="line"><span class="cl">	option proto &#39;static&#39;
</span></span><span class="line"><span class="cl">	option device &#39;br-lan.20&#39;
</span></span><span class="line"><span class="cl">	option ipaddr &#39;192.168.20.1&#39;
</span></span><span class="line"><span class="cl">	option netmask &#39;255.255.255.0&#39;
</span></span><span class="line"><span class="cl">	list dns &#39;192.168.20.0.1&#39;
</span></span></code></pre></div><p><code>/etc/init.d/network restart</code> to apply the config.</p>
<p>For now, I just wanted a stable internet connection and didn&rsquo;t even bother to think about having SR-IOV, so it&rsquo;s been decided to just make use of the existing Proxmox Linux bridges for both the WAN and LAN interface.</p>
<h2 id="en-hikari-configuration-on-openwrt">En Hikari configuration on OpenWRT</h2>
<p><strong>References:</strong>
<a href="https://note.com/arunya/n/n7d81e0de9db7">https://note.com/arunya/n/n7d81e0de9db7</a>
<a href="https://www.ficusonline.com/ja/posts/openwrt-v6-plus-map-e">https://www.ficusonline.com/ja/posts/openwrt-v6-plus-map-e</a></p>
<p>I found these two articles that provide step-by-step instructions to get En Hikari working with v6 plus and the fixed IPv4. The initial steps to have DHCPv6 working are in the first one. In my case, I installed the required packages first:</p>
<ul>
<li>luci-proto-ipv6</li>
<li>map</li>
<li>ds-lite</li>
<li>ip-full</li>
</ul>
<p>As I know Hikari Cross utilizes MAP-E. I am not sure if <code>ds-lite</code> is really required, but I just installed it anyway.</p>
<p>I created a new WAN interface, named it &lsquo;wan6&rsquo;, and set the protocol to <code>DHCPv6 client</code>. Under DHCP Server &gt; IPv6 Settings, the RA-Service, DHCPv6 Service, and NDP Proxy were all set to disabled. After a few seconds, I was assigned an IPv6 /56 subnet.</p>
<p>
    <img src="/posts/upgrading-our-home-internet-to-10-gbps/wan6_dhcp.png" alt="wan6 DHCP settings">
  </p>
<p>For the clients to connect over IPv6, I had to enable IPv6 under <strong>Interfaces &gt; LAN &gt; Advanced Settings</strong>. IPv6 assignment length was set to <code>64</code> and hint to <code>00</code> so my LAN gets assigned the first smaller subnet of the /56 block.</p>
<p>Under <strong>LAN &gt; DHCP Server &gt; IPv6 Settings</strong>, RA-Service and DHCPv6 Service should be set to <code>server</code> and NDP Proxy to <code>relay</code>. Under IPv6 RA Settings, I made sure SLAAC was enabled. The rest were set to default. After applying the changes, my devices started having an IPv6 address, the majority by SLAAC and a very few through DHCPv6.</p>
<p>For the IPv4 configuration, I just followed the article. Though I am still sharing my snaps here for future reference:</p>
<p>Info that was provided by the ISP:</p>
<ul>
<li>V4アドレス</li>
<li>インターフェスID</li>
<li>BRアドレス</li>
<li>ユーサID</li>
<li>パスワード</li>
</ul>
<p>
    <img src="/posts/upgrading-our-home-internet-to-10-gbps/map-e_1.png" alt="MAP-E configuration">
  </p>
<p>
    <img src="/posts/upgrading-our-home-internet-to-10-gbps/map-e_2.png" alt="MAP-E configuration">
  </p>
<p>
    <img src="/posts/upgrading-our-home-internet-to-10-gbps/map-e_3.png" alt="MAP-E configuration">
  </p>
<h2 id="quick-speed-test">Quick speed test</h2>
<p>I don&rsquo;t have any other equipment (yet, lol) that supports 10G traffic, so I spun up an LXC container on the same PVE hosting OpenWRT and ran a simple iPerf test from there. I selected one of the JP servers from <a href="https://github.com/R0GGER/public-iperf3-servers">this list</a> maintained by <a href="https://github.com/R0GGER">@R0GGER</a> (Big thanks to him!).</p>
<p>The result, DL and UL speed respectively:</p>
<p>The result:</p>
<p><strong>DL SPEED</strong>

    <img src="/posts/upgrading-our-home-internet-to-10-gbps/10_dlspeed.png" alt="DL Speed">
  </p>
<p><strong>UL SPEED</strong>

    <img src="/posts/upgrading-our-home-internet-to-10-gbps/11_ulspeed.png" alt="UL Speed">
  </p>
<p>I may not be getting the full 10G bandwidth, but I&rsquo;m certainly getting ~5x the speed compared to my previous connection for the same price.</p>
<h2 id="whats-next">What&rsquo;s next</h2>
<p>In addition to the benefits a multi-gigabit upstream brings, being assigned an IPv6 subnet from my ISP will allow me to host multiple services from within my local network since the hosts will now have publicly routable addresses. This means a few things:</p>
<ol>
<li>No need to keep track and configure ports for port-forwarding to different VMs or containers.</li>
<li>Multiple services can be hosted on the same port.</li>
<li>I can create a DMZ network to completely expose hosts to the public internet.</li>
<li>P2P connectivity can be established without the requirement of NAT-ing.</li>
</ol>
<h2 id="references">References</h2>
<ul>
<li><a href="https://gist.github.com/mietzen/736583d37a1d370273c0775aaaa57aa5">Cross-flashing X710 NICs</a></li>
<li><a href="https://github.com/bibigon812/xl710-unlocker/tree/master#">Disabling vendor-lock of X710 NICs</a></li>
<li><a href="https://www.moduletek.com/en/application_notes/an_00115.html">Explanation of above</a></li>
<li><a href="https://gist.github.com/subrezon/b9aa2014343f934fbf69e579ecfc8da8">OpenWRT on Proxmox</a></li>
<li><a href="https://note.com/arunya/n/n7d81e0de9db7">OpenWRT configuration for En Hikari with v6 plus and Fixed IP</a></li>
<li><a href="https://www.intel.com/content/www/us/en/download/18635/non-volatile-memory-nvm-update-utility-for-intel-ethernet-adapters-700-series-linux.html">Intel X710 drivers</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Connecting a remote server to my Kubernetes cluster and mounting an NFS share over tailscale</title>
      <link>https://luislogs.com/posts/connecting-a-remote-server-to-my-kubernetes-cluster-and-mounting-an-nfs-share-over-tailscale/</link>
      <pubDate>Wed, 18 Dec 2024 08:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/connecting-a-remote-server-to-my-kubernetes-cluster-and-mounting-an-nfs-share-over-tailscale/</guid>
      <description>The homelab has not undergone any major change in recent months due to personal reasons, but now is a good time to get back on track. Today I want to share how I managed to connect a completely remote server to my Kubernetes cluster and how it&amp;rsquo;s able to directly write to my home NAS with the help of Tailscale.
This might come in handy to those who are thinking of setting up their own VPS to connect to their K8s cluster, whether it be hosted at home or in the cloud, or maybe to those who just simply want to connect two mutually remote devices running on two different networks with a firewall.</description>
      <content:encoded><![CDATA[<p>The homelab has not undergone any major change in recent months due to personal reasons, but now is a good time to get back on track. Today I want to share how I managed to connect a completely remote server to my Kubernetes cluster and how it&rsquo;s able to directly write to my home NAS with the help of Tailscale.</p>
<p>This might come in handy to those who are thinking of setting up their own VPS to connect to their K8s cluster, whether it be hosted at home or in the cloud, or maybe to those who just simply want to connect two mutually remote devices running on two different networks with a firewall.</p>
<p>
    <img src="/posts/connecting-a-remote-server-to-my-kubernetes-cluster-and-mounting-an-nfs-share-over-tailscale/tailnet.png" alt="Tailscale Network">
  </p>
<p>To start off, first, keep in mind that my NAS in the backend is exported via NFS by an LXC container hosted on Proxmox, and the remote service (Deluge in this case) that would be mounting the share is running as a docker container.</p>
<h2 id="tailscale-pre-requisites-for-lxc-containers-on-pve">Tailscale pre-requisites for LXC containers on PVE</h2>
<p>On the NFS server end, the container needs access to <code>/dev/net/tun</code> on the host. To allow this we need two lines in the container configuration (reference: <a href="https://tailscale.com/kb/1130/lxc-unprivileged">tailscale kb article</a>).</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">lxc.mount.entry</span><span class="p">:</span><span class="w"> </span><span class="l">/dev/net/tun dev/net/tun none bind,create=file</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">lxc.cgroup2.devices.allow</span><span class="p">:</span><span class="w"> </span><span class="l">c 10:200 rwm</span><span class="w">
</span></span></span></code></pre></div><p>Stop the container and hop on to your PVE host&rsquo;s terminal. Modify <code>/etc/pve/lxc/1201.conf</code> and the above two lines.</p>
<h2 id="tailscale-installation-on-the-lxc-container-and-remote-server">Tailscale installation on the LXC container and remote server</h2>
<p>Next up is installation of tailscale. The installation is fairly easy. An installation command can be generated together with a pre-approved authentication. Go to your Tailscale account &gt; Machines &gt; Add device &gt; Generate install script. Ensure to create a reusable key. There is no need to add tags if the device is intended for a single user. Execute on both the LXC container and remote server</p>
<p>e.g.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">curl -fsSL https://tailscale.com/install.sh <span class="p">|</span> sh <span class="o">&amp;&amp;</span> sudo tailscale up --auth-key<span class="o">=</span>tskey-auth-kHD2rsVNiv11CNTRL-188WY38WSG2iqTHTZs1oF2e3LMSmMYuXF
</span></span></code></pre></div><p>You should now be able to see your device under the Machines tab. The tailscale IP will also be visible from there. If you want to check the status of tailscale: <code>sudo systemctl status tailscaled</code></p>
<p>To avoid manual intervention in the future, you can disable the key expiry for each device in the Machines tab.</p>
<h2 id="configure-nfs">Configure NFS</h2>
<p>Allow the tailscale IP in your /etc/exports file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/mnt/nfs-share 100.22.112.36/32<span class="o">(</span>rw,sync,no_subtree_check,all_squash,anonuid<span class="o">=</span>1000,anongid<span class="o">=</span>1000,insecure<span class="o">)</span>
</span></span></code></pre></div><p>On the remote server, ensure the containers are stopped and modify your compose file to mount the nfs share to the <code>/downloads</code> directory.</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">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;2.1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">deluge</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">lscr.io/linuxserver/deluge:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">deluge</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">PUID=1000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">PGID=1000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TZ=Asia/Manila</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">DELUGE_LOGLEVEL=error</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">.:/config</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">nfsdir:/downloads</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="m">8112</span><span class="p">:</span><span class="m">8112</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">6881</span><span class="p">:</span><span class="m">6881</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">6881</span><span class="p">:</span><span class="m">6881</span><span class="l">/udp</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">nfsdir</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">local</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">driver_opts</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">nfs</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">o</span><span class="p">:</span><span class="w"> </span><span class="l">addr=100.22.112.36,rw,vers=4.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">device</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;:/mnt/nfs-share&#34;</span><span class="w">
</span></span></span></code></pre></div><p>Start the container and try to create a file in the NFS share.</p>
<h2 id="install-tailscale-operator-on-k8s">Install tailscale-operator on k8s</h2>
<p>It will be easier for you to refer to the official documentation so I am sharing the actual KB article <a href="https://tailscale.com/kb/1236/kubernetes-operator">here</a>.</p>
<p>In summary, two items are needed to be configured from the Tailscale Management page — tags and OAuth client credentials. Complete these steps and carry on with the helm installation. Helm installation steps are also shared below.</p>
<h3 id="installation-with-helm">Installation with helm</h3>
<p>Replace the clientId and clientSecret variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">helm repo add tailscale https://pkgs.tailscale.com/helmcharts
</span></span><span class="line"><span class="cl">helm repo update
</span></span><span class="line"><span class="cl">helm upgrade <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --install <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  tailscale-operator <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  tailscale/tailscale-operator <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --namespace<span class="o">=</span>tailscale <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --create-namespace <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --set-string oauth.clientId<span class="o">=</span><span class="s2">&#34;&lt;OAauth client ID&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --set-string oauth.clientSecret<span class="o">=</span><span class="s2">&#34;&lt;OAuth client secret&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --wait
</span></span></code></pre></div><p>Create a ProxyGroup of type egress.</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">tailscale.com/v1alpha1</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">ProxyGroup</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">ts-proxies</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">egress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span></code></pre></div><h3 id="installation-with-argocd">Installation with ArgoCD</h3>
<p>If you are tidy, maintain your infrastructure as code, and use ArgoCD, then go ahead and download the values.yaml file and update the clientId and clientSecret fields.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">helm show values tailscale/tailscale-operator &gt; values.yaml
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">oauth</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">clientId</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;kq7LLBiMeL11CNTRL&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">clientSecret</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;tskey-client-kq7LLBiMeL11CNTRL-rPFdRtSCj74REgir443J84CYp12Lo8Vce&#34;</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>Create the manifest for your ArgoCD application (replace <code>yourgituser</code> and <code>yourrepo</code>).</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">argoproj.io/v1alpha1</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">Application</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">tailscale</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">argocd</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">destination</span><span class="p">:</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">tailscale</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</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">sources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;https://pkgs.tailscale.com/helmcharts&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">chart</span><span class="p">:</span><span class="w"> </span><span class="l">tailscale-operator</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="m">1.78.3</span><span class="w"> </span><span class="c"># This is the latest version as of the time of this post.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">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">releaseName</span><span class="p">:</span><span class="w"> </span><span class="l">tailscale-operator</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">valueFiles</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="l">$values/tailscale/values.yaml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:yourgithubuser/yourrepo.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="l">HEAD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">ref</span><span class="p">:</span><span class="w"> </span><span class="l">values</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:yourgithubuser/yourrepo.git</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">tailscale/manifests</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="l">HEAD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CreateNamespace=true</span><span class="w">
</span></span></span></code></pre></div><p>Don&rsquo;t forget to create the egress ProxyGroup manifest!</p>
<h2 id="testing-connectivity-from-a-pod-to-the-remote-server">Testing connectivity from a pod to the remote server</h2>
<p>Login to any pod and perform a curl to the other end. If you don&rsquo;t have a test pod, then you can deploy an alpine image.</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">apps/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">Deployment</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">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">run</span><span class="p">:</span><span class="w"> </span><span class="l">alpine</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">alpine</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">alpine</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">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">strategy</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</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">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">alpine</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">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">alpine:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">command</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="l">/bin/sh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="s2">&#34;-c&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="s2">&#34;apk --update add curl &amp;&amp; sleep 1440m&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">imagePullPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">IfNotPresent</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">alpine</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">kubectl -n alpine <span class="nb">exec</span> -it alpine-9c7cb4b95-nx7b4 -- curl http://10.43.59.234:8112
</span></span></code></pre></div><h2 id="pod-to-tailscale-communication">Pod to tailscale communication</h2>
<p>Now that I have a secondary network attachd to my cluster, I can utilize my ingress controller, that is Traefik, to provide HTTPS access to devices on that network.</p>
<p>To do so traefik must be allowed to reach ExternalNames. Set <code>providers.allowExternalNameServices</code> to <code>true</code> in the values.yaml file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">providers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">kubernetesIngress</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">allowExternalNameServices</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="nn">...</span><span class="w">
</span></span></span></code></pre></div><p>Create the ingress resource as you would normally do, and also create a service of type ExternalName. You can either provide the IP or the tailscale FQDN.</p>
<p>In case of the latter:</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">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">tailscale.com/tailnet-fqdn</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;voyager.taile9a23.ts.net&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">tailscale.com/proxy-group</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;ts-proxies&#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">ts-egress-voyager</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">externalName</span><span class="p">:</span><span class="w"> </span><span class="l">voyager-deluge</span><span class="w"> </span><span class="c"># any value - will be overwritten by the operator</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">ExternalName</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="m">8112</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">web</span><span class="w">
</span></span></span></code></pre></div><p>Note that you have to specify the ProxyGroup that was created in the previous step. Also an additional clusterIP service will be created on top of this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">NAME                         TYPE           CLUSTER-IP      EXTERNAL-IP                                      PORT(S)    AGE
</span></span><span class="line"><span class="cl">ts-egress-voyager            ExternalName   &lt;none&gt;          ts-ts-egress-voyager-mgmz2.tailscale.svc.local   8112/TCP   2s
</span></span><span class="line"><span class="cl">ts-ts-egress-voyager-mgmz2   ClusterIP      10.43.231.143   &lt;none&gt;                                           8112/TCP   2s
</span></span></code></pre></div><p>This is the final step and you should now be able to access Deluge on the remote server via HTTPS.</p>
<h2 id="tailscale-lovin">Tailscale Lovin'</h2>
<p>What I love about tailscale is how easy it is to setup and the how the folks over there continue to offer a free plan for personal use. I still remember how difficult it was to setup a VPN with friends 15-20 years ago just to be able to play games like Counter-Strike or Starcraft (maybe I was not just as experienced yet? I don&rsquo;t know!) But with tailscale today, it&rsquo;s totally a game changer. I also want to mention here that tailscale offer Mullvad VPN access for $5/month per 5 devices. Although I am not sure if this is supported by the tailscale-operator, at the least all your other devices can utilize this (you can even install tailescale on the AppleTV!). I probably will consider subscribing to this in the not so distant future if I don&rsquo;t find any other use case for remote Deluge server.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>K8s with external Ceph, disaster recovery, and StorageClass migration</title>
      <link>https://luislogs.com/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/</link>
      <pubDate>Sat, 30 Mar 2024 20:30:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/</guid>
      <description>In the past couple of weeks I was able to source matching mini USFF PCs which upgrades the mini homelab from 14 CPU cores to 18! Along with this I decided to attach a 2.5Gbe NIC and a 1TB NVME on each device to be used for Ceph allowing for hyper-converged infrastructure.
Ceph on its own is a huge topic. It has so many moving parts-monitors, metadata servers, OSDs, placement groups to name a few.</description>
      <content:encoded><![CDATA[<p>In the past couple of weeks I was able  to source matching mini USFF PCs which upgrades the mini homelab from 14 CPU cores to 18! Along with this I decided to attach a 2.5Gbe NIC and a 1TB NVME on each device to be used for Ceph allowing for hyper-converged infrastructure.</p>




	




































  	
	

	
		<script src="/shortcode-gallery/jquery-3.7.0.min.js"></script>
	
	
	
		<script src="/shortcode-gallery/lazy/jquery.lazy.min.js"></script>
	

	<script src="/shortcode-gallery/swipebox/js/jquery.swipebox.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/swipebox/css/swipebox.min.css">

	<script src="/shortcode-gallery/justified_gallery/jquery.justifiedGallery.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/justified_gallery/justifiedGallery.min.css"/>


<style>
	

	
</style>





<div id="gallery-84caa044864117c79524b86417764b30-0-wrapper" class="gallery-wrapper">
<div id="gallery-84caa044864117c79524b86417764b30-0" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/minipcs/1.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>9mm f/2.4 0.0333sec ISO 320"
						

						
					
					>
					<img			
						width="600" height="450"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOM0ZXjvk2IIlVSryspYp/tAdz6A12FncXdlqAsbqRZY3ZXSfJ3EcEc9QcdquaLpumCaS1khSO/tM7Vjw6up6SDP3iP/AK3pjEvkSDV3svtSK28ea0kuAhH8WfXPT9aiWpcZOLsalxpbarr2uQr5IeIxeW0hJPzDPBzWWthBbQXKNNHLOojKGNHBB3DnJA6g1sx3n2DU7mXT7i1vWuhGD8srMxUf7C4/WuZ1DVJre42iSKUzKvmKrHovQdBg8CmthSvc6DUraeG&#43;ESp&#43;/df3M0X30HGSPSqUVlFpesaZHIRGWbmXhmYMSpySMEjn1/Gt&#43;&#43;/5GK1/65N/SsXxF/yFtI&#43;h/wDRhqaa906K8rtadEbF5o8WjalHPEtxPIZN43SbEzuYAY25P3a5i&#43;8O38t5NqGk&#43;XLLbysHiCgknqeDw3DYx/OvQPFH/H3B/vr/AOhNWZ4c/wCPrUv&#43;vt/5Cqm7K5zQV2f/2Q=="
							
							class="lazy"
							data-src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/minipcs/1_hu083888481282419fffa537aed85f52ab_3332078_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/minipcs/2.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>6.7mm f/1.8 1/60sec ISO 400"
						

						
					
					>
					<img			
						width="600" height="450"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOYvvG&#43;ZpHawuBI7hi0655GfQj1NU7TxVcpGYo4BsfhicAkZzjp7Cur1ttBnuHWyt4Zz5mxmYkIT3CYGPx6fXtjTaDaAMY47qN1UsY7Vll6fUj19KbT6iSi9uhc8G6zbWGq3bTI8Fk0TSgSZ3H5gQAB1&#43;8cVY1fxvDq&#43;mzWSxXqeadu2I7AT2BPpz0Nc42kPc2xjW8iV3b/VXQ8uQYP1IPT3qpcaJrtkuTC7x9ujj8Aalb3LvpY7jUPCtzEiqbSO4RFwDGMkfh1/KsI2MljMHt5pbSVOQOcD6jr39a9XP&#43;sP0rzrXf8Aj/n&#43;g/mKpanJWTp6xY1NauzEBeRQXv8ACzEBWK/Tgfqag0W0u9W16O3WTyA8u7IXy1ij6Hnoxxx/&#43;uqg/wBXXQeF/wDkNxf9cT/6EKJKyCjWlKVmf//Z"
							
							class="lazy"
							data-src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/minipcs/2_hufb50b81dabd357b55baf1a49af9450ee_1647246_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
					
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/minipcs/3.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>9mm f/2.4 1/30sec ISO 400"
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AF18N/YzqFJLDAAHXiq&#43;mRw20IGoXC26yAkx7isgHI7qcdKb4ll1HTxGPtTDcq52EgFsc4/GsK31fVI8BbyXAYffcN&#43;p5FZQha9zWUr2SOtVtCk3i3vljkx1klwP1AzTPJs/&#43;gtY/wDfY/xrmrLV76XxJbQXE8jLNKgIVzg5OMYHBFehfYov7g/KqfMidDl/F96xiWzmtc8CQSRvtJGO&#43;QawLDTJb&#43;yW8trZniLBch0bYc9D6fQ11Wu2L6rbQm1KvOo2klgBjHrmuYg8Ma7phLWsEm8jDNBKAHX0KnrUqULfEVyyvsKtmuna1aSXdpeRTrMuwqqBN2eM47Zrsv7VvfRP&#43;&#43;T/AI1y1ovihJYrYWrxweYpfMSoFAPOMYA4zXQeTe/31/76WpnJdGEU&#43;qP/2Q=="
							
							class="lazy"
							data-src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/minipcs/3_hu6c0c0e91edfa69d4dc297089c4d2eb97_3173660_600x600_fit_q90_r270_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-84caa044864117c79524b86417764b30-0");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<p>Ceph on its own is a huge topic. It has so many moving parts-monitors, metadata servers, OSDs, placement groups to name a few. And yes I am completely new to it. The good thing in today&rsquo;s age every beautiful piece of software together with the community backing it stems from how well the user documenation is written. Here I am not just talking about Ceph but also Proxmox. Both applications have very well-written documentation to get you started, and if anything just goes haywire, you have a plethora of resources to point you back to the right direction.

    <img src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/pveplusceph.png" alt="PVE and Ceph">
  </p>
<p>That said, Proxmox also nicely integrates Ceph and the complete installation can be done from the GUI itself. All I had to do was create the VLANs and specify the interface to be used for the public and private network. Though to be honest this part took me some time and a fair amount of ceph reinstallations to understand a bit more on the networking side.</p>
<p>Today&rsquo;s agenda is to walk through how I was able to integrate my K8S cluster to an externally managed ceph storage, and how I am actually migrating to this new cluster by performing a disaster recovery procedure. I am also sharing here whatever resources were used for this operation.</p>
<p>References:</p>
<ul>
<li><a href="https://pve.proxmox.com/wiki/Deploy_Hyper-Converged_Ceph_Cluster">https://pve.proxmox.com/wiki/Deploy_Hyper-Converged_Ceph_Cluster</a></li>
<li><a href="https://rook.io/docs/rook/latest-release/CRDs/Cluster/external-cluster/">https://rook.io/docs/rook/latest-release/CRDs/Cluster/external-cluster/</a></li>
<li><a href="https://github.com/catalogicsoftware/velero-docs/blob/main/velero-intro-pvs/velero-intro.md">https://github.com/catalogicsoftware/velero-docs/blob/main/velero-intro-pvs/velero-intro.md</a></li>
<li><a href="https://velero.io/docs/v1.13/migration-case/">https://velero.io/docs/v1.13/migration-case/</a></li>
<li><a href="https://velero.io/docs/v1.13/contributions/minio/">https://velero.io/docs/v1.13/contributions/minio/</a></li>
</ul>
<h2 id="current-situation">Current situation</h2>
<p>At the moment I have an RKE2 cluster running Longhorn as my storage solution for persistent volumes. It has worked wonderfully -safeguarding my data with replication. But the long term goal had always been to leverage Proxmox&rsquo;s built-in ceph management tool. Back then I was not having the appropriate hardware but with recent &ldquo;reasonable&rdquo; upgrades, I think now is the right time. There aren&rsquo;t any heavy workload and majority of what are running are media management tools. The problem is the virtual disk is 150GB which takes up about a third of my 500GB SSD. Apart from this Longhorn storage is running on top of the same disk where the RKE2 ETCD DB is running and all just converge on the same physical disk where Proxmox is also installed. The cluster makes use of a single storageclass for both RWO and RWX PVCs. This cluster is also running in the same vlan where the rest of my VMs are also running. A single vNIC is attached for both k8s traffic, and longhorn replication traffic.</p>
<h2 id="planning-and-logistics">Planning and logistics</h2>
<p>The second cluster will have about 100GB total size (about 80GB total of ephemeral storage after OS). <a href="https://rook.io/">Rook</a> will be used to connect to the Proxmox-managed ceph cluster for persistent volumes. With this the cluster will have two storageclasses — <code>ceph-rbd</code> for RWO PVCs, and <code>cephfs</code> for RWX. Each VM will have two vNICs attached. First vNIC will be assigned a new VLAN3 to be used solely for k8s traffic, and the second vNIC will be running in another VLAN dedicated for ceph traffic. <a href="https://velero.io/">Velero</a> will be used to migrate to the new cluster. <a href="https://min.io/">MinIO</a> will be used as the S3-compatible storage for storing the backups. Existing Cilium LB IPs will be re-used by re-learning BGP routes from the main router with the new VLAN.</p>
<p>I am working on a diagram for the network at home and although it&rsquo;s still work in progress, it should be more than enough to help you understand how things are connected and segmented.

    <img src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/netdiag.png" alt="Toky-prod network diagram">
  </p>
<p>To expound more on this, on high-level we have two clusters - cluster 1 (old) and cluster 2 (new). The migration will go as follows:</p>
<ul>
<li>First we will prepare cluster 2 by integrating to an external ceph cluster.</li>
<li>Then install velero on both k8s clusters.</li>
<li>On cluster 1, perform a backup of the resources.</li>
<li>Go back to cluster 2 and perform the restore. RWO PVCs directly should be migrated to ceph-rbd, and the RWX PVCs to be migrated to cephfs.</li>
<li>Block cluster 1 IPs (on VLAN20) from internet access to avoid issues with cert-manager.</li>
<li>Enable BGP peering on cluster 2 (VLAN3) and reconfigure BGP on OPNsense to route externally exposed IPs to this cluster.</li>
</ul>
<h2 id="installing-rook-and-integrating-with-an-external-cluster">Installing Rook and integrating with an external cluster</h2>
<p>Download create-external-cluster-resources.py:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zsh" data-lang="zsh"><span class="line"><span class="cl">curl -s https://raw.githubusercontent.com/rook/rook/release-1.13/deploy/examples/create-external-cluster-resources.py &gt; create-external-cluster-resources.py
</span></span></code></pre></div><p>Without enabling prometheus you might get the following error after the next step:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-v" data-lang="v"><span class="line"><span class="cl"><span class="nc">ExecutionFailureException</span><span class="p">:</span> <span class="nv">can</span><span class="s1">&#39;t find monitoring_endpoint, prometheus module might not be enabled, enable the module by running &#39;</span><span class="nv">ceph</span> <span class="nv">mgr</span> <span class="kn">module</span> <span class="nv">enable</span> <span class="nv">prometheus</span><span class="s1">&#39;
</span></span></span></code></pre></div><p>To avoid this you can either enable prometheus with the command below or add <code>--skip-monitoring-endpoint</code> when executing the python script.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-v" data-lang="v"><span class="line"><span class="cl"><span class="nv">ceph</span> <span class="nv">mgr</span> <span class="kn">module</span> <span class="nv">enable</span> <span class="nv">prometheus</span>
</span></span></code></pre></div><p>Execute the script to generate the required environment variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">python3 create-external-cluster-resources.py --rbd-data-pool-name tokyoceph --namespace rook-ceph-external --format bash
</span></span></code></pre></div><p>Sample output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">NAMESPACE</span><span class="o">=</span>rook-ceph-external
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">ROOK_EXTERNAL_FSID</span><span class="o">=</span>31833ea9-541d-435d-96ef-e9c653e97d2b
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">ROOK_EXTERNAL_USERNAME</span><span class="o">=</span>client.healthchecker
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">ROOK_EXTERNAL_CEPH_MON_DATA</span><span class="o">=</span><span class="nv">pve1</span><span class="o">=</span>10.22.0.11:6789
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">ROOK_EXTERNAL_USER_SECRET</span><span class="o">=</span><span class="nv">AQAigfdlyQocHhAAtBlaM6EMLp9N6ysQoUUN2A</span><span class="o">==</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CSI_RBD_NODE_SECRET</span><span class="o">=</span>AQAigfdlfB/OHhAAeVpSf6Ow4OE8pWjOU2XPkA<span class="o">==</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CSI_RBD_NODE_SECRET_NAME</span><span class="o">=</span>csi-rbd-node
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CSI_RBD_PROVISIONER_SECRET</span><span class="o">=</span><span class="nv">AQAigfdld8WCHxBBtakNxF2axoyXbghVzsLROA</span><span class="o">==</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CSI_RBD_PROVISIONER_SECRET_NAME</span><span class="o">=</span>csi-rbd-provisioner
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CEPHFS_POOL_NAME</span><span class="o">=</span>tokyocephfs_data
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CEPHFS_METADATA_POOL_NAME</span><span class="o">=</span>tokyocephfs_metadata
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CEPHFS_FS_NAME</span><span class="o">=</span>tokyocephfs
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CSI_CEPHFS_NODE_SECRET</span><span class="o">=</span>AQA6gvdlNsZGKxAAXbWkhSawMStb+CGwV7Lurw<span class="o">==</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CSI_CEPHFS_PROVISIONER_SECRET</span><span class="o">=</span>AQA6gvdlP3MhLBAA+vykqaFjLh20bagnGNwGsg<span class="o">==</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CSI_CEPHFS_NODE_SECRET_NAME</span><span class="o">=</span>csi-cephfs-node
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CSI_CEPHFS_PROVISIONER_SECRET_NAME</span><span class="o">=</span>csi-cephfs-provisioner
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">MONITORING_ENDPOINT</span><span class="o">=</span>10.22.0.12
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">MONITORING_ENDPOINT_PORT</span><span class="o">=</span><span class="m">9283</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">RBD_POOL_NAME</span><span class="o">=</span>tokyoceph
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">RGW_POOL_PREFIX</span><span class="o">=</span>default
</span></span></code></pre></div><p>Paste the output in your shell environment either in the CLI or in the shell configuration file e.g. <code>~/.bashrc</code> or <code>~/.zshrc</code>. If you are pasting in your shell configuration file, don&rsquo;t forge to reload.</p>
<p>Download the import script and run it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">curl -s https://raw.githubusercontent.com/rook/rook/release-1.13/deploy/examples/import-external-cluster.sh &gt; import-external-cluster.sh
</span></span><span class="line"><span class="cl">./import-external-cluster.sh
</span></span></code></pre></div><p>This import script will read the environment variables and accordingly create the necessary resources to connect to your external ceph cluster. The following resources should get created:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zsh" data-lang="zsh"><span class="line"><span class="cl">namespace/rook-ceph-external created
</span></span><span class="line"><span class="cl">secret/rook-ceph-mon created
</span></span><span class="line"><span class="cl">configmap/rook-ceph-mon-endpoints created
</span></span><span class="line"><span class="cl">secret/rook-csi-rbd-node created
</span></span><span class="line"><span class="cl">secret/rook-csi-rbd-provisioner created
</span></span><span class="line"><span class="cl">secret/rook-csi-cephfs-node created
</span></span><span class="line"><span class="cl">secret/rook-csi-cephfs-provisioner created
</span></span><span class="line"><span class="cl">storageclass.storage.k8s.io/ceph-rbd created
</span></span><span class="line"><span class="cl">storageclass.storage.k8s.io/cephfs created
</span></span></code></pre></div><p>Now it&rsquo;s time to install rook! Take note of the first two lines. Here we specify the namespace where the rook operator will be running in and the namespace for the external ceph cluster. You can have a common namespace if you wish.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zsh" data-lang="zsh"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">operatorNamespace</span><span class="o">=</span><span class="s2">&#34;rook-ceph&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">clusterNamespace</span><span class="o">=</span><span class="s2">&#34;rook-ceph-external&#34;</span>
</span></span><span class="line"><span class="cl">curl -s https://raw.githubusercontent.com/rook/rook/release-1.13/deploy/charts/rook-ceph/values.yaml &gt; values.yaml
</span></span><span class="line"><span class="cl">curl -s https://raw.githubusercontent.com/rook/rook/release-1.13/deploy/charts/rook-ceph-cluster/values-external.yaml &gt; values-external.yaml
</span></span><span class="line"><span class="cl">helm install --create-namespace --namespace <span class="nv">$operatorNamespace</span> rook-ceph rook-release/rook-ceph -f values.yaml
</span></span><span class="line"><span class="cl">helm install --create-namespace --namespace <span class="nv">$clusterNamespace</span> rook-ceph-cluster <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>--set <span class="nv">operatorNamespace</span><span class="o">=</span><span class="nv">$operatorNamespace</span> rook-release/rook-ceph-cluster -f values-external.yaml
</span></span></code></pre></div><p>Logging the important nodes post helm installation (this is for personal reference. This part can be skipped):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Operator installation</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Important Notes:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - You must customize the &#39;CephCluster&#39; resource in the sample manifests for your cluster.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Each CephCluster must be deployed to its own namespace, the samples use `rook-ceph` for the namespace.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - The sample manifests assume you also installed the rook-ceph operator in the `rook-ceph` namespace.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - The helm chart includes all the RBAC required to create a CephCluster CRD in the same namespace.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Any disk devices you add to the cluster in the &#39;CephCluster&#39; must be empty (no filesystem and no partitions).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># CephCluster installation</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Important Notes:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Visit https://rook.io/docs/rook/latest/CRDs/ceph-cluster-crd/ for more information about the Ceph CRD.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - You can only deploy a single cluster per namespace</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - If you wish to delete this cluster and start fresh, you will also have to wipe the OSD disks using `sfdisk`</span>
</span></span></code></pre></div><p>In a few minutes you should be able to see a successful connection to your ceph cluster. You can also query the storageclasses created as part of the import script.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">❯ k -n rook-ceph-external get cephcluster
</span></span><span class="line"><span class="cl">NAME                 DATADIRHOSTPATH   MONCOUNT   AGE     PHASE       MESSAGE                          HEALTH      EXTERNAL   FSID
</span></span><span class="line"><span class="cl">rook-ceph-external   /var/lib/rook     3          2m53s   Connected   Cluster connected successfully   HEALTH_OK   true       31833ea9-541d-435d-96ef-e9c653e97d2b
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">❯ k get sc
</span></span><span class="line"><span class="cl">NAME       PROVISIONER                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
</span></span><span class="line"><span class="cl">ceph-rbd   rook-ceph.rbd.csi.ceph.com      Delete          Immediate           true                   3h10m
</span></span><span class="line"><span class="cl">cephfs     rook-ceph.cephfs.csi.ceph.com   Delete          Immediate           true                   3h10m
</span></span></code></pre></div><h2 id="installing-velero-on-cluster-1-and-cluster-2">Installing velero on Cluster 1 and Cluster 2</h2>
<p>For this I prepared five files:</p>
<ul>
<li>credentials-velero</li>
<li>velero-exclude-nfs-volumepolicy.yaml</li>
<li>minio-service-endpoint.yaml</li>
<li>1_velero-change-sc-configmap.yaml</li>
<li>2_velero-change-sc-cephfs-configmap.yaml</li>
</ul>
<p>Before installation, ensure that you have an S3-compatible storage for storing backups. I am using MinIO hosted on my Openmediavault NAS. To be able to reach this from within my cluster, I have to create a service and endpoint manually pointing to the IP and port that should be reachable by the nodes. You can follow this yaml file and change the IP and port whichever is necessary:</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">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">Namespace</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">external</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="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">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">external</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">minio</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">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">name</span><span class="p">:</span><span class="w"> </span><span class="l">app</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="m">9000</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">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">9000</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">web</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="m">9001</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">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">9001</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">clusterIP</span><span class="p">:</span><span class="w"> </span><span class="l">None</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">ClusterIP</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="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">Endpoints</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">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">external</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">minio</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">subsets</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">addresses</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">ip</span><span class="p">:</span><span class="w"> </span><span class="m">10.0.0.8</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">app</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="m">9000</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">web</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="m">9001</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><p>Next we install velero on both clusters. To connect to an S3-compatible we must install the velero plugin for AWS apart from specifying it as the provider.</p>
<p>Input your S3 access and secret key in the following format:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[default]
</span></span><span class="line"><span class="cl">aws_access_key_id = inputs3SecretKeyIdHere
</span></span><span class="line"><span class="cl">aws_secret_access_key = inputs3SecretKeyHere
</span></span></code></pre></div><p>In the same install command we specify the backup location and point to the MinIO URL. We can follow the cluster FQDN format of <em>svc-name</em>.<em>namespace</em>.svc.cluster.local to reach this externally hosted service from within the pods.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">velero install <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --provider aws <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --plugins velero/velero-plugin-for-aws:v1.9.1,velero/velero-plugin-for-csi:v0.7.0 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --use-node-agent --features<span class="o">=</span>EnableCSI <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --bucket velero <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --secret-file ./credentials-velero <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --use-volume-snapshots<span class="o">=</span><span class="nb">false</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --backup-location-config <span class="nv">region</span><span class="o">=</span>minio,s3ForcePathStyle<span class="o">=</span><span class="s2">&#34;true&#34;</span>,s3Url<span class="o">=</span>http://minio.external.svc.cluster.local:9000
</span></span></code></pre></div><p>Execute <code>velero backup-location get</code> or <code>v backup-location get</code> if you also configured auto-completion:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">❯ v backup-location get
</span></span><span class="line"><span class="cl">NAME      PROVIDER   BUCKET/PREFIX   PHASE       LAST VALIDATED                  ACCESS MODE   DEFAULT
</span></span><span class="line"><span class="cl">default   aws        velero          Available   2024-03-23 08:15:37 +0900 JST   ReadWrite     true
</span></span></code></pre></div><h2 id="perform-backup-on-cluster-1">Perform backup on Cluster 1</h2>
<p>First we create a configmap to disable backup of NFS volumes. We do not want NFS volumes to be part of the backup since these are expected to be remounted later on.</p>
<p>Create a file with the following content:</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">version</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">volumePolicies</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">conditions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># nfs could be empty which matches any nfs volume source</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">nfs</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">action</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">skip</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">kubectl -n velero create cm exclude-nfs-volumepolicy --from-file velero-exclude-nfs-volumepolicy.yaml
</span></span></code></pre></div><p>Create two backups - one for all RWX volumes, and another one for all RWO volumes. In my case the only application that is using an RWX volume is adguard. I can specify the namespace here. I don&rsquo;t want the CRDs that are created dynamically by cilium so</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zsh" data-lang="zsh"><span class="line"><span class="cl">v backup create all-rwx-fs-backup --default-volumes-to-fs-backup<span class="o">=</span><span class="nb">true</span> --exclude-resources storageclasses.storage.k8s.io,ciliumendpoints.cilium.io,ciliumidentities.cilium.io --include-namespaces adguard --resource-policies-configmap exclude-nfs-volumepolicy
</span></span></code></pre></div><p>For RWO volumes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zsh" data-lang="zsh"><span class="line"><span class="cl">v backup create all-rwo-fs-backup --default-volumes-to-fs-backup<span class="o">=</span><span class="nb">true</span> --exclude-namespaces kube-system,longhorn-system,velero,adguard --exclude-resources storageclasses.storage.k8s.io,ciliumendpoints.cilium.io,ciliumidentities.cilium.io,ciliumbgppeeringpolicies.cilium.io --resource-policies-configmap exclude-nfs-volumepolicy --parallel-files-upload <span class="m">10</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">❯ v backup get
</span></span><span class="line"><span class="cl">NAME                STATUS      ERRORS   WARNINGS   CREATED                         EXPIRES   STORAGE LOCATION   SELECTOR
</span></span><span class="line"><span class="cl">all-rwo-fs-backup   Completed   0        5          2024-03-23 09:31:50 +0900 JST   29d       default            &lt;none&gt;
</span></span><span class="line"><span class="cl">all-rwx-fs-backup   Completed   0        0          2024-03-23 09:31:28 +0900 JST   29d       default            &lt;none&gt;
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">❯ k get pv
</span></span><span class="line"><span class="cl">NAME                   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                              STORAGECLASS   REASON   AGE
</span></span><span class="line"><span class="cl">adguard-conf-pv        1Gi        RWX            Retain           Bound    adguard/adguard-conf-pvc           longhorn                20d
</span></span><span class="line"><span class="cl">adguard-work-pv        2Gi        RWX            Retain           Bound    adguard/adguard-work-pvc           longhorn                20d
</span></span><span class="line"><span class="cl">code-server-pv         500Mi      RWO            Retain           Bound    vsc/code-server-config-pvc         longhorn                20d
</span></span><span class="line"><span class="cl">dashy-config-pv        200Mi      RWO            Retain           Bound    dashy/dashy-config-pvc             longhorn                20d
</span></span><span class="line"><span class="cl">grafana-pv             10Gi       RWO            Retain           Bound    grafana/grafana-pvc                longhorn                20d
</span></span><span class="line"><span class="cl">influxdb-data-pv       10Gi       RWO            Retain           Bound    influxdb/influxdb-data-pvc         longhorn                20d
</span></span><span class="line"><span class="cl">jellyseerr-config-pv   512Mi      RWO            Retain           Bound    jellyseerr/jellyseerr-config-pvc   longhorn                20d
</span></span><span class="line"><span class="cl">radarr-config-pv       1Gi        RWO            Retain           Bound    radarr/radarr-config-pvc           longhorn                20d
</span></span><span class="line"><span class="cl">sabnzbd-config-pv      512Mi      RWO            Retain           Bound    sabnzbd/sabnzbd-config-pvc         longhorn                20d
</span></span><span class="line"><span class="cl">sonarr-config-pv       1Gi        RWO            Retain           Bound    sonarr/sonarr-config-pvc           longhorn                20d
</span></span><span class="line"><span class="cl">wikijs-config-pv       512Mi      RWO            Retain           Bound    wikijs/wikijs-config-pvc           longhorn                20d
</span></span><span class="line"><span class="cl">wikijs-data-pv         10Gi       RWO            Retain           Bound    wikijs/wikijs-data-pvc             longhorn                20d
</span></span></code></pre></div><h1 id="perform-restoration-on-cluster-2">Perform restoration on Cluster 2</h1>
<p>Verify the same backups are reflected. Patch the backup-location as read-only to avoid overwriting the backups:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zsh" data-lang="zsh"><span class="line"><span class="cl">kubectl patch backupstoragelocation default <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --namespace velero <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --type merge <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --patch <span class="s1">&#39;{&#34;spec&#34;:{&#34;accessMode&#34;:&#34;ReadOnly&#34;}}&#39;</span>
</span></span></code></pre></div><p>First we will restore all RWO volumes. Since we are changing the storageclass from longhorn to ceph-rbd, we have to create a configmap to allow this conversion. Apply 1_velero-change-sc-configmap.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="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">ConfigMap</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">change-storage-class-config</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">velero</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">velero.io/plugin-config</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">velero.io/change-storage-class</span><span class="p">:</span><span class="w"> </span><span class="l">RestoreItemAction</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">longhorn</span><span class="p">:</span><span class="w"> </span><span class="l">ceph-rbd</span><span class="w">
</span></span></span></code></pre></div><p>Execute the restoration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">v restore create --from-backup all-rwo-fs-backup
</span></span></code></pre></div><p>Wait for the pods to go up and running.

    <img src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/watchpods.png" alt="Watch kubectl get pods">
  </p>
<p>Next we update the config map to migrate longhorn storageclasses to cephfs. Apply 2_velero-change-sc-cephfs-configmap.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="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">ConfigMap</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">change-storage-class-config</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">velero</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">velero.io/plugin-config</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">velero.io/change-storage-class</span><span class="p">:</span><span class="w"> </span><span class="l">RestoreItemAction</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">longhorn</span><span class="p">:</span><span class="w"> </span><span class="l">cephfs</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">v restore create --from-backup all-rwx-fs-backup
</span></span></code></pre></div><p>You can check the restoration status with</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">v restore get
</span></span></code></pre></div><p>
    <img src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/restorecomplete.png" alt="Restore complete">
  </p>
<p>Have a look on your PVs and make sure everything is there. Check the storageclass as well.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">❯ k get pv
</span></span><span class="line"><span class="cl">NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                              STORAGECLASS   REASON   AGE
</span></span><span class="line"><span class="cl">pvc-002f450b-1765-4a2d-8900-916f83ac4fec   1Gi        RWO            Delete           Bound    sonarr/sonarr-config-pvc           ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-5613d3f3-473a-47ed-8e3f-8bedd3cb36c4   10Gi       RWO            Delete           Bound    grafana/grafana-pvc                ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-5d088e43-fd9e-4e08-ac4c-c095a681b18b   500Mi      RWO            Delete           Bound    vsc/code-server-config-pvc         ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-80d7d785-9c82-4995-808e-dfb7985c910e   512Mi      RWO            Delete           Bound    jellyseerr/jellyseerr-config-pvc   ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-853f057a-c851-474d-8825-3bfbe2eb6573   10Gi       RWO            Delete           Bound    wikijs/wikijs-data-pvc             ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-8f5a7b54-cffa-4453-909a-519e9dc74f83   512Mi      RWO            Delete           Bound    wikijs/wikijs-config-pvc           ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-a5734f5f-7f10-4e22-bcb0-9c2ab4d95023   10Gi       RWO            Delete           Bound    influxdb/influxdb-data-pvc         ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-b9a70ed6-1a60-4e2b-a1fe-1de19d64c1f5   512Mi      RWO            Delete           Bound    sabnzbd/sabnzbd-config-pvc         ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-c7d935b3-2194-43ac-96b7-5eb184cb716a   200Mi      RWO            Delete           Bound    dashy/dashy-config-pvc             ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-f0d2af55-b1d9-4d76-b929-03007c787529   1Gi        RWO            Delete           Bound    radarr/radarr-config-pvc           ceph-rbd                168m
</span></span><span class="line"><span class="cl">pvc-f7e848cb-4ce8-47c9-8a81-65775fd1c747   1Gi        RWX            Delete           Bound    adguard/adguard-conf-pvc           cephfs                  161m
</span></span><span class="line"><span class="cl">pvc-fa9a9b75-48e8-48f0-8e53-75105b80b913   2Gi        RWX            Delete           Bound    adguard/adguard-work-pvc           cephfs                  161m
</span></span></code></pre></div><p>We should be able to see ceph logs by now continuously generating:</p>
<p>Ceph logs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">2024-03-24T10:25:57.822352+0900 mgr.pve2 (mgr.1064599) 46085 : cluster 0 pgmap v46023: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 17 KiB/s wr, 2 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:25:59.822721+0900 mgr.pve2 (mgr.1064599) 46086 : cluster 0 pgmap v46024: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 19 KiB/s wr, 3 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:01.823161+0900 mgr.pve2 (mgr.1064599) 46087 : cluster 0 pgmap v46025: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 27 KiB/s wr, 4 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:03.823408+0900 mgr.pve2 (mgr.1064599) 46088 : cluster 0 pgmap v46026: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 19 KiB/s wr, 3 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:05.823949+0900 mgr.pve2 (mgr.1064599) 46089 : cluster 0 pgmap v46027: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 29 KiB/s wr, 4 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:07.824176+0900 mgr.pve2 (mgr.1064599) 46090 : cluster 0 pgmap v46028: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 22 KiB/s wr, 2 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:09.824519+0900 mgr.pve2 (mgr.1064599) 46091 : cluster 0 pgmap v46029: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 23 KiB/s wr, 3 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:11.824919+0900 mgr.pve2 (mgr.1064599) 46092 : cluster 0 pgmap v46030: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 51 KiB/s wr, 6 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:13.825139+0900 mgr.pve2 (mgr.1064599) 46093 : cluster 0 pgmap v46031: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 41 KiB/s wr, 5 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:15.825790+0900 mgr.pve2 (mgr.1064599) 46094 : cluster 0 pgmap v46032: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 53 KiB/s wr, 7 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:17.826087+0900 mgr.pve2 (mgr.1064599) 46095 : cluster 0 pgmap v46033: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 42 KiB/s wr, 6 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:19.826454+0900 mgr.pve2 (mgr.1064599) 46096 : cluster 0 pgmap v46034: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 44 KiB/s wr, 6 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:21.826811+0900 mgr.pve2 (mgr.1064599) 46097 : cluster 0 pgmap v46035: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 50 KiB/s wr, 7 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:23.827027+0900 mgr.pve2 (mgr.1064599) 46098 : cluster 0 pgmap v46036: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 21 KiB/s wr, 3 op/s
</span></span><span class="line"><span class="cl">2024-03-24T10:26:25.827427+0900 mgr.pve2 (mgr.1064599) 46099 : cluster 0 pgmap v46037: 97 pgs: 97 active+clean; 3.4 GiB data, 14 GiB used, 2.7 TiB / 2.7 TiB avail; 30 KiB/s wr, 4 op/s
</span></span></code></pre></div><h1 id="migrate-the-bgp-peers">Migrate the BGP peers</h1>
<p>The last but not the least is to remove the BGP peering with the old cluster and configure on the new one! If you are also using OPNsense and given you have the BGP peer configuration resources already created in the cluster, theh you just have to clone the existing BGP configuration (on OPNsense) for each of the old nodes, change the ASN number, and update the router ID to be in the new VLAN.

    <img src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/bgp.png" alt="OPNsense BGP peer configuration">
  </p>
<h1 id="onwards-with-ceph">Onwards with Ceph</h1>
<p>This sums up how I was able to migrate the storageclass from Longhorn to Ceph by performing disaster recovery to a new cluster. In the past I&rsquo;ve done a similar kind of operationbut by simply restoring longhorn volumes with the same storageclass. It was fairly easy and longhorn even allows you to use the last PV and PVC name used. The backups can be stored in either NFS or S3 compatible storage as well. But with Velero it just feels a lot more straightforward. Since operation is via CLI, it allows you to prepare backup and restore commands that can be kept as a playbook for future use. By simply copy-pasting you will be able to restore your entire cluster without having to navigate in the GUI. Though don&rsquo;t get me wrong - I am only saying this in relation to backup and restore. I haven&rsquo;t even operated Longhorn long enough!

    <img src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/ceph-pve-1.png" alt="Ceph dashboard on PVE">
  

    <img src="/posts/k8s-with-external-ceph-disaster-recovery-and-storageclass-migration/ceph-pve-2.png" alt="Ceph dashboard on PVE">
  </p>
<p>Talking about Ceph, I don&rsquo;t really have anything to say yet. I just know it has long been established as the grandfather of distributed storage. The first time I came across it was at work when we started to face DB latency issues which seemed to be due to the bluestore cache and OSD memory facing some kind of memory leak. Personally I just started using it as the storage backend of my RKE2 cluster and so far connecting to it with Rook has been a breeze. With very minimal workload, I still look forward to any new learning I can get running my small cluster at home.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Low-power NAS with HP T630 and Openmediavault</title>
      <link>https://luislogs.com/posts/low-power-nas-with-hp-t630-and-openmediavault/</link>
      <pubDate>Fri, 22 Mar 2024 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/low-power-nas-with-hp-t630-and-openmediavault/</guid>
      <description>Retro post from October 2023
I used to run my k3s cluster on top of my OPNsense box, an HP T630 thin client, and a Lenovo Mini PC. I&amp;rsquo;ve recently replaced both the former with two other Mini PCs from HP as well running on 6th-gen Intel i5 CPU. So I was again sitting on the fence whether I should just sell the T630 or try to find some other use case for it.</description>
      <content:encoded><![CDATA[<p><strong>Retro post from October 2023</strong></p>
<p>I used to run my k3s cluster on top of my OPNsense box, an HP T630 thin client, and a Lenovo Mini PC. I&rsquo;ve recently replaced both the former with two other Mini PCs from HP as well running on 6th-gen Intel i5 CPU. So I was again sitting on the fence whether I should just sell the T630 or try to find some other use case for it.</p>
<p>Though I really love the thin client. It&rsquo;s fanless (it doesn&rsquo;t emit any noise), has two M.2 SATA slots, and an internal USB3.0 port that can be used for another storage device. The only thing is it used to run quite hot when k3s together with longhorn was running. I guess this was not really meant for continuous workload but only for lightweight tasks such as web browsing&hellip; or so I thought a NAS!</p>
<p>Since I now have a small k3s cluster running 24/7, and now that winter is coming (hello higher electriciy rates!) I just thought it will be a good idea to shutdown my main server and NAS to save up on electricity costs. I can then convert the T630 from a Proxmox node to a dedicated NAS running TrueNAS or Openmediavault.</p>
<p>After debating with myself on the two NAS solutions, I finally decided to go with OMV. Reason is I wanted something as lightweight as possible. I don&rsquo;t really need the additional features of TrueNAS. Asisde from this OMV has a plugin which allows the use of system memory during run time to avoid wear and tear if I want to use a USB flash drive as my boot disk. With this I will be able to use the two M.2 SATA slots for my RAID0-ed drives.</p>
<p>
    <img src="/posts/low-power-nas-with-hp-t630-and-openmediavault/nvme.png" alt="NVME to USB3">
  </p>
<p>Just to note, at first I actually bought a USB to M.2 NVME adapter to have a mirrored RAID setup with 1 NVME drive and 1 SATA drive. Unfortunately the USB adapter would always reset and it seems like due to the NVME drive overheating.</p>
<p><code>dmesg</code> output</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[Wed Oct 11 00:49:04 2023] usb 3-1: reset SuperSpeed USB device number 2 using xhci_hcd
</span></span><span class="line"><span class="cl">[Wed Oct 11 00:49:04 2023] sd 2:0:0:0: [sdc] tag#0 FAILED Result: hostbyte=DID_TIME_OUT driverbyte=DRIVER_OK cmd_age=30s
</span></span><span class="line"><span class="cl">[Wed Oct 11 00:49:04 2023] sd 2:0:0:0: [sdc] tag#0 CDB: Read(10) 28 00 00 00 00 00 00 00 20 00
</span></span><span class="line"><span class="cl">[Wed Oct 11 00:49:04 2023] I/O error, dev sdc, sector 0 op 0x0:(READ) flags 0x80700 phys_seg 4 prio class 2
</span></span><span class="line"><span class="cl">[Wed Oct 11 00:52:25 2023] usb 3-1: reset SuperSpeed USB device number 2 using xhci_hcd
</span></span><span class="line"><span class="cl">[Wed Oct 11 00:52:25 2023] sd 2:0:0:0: [sdc] tag#0 FAILED Result: hostbyte=DID_TIME_OUT driverbyte=DRIVER_OK cmd_age=31s
</span></span><span class="line"><span class="cl">[Wed Oct 11 00:52:25 2023] sd 2:0:0:0: [sdc] tag#0 CDB: Read(10) 28 00 3b 9e 12 00 00 00 08 00
</span></span><span class="line"><span class="cl">[Wed Oct 11 00:52:25 2023] I/O error, dev sdc, sector 1000215040 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 2
</span></span><span class="line"><span class="cl">[Wed Oct 11 01:01:44 2023] usb 3-1: reset SuperSpeed USB device number 2 using xhci_hcd
</span></span><span class="line"><span class="cl">[Wed Oct 11 01:02:21 2023] usb 3-1: reset SuperSpeed USB device number 2 using xhci_hcd
</span></span></code></pre></div><p>I gave up on this and got myself another 500GB SATA SSD. The HP T630 has slots for a M.2 2280 and a 2230 drive. The challenge is finding a 2230 SATA drive that comes from the reliable manufacturers. Though with a 2280 size it&rsquo;s fairly easy so I decided to go for a second one instead.</p>
<p>
    <img src="/posts/low-power-nas-with-hp-t630-and-openmediavault/sata.jpg" alt="M.2 SATA drive on 2230 slot">
  </p>
<p>How did I screw it on the 2230 slot? Well, I didn&rsquo;t. I used an insulated tape to put it in place. Many would say it&rsquo;s unsafe especially if it will be used as a NAS, but I don&rsquo;t plan of storing any important files here. It will mainly be used as a quick low powered backup solution for my homelab - storing VM disk images, hosting an S3-compatible strage like MinIO, and maybe for running non-critical docker containers for monitoring.</p>
<p>
    <img src="/posts/low-power-nas-with-hp-t630-and-openmediavault/minio.png" alt="MinIO">
  </p>
<p>There is no special configuration required to run MinIO or docker containers since both come as readily-available plugins to download within OMV.</p>
<p>
    <img src="/posts/low-power-nas-with-hp-t630-and-openmediavault/omv.png" alt="Openmediavault Dashboard">
  </p>
<p>Also since this post should have been logged from October of 2023, I am just updating here that I have decided to break the RAID0 and use the two SATA drives separately. One used for docker containers, and the other one for MinIO. Over time I figured there is no good use case for me as of now to configure RAID0 on my drives since I already have my Unraid setup for bigger backups. But with two drives running independently, I do get the benefit of saving extra effort to restore the files if either of the two goes down!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Fixing Longhorn error FailedMount - exit status 32</title>
      <link>https://luislogs.com/posts/fixing-longhorn-error-failedmount-exit-status-32/</link>
      <pubDate>Thu, 14 Mar 2024 18:00:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/fixing-longhorn-error-failedmount-exit-status-32/</guid>
      <description>A couple of days ago I started facing Longhorn issues after rebooting all three nodes. For some reason my adguard deployment was stuck trying to mount the PV. I&amp;rsquo;m running my adguard deployment with RWX and this means it&amp;rsquo;s mounted over NFS.
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedMount 45m (x3 over 56m) kubelet Unable to attach or mount volumes: unmounted volumes=[adguard-conf-pv adguard-work-pv], unattached volumes=[], failed to process volumes=[]: timed out waiting for the condition Warning FailedMount 16m (x23 over 61m) kubelet MountVolume.</description>
      <content:encoded><![CDATA[<p>A couple of days ago I started facing Longhorn issues after rebooting all three nodes. For some reason my adguard deployment was stuck trying to mount the PV. I&rsquo;m running my adguard deployment with RWX and this means it&rsquo;s mounted over NFS.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Events:
</span></span><span class="line"><span class="cl">  Type     Reason       Age                 From     Message
</span></span><span class="line"><span class="cl">  ----     ------       ----                ----     -------
</span></span><span class="line"><span class="cl">  Warning  FailedMount  45m (x3 over 56m)   kubelet  Unable to attach or mount volumes: unmounted volumes=[adguard-conf-pv adguard-work-pv], unattached volumes=[], failed to process volumes=[]: timed out waiting for the condition
</span></span><span class="line"><span class="cl">  Warning  FailedMount  16m (x23 over 61m)  kubelet  MountVolume.MountDevice failed for volume &#34;adguard-work-pv&#34; : rpc error: code = Internal desc = mount failed: exit status 32
</span></span><span class="line"><span class="cl">Mounting command: /usr/local/sbin/nsmounter
</span></span><span class="line"><span class="cl">Mounting arguments: mount -t nfs -o vers=4.1,noresvport,intr,hard 10.43.20.191:/adguard-work-pv /var/lib/kubelet/plugins/kubernetes.io/csi/driver.longhorn.io/6c472e8b20509432a91f0d78010890b28fd5ebac0d9e85d0554ba7a69c9dccbd/globalmount
</span></span><span class="line"><span class="cl">Output: mount.nfs: Protocol not supported
</span></span><span class="line"><span class="cl">  Warning  FailedMount  6m50s (x21 over 61m)  kubelet  Unable to attach or mount volumes: unmounted volumes=[adguard-work-pv adguard-conf-pv], unattached volumes=[], failed to process volumes=[]: timed out waiting for the condition
</span></span><span class="line"><span class="cl">  Warning  FailedMount  67s (x31 over 62m)    kubelet  MountVolume.MountDevice failed for volume &#34;adguard-conf-pv&#34; : rpc error: code = Internal desc = mount failed: exit status 32
</span></span><span class="line"><span class="cl">Mounting command: /usr/local/sbin/nsmounter
</span></span><span class="line"><span class="cl">Mounting arguments: mount -t nfs -o vers=4.1,noresvport,intr,hard 10.43.25.161:/adguard-conf-pv /var/lib/kubelet/plugins/kubernetes.io/csi/driver.longhorn.io/e5cf370525e8a07eda18f130064a2fb3712c6f4668904245d8622230e3defd8e/globalmount
</span></span><span class="line"><span class="cl">Output: mount.nfs: Protocol not supported
</span></span></code></pre></div><p>At first I was having some doubt on the NFS package — if it got updated and started causing issues with Longhorn. But this wasn&rsquo;t the case. Googling online landed me to one github issue that was opened fairly recently (<a href="https://github.com/longhorn/longhorn/issues/6857">link here</a>). <a href="https://github.com/andrewheberle">andrewheberle</a> shared a comment about facing the same issue after upgrading the kernel to 5.15.0-94.</p>
<p><a href="https://www.kolide.com/features/checks/ubuntu-unattended-upgrades">https://www.kolide.com/features/checks/ubuntu-unattended-upgrades</a></p>
<p>Checking my apt history, it looks like Ubuntu upgraded automatically last 8th of February: <code>less /var/log/apt/history.log</code></p>
<p>
    <img src="/posts/fixing-longhorn-error-failedmount-exit-status-32/photos/apt_history.png" alt="">
  </p>
<p>This looks to be due to unattended upgrades that are enabled by default upon OS installation.</p>
<p>You can check this by executing: <code>cat /etc/apt/apt.conf.d/20auto-upgrades</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">APT::Periodic::Update-Package-Lists &#34;1&#34;;
</span></span><span class="line"><span class="cl">APT::Periodic::Unattended-Upgrade &#34;1&#34;;
</span></span></code></pre></div><p>In my case I disabled both updating of package list and unattended upgrades by changing the value from <strong>1</strong> to <strong>0</strong>.</p>
<p>Before we start the rollback, let&rsquo;s check the available linux kernel images with <code>dpkg -l | grep linux-image</code>. We should be able to see our target kernel version. In this case 5.15.0-92 should be there.</p>
<p>
    <img src="/posts/fixing-longhorn-error-failedmount-exit-status-32/photos/dpkg.png" alt="">
  </p>
<p>After verification then we can start uninstallation of the new kernel image. On the next reboot, the kernel should be running with the next latest available kernel version.</p>
<p>Refeferences:</p>
<ul>
<li><a href="https://www.geekersdigest.com/how-to-downgrade-to-a-lower-kernel-version-in-ubuntu-linux/">https://www.geekersdigest.com/how-to-downgrade-to-a-lower-kernel-version-in-ubuntu-linux/</a></li>
<li><a href="https://askubuntu.com/questions/1303568/downgrade-kernel-by-deleting-new-one-from-boot">https://askubuntu.com/questions/1303568/downgrade-kernel-by-deleting-new-one-from-boot</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sudo apt remove linux-headers-5.15.0-97-generic -y
</span></span><span class="line"><span class="cl">sudo apt remove linux-headers-5.15.0-97 linux-headers-5.15.0-97-generic linux-modules-5.15.0-97-generic linux-image-unsigned-5.15.0-97-generic -y
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo apt remove linux-headers-5.15.0-94-generic -y
</span></span><span class="line"><span class="cl">sudo apt remove linux-headers-5.15.0-94 linux-headers-5.15.0-94-generic linux-modules-5.15.0-94-generic linux-image-unsigned-5.15.0-94-generic -y
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># then reboot
</span></span><span class="line"><span class="cl">sudo shutdown -r now
</span></span></code></pre></div><p>
    <img src="/posts/fixing-longhorn-error-failedmount-exit-status-32/photos/apt_remove.png" alt="">
  </p>
<p>Post reboot check the version:
<code>uname -r</code></p>
<p>The longhorn volumes are also successfully mounted this time with no errors.</p>
<p>P.S. This known issue seems to be logged already in <a href="https://longhorn.io/kb/troubleshooting-rwx-volume-fails-to-attached-caused-by-protocol-not-supported/">this Longhorn KB article</a></p>
]]></content:encoded>
    </item>
    
    <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>Secure HTTP access to services with Traefik, Cert-manager and Cloudflare</title>
      <link>https://luislogs.com/posts/secure-http-access-to-services-with-traefik-cert-manager-and-cloudflare/</link>
      <pubDate>Sun, 26 Nov 2023 09:20:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/secure-http-access-to-services-with-traefik-cert-manager-and-cloudflare/</guid>
      <description>Before I started working on spinning up my 3-node K3s cluster, I was under the impression that Traefik would be one of the easiest to migrate from my docker setup since it already had some kind of native integration with Kubernetes in terms of available custom resources. Unfortunately this wasn&amp;rsquo;t the case as with my personal experience. Reading through the custom values yaml file and referring to the available documentation to figure out how the docker configuration compares to when deploying in K8s wasn&amp;rsquo;t as straightforward as I expected.</description>
      <content:encoded><![CDATA[<p>Before I started working on spinning up my 3-node K3s cluster, I was under the impression that Traefik would be one of the easiest to migrate from my docker setup since it already had some kind of native integration with Kubernetes in terms of available custom resources. Unfortunately this wasn&rsquo;t the case as with my personal experience. Reading through the custom values yaml file and referring to the available documentation to figure out how the docker configuration compares to when deploying in K8s wasn&rsquo;t as straightforward as I expected. The part where I took time the most was when I was working on setting up automatic TLS certificate provisioning for the backend services I was running.</p>
<p>I got everything working eventually after multiple iterations of trial and error, and upgrading my traefik helm release countless times. And after finding this <a href="https://traefik.io/blog/secure-web-applications-with-traefik-proxy-cert-manager-and-lets-encrypt/">Traefik blog post</a> where they explain how it all actually works and can be configured. At this point I&rsquo;ve migrated all of my docker applications into my k8s cluster, and reading through the Traefik documentation again, it just makes a lot more sense now.</p>
<p>I guess the most important bit of the documentation is the part where they mentioned the requirement of using Cert-manager when using Traefik Proxy in Kubernetes.
Quoting from the documentation:</p>
<blockquote>
<p><strong>LetsEncrypt Support with the Ingress Provider</strong>
By design, Traefik is a stateless application, meaning that it only derives its configuration from the environment it runs in, without additional configuration. For this reason, users can run multiple instances of Traefik at the same time to achieve HA, as is a common pattern in the kubernetes ecosystem.
When using a single instance of Traefik Proxy with Let&rsquo;s Encrypt, you should encounter no issues. However, this could be a single point of failure. Unfortunately, it is not possible to run multiple instances of Traefik 2.0 with Let&rsquo;s Encrypt enabled, because there is no way to ensure that the correct instance of Traefik receives the challenge request, and subsequent responses. Previous versions of Traefik used a KV store to attempt to achieve this, but due to sub-optimal performance that feature was dropped in 2.0.
If you need Let&rsquo;s Encrypt with high availability in a Kubernetes environment, we recommend using Traefik Enterprise which includes distributed Let&rsquo;s Encrypt as a supported feature.
If you want to keep using Traefik Proxy, LetsEncrypt HA can be achieved by using a Certificate Controller such as Cert-Manager. When using Cert-Manager to manage certificates, it creates secrets in your namespaces that can be referenced as TLS secrets in your ingress objects.</p>
</blockquote>
<p>Since we are working in K8s, almost everything you can think of can be abstracted. For this instance, the TLS configuration is abstracted from the helm custom values yaml file and instead can be pegged to an Ingress resource when using Cert-manager.</p>
<p>To summarize in a few lines:</p>
<ul>
<li>Install Traefik so it can act your Ingress controller.</li>
<li>Install Cert-manager so it can monitor your Ingress resources, and based on the TLS configuration and with some annotations, it will automatically initiate the DNS challenge, create the TLS certificates sand save them as a secret in the application namespace.</li>
<li>Create Ingress resources to access your backend servcies while at the same time terminate HTTPS traffic (with Traefik).</li>
</ul>
<p>
    <img src="/posts/secure-http-access-to-services-with-traefik-cert-manager-and-cloudflare/traefik_1.png" alt="Traefik and Cert-manager flow">
  </p>
<p>Do note that while Traefik is an ingress controller and comes with its own IngressRoute CRD, it cannot be used together with Cert-manager at the moment. In this case we have to use the default Kubernetes Ingress resource.</p>
<p>In this post I will show how I configured Traefik and Cert-manager (patterned to the Traefik blog post!) to be used with Cloudflare. I will also show how you can have another layer of security by having multiple entry points and explicitly defining one during the ingress configuration. Though before proceeding, do note that I assume you already have an idea how to configure dynamic DNS and port-forwarding on Cloudflare and on your router.</p>
<h2 id="traefik">Traefik</h2>
<p>Recently I&rsquo;ve started using ArgoCD for managing all my applications including those that are installed via helm charts. For now we will install Traefik using helm.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm repo add traefik https://traefik.github.io/charts <span class="c1"># Add the Traefik repository</span>
</span></span><span class="line"><span class="cl">helm repo update <span class="c1"># Update the repositories</span>
</span></span><span class="line"><span class="cl">helm show values traefik/traefik &gt; values.yaml <span class="c1"># Download the custom values yaml</span>
</span></span></code></pre></div><p>I would recommend you to browse through the custom values yaml file to get familiarized with the available configuration and other features of Traefik. If you want to get started quickly then you can follow the below yaml configuration:</p>
<p><em>values.yaml</em></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">deployment</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">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">terminationGracePeriodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">60</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">minReadySeconds</span><span class="p">:</span><span class="w"> </span><span class="m">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">ingressRoute</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dashboard</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">matchRule</span><span class="p">:</span><span class="w"> </span><span class="l">Host(`traefik.yownowndomain.com`) &amp;&amp; PathPrefix(`/dashboard`) || Host(`traefik.yownowndomain.com`) &amp;&amp; PathPrefix(`/api`)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">entryPoints</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;traefik&#34;</span><span class="p">,</span><span class="s2">&#34;websecure&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">additionalArguments</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="s2">&#34;--serversTransport.insecureSkipVerify=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="s2">&#34;--providers.kubernetesingress.ingressendpoint.publishedservice=traefik/traefik&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><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">traefik</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="m">9000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">expose</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">exposedPort</span><span class="p">:</span><span class="w"> </span><span class="m">9000</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">web</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="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">expose</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">exposedPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</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">redirectTo</span><span class="p">:</span><span class="w"> </span><span class="l">websecure</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">webext</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="m">8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">expose</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">exposedPort</span><span class="p">:</span><span class="w"> </span><span class="m">8080</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">redirectTo</span><span class="p">:</span><span class="w"> </span><span class="l">websecureexternal</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">websecure</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="m">443</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">expose</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">exposedPort</span><span class="p">:</span><span class="w"> </span><span class="m">443</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">http3</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">false</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">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">options</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">certResolver</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">middlewares</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">default-default@kubernetescrd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">websecureext</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="m">8443</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">expose</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">exposedPort</span><span class="p">:</span><span class="w"> </span><span class="m">8443</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">http3</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">false</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">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">options</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">certResolver</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">middlewares</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">default-default@kubernetescrd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">metrics</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="m">9100</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">expose</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">exposedPort</span><span class="p">:</span><span class="w"> </span><span class="m">9100</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">dnsovertls</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="m">8853</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">expose</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">exposedPort</span><span class="p">:</span><span class="w"> </span><span class="m">853</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">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">enabled</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">middlewares</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">default-default@kubernetescrd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">tlsOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">default</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">minVersion</span><span class="p">:</span><span class="w"> </span><span class="l">VersionTLS12</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">cipherSuites</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305</span><span class="w">
</span></span></span><span class="line"><span class="cl"><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">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">single</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">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="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">10.80.0.2</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></code></pre></div><p>In my custom yaml file notice that I have four different entryPoints defined. I configured it like this to have separate incoming streams between my internal-only applications against the internet-exposed ones. Having a common entryPoint for both internal and externally exposed applications is a security flaw since hackers might still be able to access your internal-only apps if they guessed the sub-domain and locally configured CNAME forwarding (they don&rsquo;t need access to your DNS configuration).</p>
<p>Once your custom values yaml file is ready. Hit up the helm install command and proceed to the next step to create a Middleware resource.</p>
<p><em>default-middleware.yaml</em></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">traefik.io/v1alpha1</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">Middleware</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">traefik</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">headers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">customResponseHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">X-Robots-Tag</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;none,noarchive,nosnippet,notranslate,noimageindex&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">X-Forwarded-Proto</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">customRequestHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">X-Forwarded-Proto</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">sslProxyHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">X-Forwarded-Proto</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">referrerPolicy</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;same-origin&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">hostsProxyHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;X-Forwarded-Host&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">contentTypeNosniff</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">browserXssFilter</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">forceSTSHeader</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">stsIncludeSubdomains</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">stsSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">63072000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">stsPreload</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div><p><code>helm install traefik/traefik -f values.yaml</code></p>
<p><code>kubectl apply -f default-middleware.yaml</code></p>
<p>Now that you have Traefik up and running, expose the <code>webext</code> and <code>websecureext</code> entryPoints on your router. Simply port-forward 80 to 8080, and 443 to 8443.</p>
<h2 id="cloudflare">Cloudflare</h2>
<p>Going to Cloudflare, you have to configure an access token to be used later on when configuring Cert-manager. Login to your Cloudflare account. Navigate to My Profile &gt; API Tokens &gt; Create Token.</p>
<p>Under Permissions, set Zone - DNS - Edit</p>
<p>Under Zone Resources, set Include - Specific zone - &ldquo;yourowndomain.com&rdquo;</p>
<p>E.g.

    <img src="/posts/secure-http-access-to-services-with-traefik-cert-manager-and-cloudflare/cf_1.png" alt="Cloudflare Access Token">
  </p>
<p>Create and save your token somewhere safe.</p>
<h2 id="cert-manager">Cert-manager</h2>
<p>Cert-manager can be installed using helm. Quoting from the official documentation, Cert-Manager together with the CRDs can be installed with the following commands:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm repo add jetstack https://charts.jetstack.io
</span></span><span class="line"><span class="cl">helm repo update
</span></span><span class="line"><span class="cl">helm install <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  cert-manager jetstack/cert-manager <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --namespace cert-manager <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --create-namespace <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --version v1.13.2 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --set <span class="nv">installCRDs</span><span class="o">=</span><span class="nb">true</span>
</span></span></code></pre></div><p>After installation, it&rsquo;s time to configure cert-manager for use with Cloudflare. You simply just have to create a secret containing your cloudflare email and API token:</p>
<p><em>cloudflare-api-token-secret.yaml</em></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">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">Secret</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">cloudflare-api-token</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">cert-manager</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">Opaque</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">stringData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">example@domain.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">apiToken</span><span class="p">:</span><span class="w"> </span><span class="l">AbcD321eFg456_</span><span class="w">
</span></span></span></code></pre></div><p>Next is to create a ClusterIssuer resource. This is one of the CRDs that will come with cert-manager. Here you have to specify your email to be used with Let&rsquo;s Encrypt as well as the secret you created in the previous step.</p>
<p><em>clusterissuer.yaml</em></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">cert-manager.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">ClusterIssuer</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">letsencrypt-cluster-issuer</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">cert-manager</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">acme</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">example@domain.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Prod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://acme-v02.api.letsencrypt.org/directory</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># We use the staging server here for testing to avoid hitting</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#    server: https://acme-staging-v02.api.letsencrypt.org/directory</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">privateKeySecretRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># if not existing, it will register a new account and stores it</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">letsencrypt-acc-key</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">solvers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">dns01</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># The ingressClass used to create the necessary ingress routes</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cloudflare</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">example@domain.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">apiTokenSecretRef</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">cloudflare-api-token</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">apiToken</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">dnsZones</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="s1">&#39;yourowndomain.com&#39;</span><span class="w">
</span></span></span></code></pre></div><p>If you are doing this the first time, I strongly suggest you first use the staging server of Let&rsquo;s Encrypt and ensure certificate issuance works fine. To switch to the porduction server all you have to do is just re-create the resource and point to the new URL. Do note in the example above, the resource is pointing to the production server.</p>
<h2 id="exposing-your-application">Exposing your application</h2>
<p>To test this we create a simple nginx deployment that is exposed within the cluster and an ingress resource.</p>
<p><em>nginx.yaml</em></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">apps/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">Deployment</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">nginx-deployment</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">app</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</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">selector</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</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</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">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">app</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</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">containers</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">nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:latest</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">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</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="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">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">name</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</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">selector</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</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</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">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http&#34;</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="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</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">ClusterIP</span><span class="w">
</span></span></span></code></pre></div><p>When creating the ingress manifest, you have to ensure that you have the annotation: <code>cert-manager.io/cluster-issuer: &quot;letsencrypt-cluster-issuer&quot;</code>.</p>
<p>Apart from this if you want your application to be accessed from a specific traefik entryPoint, then you need to explicitly mention this as another annotation. By default the ingress will be allowed from any entryPoint if you don&rsquo;t specify this. In the below example manifest, I am keeping this annotation commented out so I can access my Nginx app from both internal and external entryPoints.</p>
<p><em>nginx-ingress.yaml</em></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">nginx</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="c"># traefik.ingress.kubernetes.io/router.entrypoints: websecureext</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">nginx.yownowndomain.com</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-nginx-ingress</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">nginx.yownowndomain.com</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">nginx</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>Once you apply these manifests, simply do a <code>kubectl get certificate</code>. This will show that a new certificate and secret resource got automatically created.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">kubectl get certificate
</span></span><span class="line"><span class="cl">NAME                READY   SECRET              AGE
</span></span><span class="line"><span class="cl">tls-nginx-ingress   False   tls-nginx-ingress   61s
</span></span></code></pre></div><p>Wait for a few minutes. You should see that cerficicate ready status should be set to True.</p>
<p>You can also describe this certificate to see the events.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Events:
</span></span><span class="line"><span class="cl">  Type    Reason     Age   From                                       Message
</span></span><span class="line"><span class="cl">  ----    ------     ----  ----                                       -------
</span></span><span class="line"><span class="cl">  Normal  Issuing    91s   cert-manager-certificates-trigger          Issuing certificate as Secret does not exist
</span></span><span class="line"><span class="cl">  Normal  Generated  90s   cert-manager-certificates-key-manager      Stored new private key in temporary Secret resource &#34;tls-nginx-ingress-2mvlp&#34;
</span></span><span class="line"><span class="cl">  Normal  Requested  90s   cert-manager-certificates-request-manager  Created new CertificateRequest resource &#34;tls-nginx-ingress-1&#34;
</span></span><span class="line"><span class="cl">  Normal  Issuing    11s   cert-manager-certificates-issuing          The certificate has been successfully issued
</span></span></code></pre></div><p>To expose this on the internet, you will have to go back to Cloudflare and configure a CNAME forwarding to the sub-domain that points to your router&rsquo;s external IP, given that you have already configured dynamic DNS.</p>
<p>You can also test this by configuring a CNAME entry on your local network&rsquo;s DNS.</p>
<p>Once you ge this working you should be able to see a valid certificate when you access your nginx page.</p>
<p>
    <img src="/posts/secure-http-access-to-services-with-traefik-cert-manager-and-cloudflare/nginx.png" alt="Nginx TLS">
  </p>
<p>The certificate will have a validity of 3 months but you don&rsquo;t have to worry since Cert-manager will renew this automatically.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Troubleshooting low throughput on Proxmox</title>
      <link>https://luislogs.com/posts/troubleshooting-my-kubernetes-network/</link>
      <pubDate>Sat, 28 Oct 2023 12:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/troubleshooting-my-kubernetes-network/</guid>
      <description>When I initially spinned up my k8s cluster, I got everything working but I always experienced network disconnects. it turns out it&amp;rsquo;s due to my NIC. It was quite difficult to notice this or maybe I just really never doubted my hypervisor and the hardware. Eventually I thought of checking the dmesg logs from within proxmox.
Sep 29 14:54:58 pve1 kernel: e1000e 0000:00:1f.6 eno1: Detected Hardware Unit Hang: TDH &amp;lt;22&amp;gt; TDT &amp;lt;bb&amp;gt; next_to_use &amp;lt;bb&amp;gt; next_to_clean &amp;lt;22&amp;gt; buffer_info[next_to_clean]: time_stamp &amp;lt;100cb7acb&amp;gt; next_to_watch &amp;lt;23&amp;gt; jiffies &amp;lt;100cb7d99&amp;gt; next_to_watch.</description>
      <content:encoded><![CDATA[<p>When I initially spinned up my k8s cluster, I got everything working but I always experienced network disconnects. it turns out it&rsquo;s due to my NIC. It was quite difficult to notice this or maybe I just really never doubted my hypervisor and the hardware. Eventually I thought of checking the dmesg logs from within proxmox.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Sep 29 14:54:58 pve1 kernel: e1000e 0000:00:1f.6 eno1: Detected Hardware Unit Hang:
</span></span><span class="line"><span class="cl">                               TDH                  &lt;22&gt;
</span></span><span class="line"><span class="cl">                               TDT                  &lt;bb&gt;
</span></span><span class="line"><span class="cl">                               next_to_use          &lt;bb&gt;
</span></span><span class="line"><span class="cl">                               next_to_clean        &lt;22&gt;
</span></span><span class="line"><span class="cl">                             buffer_info[next_to_clean]:
</span></span><span class="line"><span class="cl">                               time_stamp           &lt;100cb7acb&gt;
</span></span><span class="line"><span class="cl">                               next_to_watch        &lt;23&gt;
</span></span><span class="line"><span class="cl">                               jiffies              &lt;100cb7d99&gt;
</span></span><span class="line"><span class="cl">                               next_to_watch.status &lt;0&gt;
</span></span><span class="line"><span class="cl">                             MAC Status             &lt;80083&gt;
</span></span><span class="line"><span class="cl">                             PHY Status             &lt;796d&gt;
</span></span><span class="line"><span class="cl">                             PHY 1000BASE-T Status  &lt;3800&gt;
</span></span><span class="line"><span class="cl">                             PHY Extended Status    &lt;3000&gt;
</span></span><span class="line"><span class="cl">                             PCI Status             &lt;10&gt;
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">Sep 29 14:55:07 pve1 kernel: e1000e 0000:00:1f.6 eno1: Reset adapter unexpectedly
</span></span><span class="line"><span class="cl">Sep 29 14:55:07 pve1 kernel: vmbr0: port 1(eno1) entered disabled state
</span></span><span class="line"><span class="cl">Sep 29 14:55:07 pve1 kernel: vmbr0v20: port 1(eno1.20) entered disabled state
</span></span><span class="line"><span class="cl">Sep 29 14:55:07 pve1 kernel: vmbr0v30: port 1(eno1.30) entered disabled state
</span></span><span class="line"><span class="cl">Sep 29 14:55:11 pve1 kernel: e1000e 0000:00:1f.6 eno1: NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None
</span></span><span class="line"><span class="cl">Sep 29 14:55:11 pve1 kernel: vmbr0: port 1(eno1) entered blocking state
</span></span><span class="line"><span class="cl">Sep 29 14:55:11 pve1 kernel: vmbr0: port 1(eno1) entered forwarding state
</span></span><span class="line"><span class="cl">Sep 29 14:55:11 pve1 kernel: vmbr0v20: port 1(eno1.20) entered blocking state
</span></span><span class="line"><span class="cl">Sep 29 14:55:11 pve1 kernel: vmbr0v20: port 1(eno1.20) entered forwarding state
</span></span><span class="line"><span class="cl">Sep 29 14:55:11 pve1 kernel: vmbr0v30: port 1(eno1.30) entered blocking state
</span></span><span class="line"><span class="cl">Sep 29 14:55:11 pve1 kernel: vmbr0v30: port 1(eno1.30) entered forwarding state
</span></span></code></pre></div><p>When I saw these messages I thought my switch was acting up. I was almost ready to purchase a new switch but then upon further googling I landed in this <a href="https://forum.proxmox.com/threads/e1000-driver-hang.58284/page-10">forum post</a>.</p>
<p>Turns out this specific model is known to be freezing from time to time when segmentation is done by the NIC. This comes enabled by default when installing Proxmox. A quick fix suggeested in the post which worked for me was to simply disable the TSO and GSO flags on the specific interface. On runtime this can be disabled by executing <code>ethtool -K eno1 tso off gso off</code>.</p>
<p>To make the changes permenant, the following line should be added in the /etc/network/interfaces file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">post-up /usr/bin/logger -p debug -t ifup &#34;Disabling segmentation offload for eno1&#34; &amp;&amp; /sbin/ethtool -K $IFACE tso off gso off &amp;&amp; /usr/bin/logger -p debug -t ifup &#34;Disabled offload for eno1&#34;
</span></span></code></pre></div><p>e.g.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"></code></pre></div><p>One more issue I faced was very slow throughput when playing videos from Jellyfin.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/troubleshooting-my-kubernetes-network/proxmox2.png" alt="Low throughput on Proxmox">
      <figcaption>Low throughput on Proxmox</figcaption>
    </figure>
  </p>
<p>Noticed that throughput was very low regardless whether I&rsquo;m using the docker traefik or the one in my kubernetes cluster, while the original traefik container on my Unraid</p>
<p>After drilling down that issue seems to be specific to my Proxmox node, I checked whether the 1Gbit/s speed was properly negotiated as suggested in this other <a href="https://forum.proxmox.com/threads/network-speed-limited-to-100mbit-s.73311/">forum post</a>.</p>
<p>Executing <code>ethtool eno1</code> showed me the following output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@pve1:~# ethtool eno1
</span></span><span class="line"><span class="cl">Settings for eno1:
</span></span><span class="line"><span class="cl">	Supported ports: [ TP ]
</span></span><span class="line"><span class="cl">	Supported link modes:   10baseT/Half 10baseT/Full
</span></span><span class="line"><span class="cl">	                        100baseT/Half 100baseT/Full
</span></span><span class="line"><span class="cl">	                        1000baseT/Full
</span></span><span class="line"><span class="cl">	Supported pause frame use: No
</span></span><span class="line"><span class="cl">	Supports auto-negotiation: Yes
</span></span><span class="line"><span class="cl">	Supported FEC modes: Not reported
</span></span><span class="line"><span class="cl">	Advertised link modes:  10baseT/Full
</span></span><span class="line"><span class="cl">	Advertised pause frame use: No
</span></span><span class="line"><span class="cl">	Advertised auto-negotiation: Yes
</span></span><span class="line"><span class="cl">	Advertised FEC modes: Not reported
</span></span><span class="line"><span class="cl">	Speed: 10Mb/s
</span></span><span class="line"><span class="cl">	Duplex: Full
</span></span><span class="line"><span class="cl">	Auto-negotiation: on
</span></span><span class="line"><span class="cl">	Port: Twisted Pair
</span></span><span class="line"><span class="cl">	PHYAD: 1
</span></span><span class="line"><span class="cl">	Transceiver: internal
</span></span><span class="line"><span class="cl">	MDI-X: on (auto)
</span></span><span class="line"><span class="cl">	Supports Wake-on: pumbg
</span></span><span class="line"><span class="cl">	Wake-on: g
</span></span><span class="line"><span class="cl">        Current message level: 0x00000007 (7)
</span></span><span class="line"><span class="cl">                               drv probe link
</span></span><span class="line"><span class="cl">	Link detected: yes
</span></span></code></pre></div><p>Disabled auto-negotiation and configured 1Gbit manually:
<code>ethtool -s eno1 speed 1000 duplex full autoneg off</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@pve1:~# ethtool eno1
</span></span><span class="line"><span class="cl">Settings for eno1:
</span></span><span class="line"><span class="cl">	Supported ports: [ TP ]
</span></span><span class="line"><span class="cl">	Supported link modes:   10baseT/Half 10baseT/Full
</span></span><span class="line"><span class="cl">	                        100baseT/Half 100baseT/Full
</span></span><span class="line"><span class="cl">	                        1000baseT/Full
</span></span><span class="line"><span class="cl">	Supported pause frame use: No
</span></span><span class="line"><span class="cl">	Supports auto-negotiation: Yes
</span></span><span class="line"><span class="cl">	Supported FEC modes: Not reported
</span></span><span class="line"><span class="cl">	Advertised link modes:  1000baseT/Full
</span></span><span class="line"><span class="cl">	Advertised pause frame use: No
</span></span><span class="line"><span class="cl">	Advertised auto-negotiation: Yes
</span></span><span class="line"><span class="cl">	Advertised FEC modes: Not reported
</span></span><span class="line"><span class="cl">	Speed: 1000Mb/s
</span></span><span class="line"><span class="cl">	Duplex: Full
</span></span><span class="line"><span class="cl">	Auto-negotiation: on
</span></span><span class="line"><span class="cl">	Port: Twisted Pair
</span></span><span class="line"><span class="cl">	PHYAD: 1
</span></span><span class="line"><span class="cl">	Transceiver: internal
</span></span><span class="line"><span class="cl">	MDI-X: off (auto)
</span></span><span class="line"><span class="cl">	Supports Wake-on: pumbg
</span></span><span class="line"><span class="cl">	Wake-on: g
</span></span><span class="line"><span class="cl">        Current message level: 0x00000007 (7)
</span></span><span class="line"><span class="cl">                               drv probe link
</span></span><span class="line"><span class="cl">	Link detected: yes
</span></span></code></pre></div><p>After having these in place, my cluster has been running perfectly fine for almost a month now with nohiccups!</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>
    
    <item>
      <title>State of the Network — the first 120 days</title>
      <link>https://luislogs.com/posts/state-of-the-network-the-first-120-days/</link>
      <pubDate>Sun, 16 Jul 2023 00:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/state-of-the-network-the-first-120-days/</guid>
      <description>The past months have been crazy since the induction of my homelab. There have been so much reading here and there and a bulk of what used to be my idle time has since been allotted to technical research and self-development. My writing has not been able to keep up either because there have been a lot of changes and modifications I’ve been doing from the get-go.
The network just reached the fourth month mark and it’s already about to undergo a somewhat major re-design.</description>
      <content:encoded><![CDATA[<p>The past months have been crazy since the induction of my homelab. There have been so much reading here and there and a bulk of what used to be my idle time has since been allotted to technical research and self-development. My writing has not been able to keep up either because there have been a lot of changes and modifications I’ve been doing from the get-go.</p>
<p>The network just reached the fourth month mark and it’s already about to undergo a somewhat major re-design. But just before going to the next chapter of the journey, I want to take this time to log the current state of the existing infrastructure and services. Don&rsquo;t get me wrong — I don&rsquo;t run a full-fledged data center, nor do I even have a server rack setup. I only have photos and videos as critical files and I only have two users at home including myself (lol). It&rsquo;s called a homelab for a reason. Though it would still be fun to document the progress be it big or small and know how far I&rsquo;ve come along the way.</p>
<h2 id="homelab-networking-and-hardware">Homelab Networking and Hardware</h2>
<p>On high level, below is the current diagram of existing services, subnets, VLANs, and devices.</p>
<p>



	




































  	
	

	
		<script src="/shortcode-gallery/jquery-3.7.0.min.js"></script>
	
	
	
		<script src="/shortcode-gallery/lazy/jquery.lazy.min.js"></script>
	

	<script src="/shortcode-gallery/swipebox/js/jquery.swipebox.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/swipebox/css/swipebox.min.css">

	<script src="/shortcode-gallery/justified_gallery/jquery.justifiedGallery.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/justified_gallery/justifiedGallery.min.css"/>


<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-0-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-0" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/diagram/diagram.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="388"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABUAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AO/utevIdae2VkESyhTkZOOK1Tr&#43;lg4N0v8A3yax73RLmXWmnyux5Aw&#43;YZwPqatnwpAf&#43;XqUf5P&#43;NePTljYym1G&#43;ul/02O&#43;aw7Ubu2nQvLr&#43;mOwVboEk4A2n/Crn2iP3/I1jx&#43;F4Y5VkF3KSrA4wK3a78PKvJP20UvT/AIdnNVVJW9m7kLAmVY3wVOT3HSpqib/j5T/dNS10mIUUUUAf/9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/diagram/diagram_hub7bb2a9756cb13f6f6e108173f8af10e_121105_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-0");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<table>
<thead>
<tr>
<th>VLAN</th>
<th>Traffic type</th>
<th>Services</th>
</tr>
</thead>
<tbody>
<tr>
<td>Untagged</td>
<td>Local</td>
<td>Local devices, DNS</td>
</tr>
<tr>
<td>10</td>
<td>Docker</td>
<td>Traefik, UptimeKuma, InfluxDB, Grafana, Code-server, Firefox, Guacamole, JDownloader, Jellyfin, Krusader, Wiki.js</td>
</tr>
<tr>
<td>20</td>
<td>VM</td>
<td>Setagaya Workstation (PopOS), GNS3 server (Ubuntu 22.04), Voyager-staging (staging environment for remote server), Windows 10</td>
</tr>
<tr>
<td>30</td>
<td>LXC</td>
<td>Tailscale, Deluge</td>
</tr>
</tbody>
</table>
<p>Orange boxes signify the servers running a hypervisor — one Proxmox, and the other, Unraid. The Proxmox box is mainly running my network stack which includes OPNsense, a LXC container running Pihole as my DNS, and another one running tailscale for a machine-to-machine VPN with the remote peer acting as an exit node. While it might be possible to run tailscale from within OPNsense, I decided to configure it separately since I only have a simple use case of tunneling torrent traffic to a small thin client back home in Manila.</p>
<p>On the other hand, the box running Unraid primarily runs our NAS and a couple of docker containers and VMs. It&rsquo;s been very stable, running without any hiccup except for the time when all containers and VMs together with all the other devices were on a single subnet. This is what pushed me to segregate the traffic with VLANs. Speaking of, this is easily made possible by a cheap “smart” (you can say it’s managed) switch from TP-Link. I can’t stress enough how much I love this brand! I’ve been using their routers for more than a decade now and it has never failed me.</p>
<p>Back to the Unraid server, it had also undergone some few upgrades (the initial specs can be found in one of my first few posts <a href="https://luislogs.com/posts/new-nas-and-homelab-setup-using-unraid/">here</a>). First is the addition of a 500GB SSD as an un-mirrored cache drive to avoid wear and tear of the NVMe drives. This is also mainly used for non-critical data such as when storing movies, tv shows, and ISO files, etc.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-1-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-1" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/ssd/samsung_860_evo_res.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>9mm f/2.4 0.0303sec ISO 1000"
						

						
					
					>
					<img			
						width="600" height="452"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AKniV7W98SEyFg80fyKq5UKoxj/x01W03xFFFaxizj0&#43;1UpgRm2O8&#43;5IJyT1/Gtk6KLjV4tUZ3UrA4VOCCCZDz6f/WpBpdvPaR3DRfPICxwAMjJ9qvUjRmQ3iS6nOy80WKdH4ybUEAe5DGsOePQr68lQWs1m4fDHzPkySBnnnH0Irs7nQ7axiaVHTdnKgOD&#43;mOa88nYK2oEbjtlPyk8YEg/&#43;vQ79QVj0jStQtRo9q3mwDbAFl/fLlThjz7/NUP8Ab1pawxxRXVrII12jLDJH51ymm/8AILuvx/lWR2X/AHaYHaT&#43;JYpIzHIlsgfo&#43;4ZxmubbSZZ4ZihYJdPvD7TtUFgS270AFUbr7tv/ALn9a6y3/wCRah/69JP5Gna7E3ZH/9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/ssd/samsung_860_evo_res_huf4f05a43168f222215b51547a553bbe1_223148_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-1");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>Second is the installation of a third party heat sink for the NVMe drives which brought down temperature levels by about 5-10C.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-2-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-2" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
					
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/nvme/20230408_204511_res.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>6.7mm f/1.8 1/100sec ISO 320"
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/ANrRZYpL1Z4IEjEEe1/MT5wuOBn3IB9Dg1xOtrHqGtTtHGyw3ThVl6hGJ647Z4/WumuBcPoMJa5VL&#43;VcTlH&#43;QZ52sBxwCATjqeODXG3Nld28wW4uLtCSeIZdgfnjB6U0imza1bw6umWYvLCa4dchprZXHz4/iBI/HHPesT&#43;0T/z43v8A30n/AMRT/tFwkf2jy7gtCyCLzJd6L13A5PccYHvUn/CQ3/8Az7Wn/fof4UwE0/xM8ey2uLYEBduCcn6j1rpDNaz2oktpI7q3PH2ecfPGf9n06dOlYWq6fGQxlRSSeCOh/H/P1rMtrqe0k3Qt5pj6Rufm/A96TVthKV9y9qk8enySrDKVhk&#43;Yw9drDufX8Pasv&#43;2j/wA9z/35FU7y6/tG7IxuMhAYDjZ1zx&#43;FH9jRf7X50Bc//9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/nvme/20230408_204511_res_hu43516cdb8668a12f12b3ea1f05263d5c_436164_600x600_fit_q90_r270_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
					
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/nvme/20230408_211735_res.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>6.7mm f/1.8 1/50sec ISO 200"
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/ALUNtGttLpsdrH5kikeVtGxgf4V9gT/XvynjfVAtxbW9lIsbg4lO4jLY45HcDP6VFq&#43;sHR9JWTzFfUJGbyRGc&#43;VH0wxPU47&#43;9ctHerfRNvtDISxyBLtZSe4JBzkjv/XlobJ9L1u90SSN5ExayzFHZw2046kH14rov&#43;E7032/76P&#43;FVjqtpcaUNLkgljeVAhYlSIs/KrbgeuT&#43;hrN/wCEST/oOSf9/qbKV0Y93512wm3MJo1AKE5AHt7f56VDZwvc3aLEFjmJxzjb75zxj2q3JMiONzcZ&#43;SReqn/Pb/JeNQishNH5Ki6mT5ZAeCM/w/X0/wAiRC3Xlx2TQsGt7gSkTFCQG2kgcdj/AC7VnfJ/z83H/f3/AOtVcGXUbg7pi0aISWBzz/Co/HFJ9iuf&#43;eT/APfApiP/2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/nvme/20230408_211735_res_hu56624e996b56981b76cdec0dcbc623bb_464411_600x600_fit_q90_r270_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-2");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>Third, the installation of a 330W UPS from APC, allowing graceful shutdown in case of power outages and protection from potential power surges.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-3-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-3" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/ups/1_ups_res.jpg" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="450"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AL1qQwFQ69qMWmwxOAHnBJRCfYjJqvaalagD9&#43;n51zfiLUIZ9RmZZVYYAGD7Csk0aSTLGhefqd3cvJcOJZA5D5PynaORjp&#43;Faug3k19paSTtvdSVZsdcGsLw1eQQRzSzKHRVkJGe232q/wCF72yTSmDTpH&#43;9YhTk4H1xVuS5SUnckOnWjxhYLCCH1K5NZGoeFHlZpbVir/3W6H/Cult&#43;gqwvU1we0lud3s4vQ4S2sdTthLCdPnYsrL9w7eRjrVnTNPvLO1Zbi3K5bOAc4rs2/pVG5&#43;6amdVtWHCkou5//9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/ups/1_ups_res_hu1a161ca91e3ac2cccc634b7bf858ba3d_182636_600x600_fit_q90_lanczos.jpg"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/ups/2_ups_res.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>6.7mm f/1.8 0.02sec ISO 640"
						

						
					
					>
					<img			
						width="338" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAEgMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AM7xDax2vi251IOVliZNmGx1UDI/WodH1a9vNYMbTu8Wwsyudx/M/Wq/iC7F7JZXyHK3K5POcMGIIz7YqXwpA3n3EzqRlQqk9/XH6VhFOKtLc6GtdDqd1G6mUZo0KOY8JzwyRPYXVv5sBk3xyOvyhvT2/wD11097Ja2sIWXAP8Cr1z7VhXWp2Vi4sLNEnuVABVB8kQzjn1x6Vjaa&#43;qHW7gajJ5qtz5zcAegX29qirapUc7mtNuEFGx0oub0gELHjtnOaPtF9/di/WqZ1O4DEIqbQflyO1J/al1/dT8qXtYB7KZ//2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/ups/2_ups_res_huf71a6b935fc345b86480270ad6e6bd23_144654_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-3");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>Fourth, the installation of a Noctua NH-L12S cooler to pull down the temperature levels of the CPU.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-4-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-4" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
					
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/cooler/1_20230414_082641_res.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>9mm f/2.4 1/40sec ISO 250"
						

						
					
					>
					<img			
						width="600" height="450"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOZ0uzvNZkVXtH8pH&#43;aUcDIGcKc8H/Gun1K6hWOKKSKG63gZjILLM2M577W5HI4zmsjw/qdrb6YYGui0isXILbVjJHOD36ds8k9KSed/tbTuMD7kJ65B5J/I/m1S2WtS3PpFpeKRZPucnDQSkZP&#43;6e/41z95YyxERHI2ttBbKyRk8AZ9PrWx9pPnEyAMtr8zlhnfIR0H5D/vk&#43;tTaZqFzex3QvVS7hilaON9oWXHOcMT0HHfn1prUbOWez2HMWJEOfkPamwyy2MgmhcgRfNsdSR&#43;Xb8Pzq7D/rP&#43;An&#43;VU7r/AJbf9cGprUh6Ei6sL20eGK38sqDja2fmPf8AAcfjXb2OhxJo9tbyP5U0ZEpl/wBrrtPt2rzjResn1/rXrcn3PxrCs7JJESk0f//Z"
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/cooler/1_20230414_082641_res_huf87a463cb09a0cea23be80e2f1f1a80b_474943_600x600_fit_q90_r180_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
					
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/cooler/2_20230414_082705_res.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>9mm f/2.4 1/30sec ISO 500"
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/ALNtqUEMKG4DAkfLsG7djrj1pX16xmOIlnGTgEx4ya4zStUuNLldY0VtygYkchcsc5J7d66P&#43;27zyw8VrbbM/KQrMR7Ajj1/MUSqSXQcYRZTN1qFtr4njiTzDGI1R2HzL17d8itT&#43;29b/wCgfD/30a5yfVWfUkuLqKN2jicIEY/K5ztJ&#43;hP55pn9v3/94/8AfVJarUGtTDvNRuLZ1t1kkimhJV8N/h361tCKZ7RLg3s/zICN0pAyRWgvgfTVYec93McdRhR&#43;WKG8KaTbn95eyQ8cB5l/lipcospQkZTx/wCjsZrmNwOceZkmqebb&#43;6n/AH0K2joGhhs/2hdSf7KAY/UYp39iaF/z0vfyj/wqk/ITR//Z"
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/cooler/2_20230414_082705_res_hu49ab095f1864478d40c29bb9b66e2441_398705_600x600_fit_q90_r270_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
					
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/cooler/3_20230530_225224_res.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>9mm f/2.4 1/33sec ISO 1250"
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOasr0uRPLbuohg&#43;zySA5yNm0foR&#43;Qqq7wnR4bQTgNHcNIu5SN6kDp1Gcj1qe1uXmW7tGwI5Zgdu0ZHI6H8BVtNMji8OXV48MfmGZQueBjAzj3y2fwocrbglcii1OYad/Z/2UqpBDSnjgmqf2aP/AJ6fr/8AWqxfLENOMS&#43;WZHZVAQZPJHfpWb/Y83/PJ6asSoLsaj6bfaUrXl3biNJmygDBvfnHHY//AFquyTLN4VWP/VymbLox&#43;ZsEc49MAVrXenajEzSW87SRBi4g2jGfoev1rn7&#43;4MtiZTpv2W6E2zyVOExjJbbiptc1fukkSreXVnbWyAqlyjyFB8u0HJ5PWuu&#43;xWvp/KuV0LRb&#43;8lS6uJ5IYl&#43;6V&#43;XPso/rXSf2Mn/AD&#43;Xf/f0f4Um0gje2x//2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/cooler/3_20230530_225224_res_hudf69c4cf3ed8ba448c812763d788cb4e_411171_600x600_fit_q90_r270_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-4");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>Lastly, a RAM upgrade to 64GB after experiencing memory shortage when working with GNS3. 32GB wasn’t enough when running multiple containers and spawning multiple VMs on top of GNS3.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-5-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-5" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			
				
				
				
				
				
				
				
				
					
				
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/ram/1_ram_res.jpg" 
					class="galleryImg"
					
						

						
							data-description="SM-G998U1 + <br/>6.7mm f/1.8 1/100sec ISO 250"
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AMIV0WnwaXp1rG2oQLLLI3mZIzsA6fh&#43;eTn0rCjLWul3WprAZ2hAWNAMhWP8bf7K/wA8UQa5b3slu92wjl4Uyof3TEDAwOx49xnHToYZtGLabtoja1XQ0u5pZ7NI1XJOYz8vXoy/wn36H2rK/sC89I/&#43;&#43;q2bvUhYtGkL7J0XMm3og7L&#43;XFQf8JTcf89T/wB8CnYzuZV08sCoLWVlMaboxCeS3Tk9sZ/8e96ff&#43;HYptPS5kmt7G8dQJSVxBITxyB9089Rx7Cucg1SaNik7PyNpJ&#43;8v0zU814VtIoRctLGMlFzwvvQ1cqEnF3T1LYSOxto7NJPOdMmWUHIZvQew6D8aZ5orPFz70v2g0xH/9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/ram/1_ram_res_hufdd28577ce19d830899ab8bcd38bcc78_482657_600x600_fit_q90_r270_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/ram/2_ram_res.jpg" 
					class="galleryImg"
					
						

						
							data-description=" + <br/>mm f/ sec ISO "
						

						
					
					>
					<img			
						width="499" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGwMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOR1vTGtNVEV5dCIPCGJRSQT0IAz&#43;NRQRWVvqEQs7iWUksrFkAGMcY9f89a63wF4fj8TwTT6rEZ5bc7S0jsScnOT9ckf8Brup/BumraultZ21ufLPzhFBB9zjNaRnaNmOpHmbaPMreBp47lCTnyjt9jkYrnhpaP8zXBBPUEcg9&#43;9eqnwpa29rdE6jBloWAA5rzeVVnlaQzRJk/dM4U&#43;nTFZO19QpxdrIk8C&#43;ImsNXEX2uW3im2q7g/dGRk/kTXrF1rGg2VtNJPLdX3lozNlic4HOOgr53s5fJuo3JwM4J9AeDXa3HiS0FkIpmYvJDscDtlcVbvbQzk3dHV3F00tncKGyWiOK89TRVIO642EEggoexx6VbHiuKWIWwjPKbC2awpHhEjAIMZ9TSsFNNRsz/9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/ram/2_ram_res_huca1ae1b4e8d54e36d067af99f25c9a74_475419_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-5");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<h2 id="services">Services</h2>
<p>Talking about the services, I am running just a handful. For the monitoring stack I have Telegraf, InfluxDB and Grafana to monitor resource utilization on the Proxmox box. I also have UptimeKuma providing a neat and simple dashboard for monitoring service uptime.</p>




	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-6-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-6" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/monitoring/1_uptimekuma_res.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="331"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABIAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APNo9St1t4UmsoJGjXG478kc9cSD19BVG5kSa4aRI1iU9EXOB&#43;ZJ/Wp5j/oNqNuPlPODz8x/D8qrVdyUkMwPU0cVYthmQ5GePf8ApUtwoEDHaR/31/WkO6FnVRp9mQACUOTjr87VUq5cf8g6y/65t/6G1U6bJRNbAGQ5GeKluVAgbAH5VFa/60/7tTXX/Hu34VLB7n//2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/monitoring/1_uptimekuma_res_hu1a8cd94fc8c4692e0bc15b97fcd4c373_160624_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/monitoring/2_influxdb_res.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="331"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABIAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APOr&#43;ZOJFOSSCQRx1&#43;tUGYTsA8a5OBu5yP1qT5ppY1kIjDEDcQcVfsdBnv3k&#43;xg3BjxkowQD/vrFdDTk9EZpqK1Mh7eNRw/fH86fH5aIcwJLlerbuPfgitmfQr62tRcT2pEYILEyIe&#43;PX1NVI1i8uQ&#43;SWwhAwobHv9KXJKL1ViuaLV4u5XlACrwOv9a6TwjI8Zutjsududpx61zkv3E&#43;v9a6Hwn1uf8AgP8AWtaP8RGNX&#43;Gy3q8sh0WUGRiMDgn/AGqwrV3TTm2sVyGBwcZra1b/AJA0v0/9mrEt/wDkHH6NW1R&#43;&#43;vQyp/A/U//Z"
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/monitoring/2_influxdb_res_hu1a8cd94fc8c4692e0bc15b97fcd4c373_556669_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/monitoring/3_grafana_res.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="318"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABEAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APMzGzcgL&#43;LCmmB/Uf8AfYpWA6fN93kEf4UERngLkle2etMBBA46Ef8AfQoMTqM7gMejCpCsAgwUPm7s55wBiogqE9&#43;/rQIP8KQdaX/CkHWmBO/T/gJqGP734Gpn6f8AATUMf3vwNAH/2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/monitoring/3_grafana_res_hu63c49fef1e2dae93cad841d816f78d1b_253743_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-6");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<p>For resource monitoring of my Unraid box, I just use the built-in dashboard which already gives me all the information I need such as CPU and disk temps, CPU, disk, and memory utilization, and power consumption.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-7-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-7" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/monitoring-unraid/1_unraid-mon.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="332"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABIAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APR00LRizbtFsDzwfsyf1p8nh3RNpI0exBwcYtk/wq8DhSdoyG9KUOSSD6daAMoeHtCDwBtJsSxB4NtHlunPT/Oalt/D2jKmTo9iG/69UH9KvIJF2ZkUg9c9TUgOOGbJ7cYoAoo7E4LHHXrTyzAE5PT1qOP734U89DTAZPI4CEOw/Gk82Qx/6xunrST/AHUpB/q/woA//9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/monitoring-unraid/1_unraid-mon_hue41aad0413aa76a74a02c2bf36d14a4b_582667_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/monitoring-unraid/2_unraid-mon.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="332"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABIAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APR4tA0Vix/sfTyd2M/ZkP8ASpG8O6Jt50ew4/6dU/wq8rgjnI2se9LuUggHOeaAMweHtCkAI0Wx9/8ARYx/SpV8PaKuCujWAx/07J/hV8HKjGRTf4h8pPPU0AZyyyYb9433vWpUdjv&#43;Y/nUC9G/3qmT&#43;OmApkfaPnbr60wSPtb526nvSn7o&#43;tMH3W/GgD//2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/monitoring-unraid/2_unraid-mon_hue41aad0413aa76a74a02c2bf36d14a4b_504967_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-7");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>For productivity and utilities, I have Guacamole for VM and command line remote access, Krusader for file management within Unraid, Visual Studio Code for modifying configuration files and blogging, Firefox for VM-less browser access, Wiki.js for saving important information and notes taking of anything related to the Homelab and the projects I am working on, Jdownloader for HTTP downloads (mainly ISO files), Heimdall as my homelab dashboard, and Traefik as my reverse proxy!</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-8-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-8" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/utils/1_guac.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="271"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAA4AIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AO58Z6h4o0oW58K6Nb3zPnzhIn3R2/iWql3e&#43;N7rwIlzb2CWuvmUb4I0TCrk9N5I6Y711looZnZcBc4K7RVvav8AdH5UragcNoUnjtvAmpyatF/xPxI/2NdkIyu1ccD5eu7rUnw&#43;fxrO2ojxlEFUeX9lBSIZ&#43;9v&#43;5/wHrXa7V/uj8qY5RCMr19qYH//Z"
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/utils/1_guac_hu4c837487343b5fe3682e8a3334c827b7_103706_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/utils/2_krusader.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="410"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABYAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AO8i8O6fOP8AkG2m4cn90Of0oOgaYkpT&#43;yrXI7&#43;WMfyrdth/o68H86qzf8fbdT9SPagZjQeGoZFIEFmCR3tF4/SpP&#43;EST/nlZf8AgIv&#43;Fb1oP9FTg9PWp8H0P51XMyeVGdHemNSvlKQnXmmvKryZMR3MM8ScfyqD/ntT/wDlov8Aun&#43;lQUMS51COIBZbXAOBmFj/AOz077Vqe4r5tnkDP&#43;ob/wCLpp/1X/A/60//AJat/u/1quZk8qP/2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/utils/2_krusader_huaaafed7fe20202f61a24db53af45dc7c_194670_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/utils/3_vsc.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="302"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABAAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APMJDnYfb&#43;tStymD0z0pkgRVRWVg4HPPvTyRt69/WgCPA9KUAc8dqTPvS8j1oA//2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/utils/3_vsc_hu3d37b6522c29224fef3ad43ec2887029_135962_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/utils/4_wikijs.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="304"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABAAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AIvB/h3R9Q8RRW91p8M0TI5KSDIPyk816DJ4E8LRQrjQrGPJ9AoP51yHgP8A5GqD/rm//oJr0&#43;cBoQTzg969HMIxjWSS6HDgZSlSbb6nPR&#43;BvCUp2SaHp5Pbo2amHw88IjpoNkPpHW0uVOUjjBBxkKBViSVIYjJI21VHJrz99jtP/9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/utils/4_wikijs_hu10ff007e12759842d19c799afe3059c7_113635_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/utils/5_jdownloader.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="301"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABAAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APSxbXmBlDywzyOn51MLe5VgEGFx7VaW6jbgA5&#43;o/wAaeJgT91qogqGG52nrn2xUEdpdOpExlHPaQj&#43;RrT8z/ZP6U1plAIIwcdyP8adwsf/Z"
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/utils/5_jdownloader_hu38b1bf95a3a937b3ec4197e4618f69ab_163272_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/utils/6_heimdall.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="297"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABAAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOP07wo15Ym6GoafGQCfIkuAspx/s479q6Cy8LtaQRr5&#43;h3Akb77uHK59T2FcnbanslEhClh0JAq/baukduYP3KqxGT5KcAe2K6PQyN3UfCT3RVFutEt2RvmKSBFZcdQe/NcjLpbB3QKcp&#43;taMut744f9TiPjb5a5xknjjjrVWfWXLSyKRuYcHA4oXmht3P/2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/utils/6_heimdall_hu6065ae11d5c1c2c1a3aee256cc67c41e_501701_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/utils/7_traefik.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="447"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABgAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APL3RcpulCluG&#43;9x9f8A61IIYj5n&#43;lINn3eG&#43;f6cfzxWhImnfZlbzFD7RjacknbzkY45xTLNbJ4n89kBz3OCBjqPU57VWt7XMfark5rMywSrBlOCDkEdqfLNLM26WRnbGMsc1ehFmL0AuDDk4Ld&#43;DjPH0ovFs1P7hwWz8wByo4HQ/XNF3a9y&#43;Zc/LYYsUUh3OQPlznPU4qNY4yDlR16&#43;lJ/CKB3qSx7xQpOVXDqDgE96HhhEQYY3FiNvoMCmdxQepoA//9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/utils/7_traefik_hu1e1757ea255f0316f25464b84ef4cd8d_72653_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-8");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>For media services, I have Jellyfin for transcoding and serving media both at home and when on-the-go. To populate my fiance and I&rsquo;s media inventory with videos from our favorite youtube channels, I use <a href="https://github.com/jmbannon/ytdl-sub">ytdl-sub</a>. This automatically downloads the videos of any defined youtube channel, manages the specified retention period, and adds the necessary metadata for a nice and smooth feel when browsing in Jellyfin. And since the videos are played back locally, this means we also get ad-free content when watching. For legal downloading of media, I have deluge. Since I don&rsquo;t use this often, I still transfer files manually to the Jellyfin directory.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-9-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-9" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/media/1_jf.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="299"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABAAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOGtdLt5Eie8v3hV4g5O3IBPQfXHNSroKmZY/thUORsLRdRgmr9voccVnpdxfa3LZxXMG5dzn5OmAB6HioY9MtYoorg37b95O9LtfmTBJ4AJB6Dv1qKlRKbgjSEbw531IbnRbC2ht5G1QO0km2SNcBo1yRuOeOCPWs68s1tYLiSO5YmOYIikD5lO7n/x2r19ZaU2nG5s7m5kmMjK0cjjLjLHPTpwuevJNUr7T0g0pLndKGZxwx478dOo/qaFd9SdFuj/2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/media/1_jf_huaba3c1dda0a76a1868af9be81be38773_929179_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/media/2_jf_tv.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="303"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABAAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOV8NaVDqd2Eu9Plli&#43;z7zs3gqdyAE88/eqjqNlZ2bxoHfLTTRyDhigV8DjjtnrnpXT&#43;HvCvhxra0n1W9TdLDudftGAMgEYA574OfQ1xsq6ZFq8sHzNbrNIFdX5wDxj2x/OojNNtFqk4RV3ctJbWUtrabxJbGTcXnZdynjjGPerV/o1ra&#43;EotViecTySBcYzHjJHX146c1Ys08M34t4vsa23BLyGZyxIBPrjngdOuK5jVY4oNVuYLdw0EcrBCDkEZ61t7NqHPfqZuac&#43;Wx//2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/media/2_jf_tv_hu0e30bbe655af32bdd924dafb51a46566_696393_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/media/3_ytdl-sub.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="304"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABAAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APMJD9wjuP61P1jxxz71FJtVERlbcBzzTyy7OefxoAYcDjatAA54HSkJHalwfSgD/9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/media/3_ytdl-sub_hu03a5145121c36bad98aaaf8ff801c5aa_156101_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-9");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>I am aware I am still lacking in the automated media management department by making use of the *ARR services, but I can’t find time to set them up especially now that this homelab environment keeps me branching out to different projects all at the same time. At the moment I just have bigger fish to fry.</p>
<h2 id="vms">VMs</h2>
<p>As for the VMs, I only have quite a few. My main workstation is running PopOS based on Ubuntu. I do use a Macbook at home but still login with RDP to this machine almost all the time since it allows me anytime-anywhere remote access and I can easily pick up where I left off when I am working on something.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-10-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-10" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/popos/1_popos.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="375"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABQAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/ALPiH4dwaLo0&#43;om5E/klR5RTG7LAZzk461w7fZ1JC2QB7EOTivbfiCCPBd/nnJjx/wB9rXijHjo361cqkm76fcgSICsZ624x9TXY&#43;Gvh1/bWm2mpfaGEc5bciwqQoDFTklwe2eneuQLr2XP4mvbfhun/ABRFk2Tg&#43;ZgZ/wCmjURqNPZfcgaPMdW&#43;IWvanpstreNbywSS7GTyscKQRyDnqKpq8LWglNpDu8sN1bqfxrDuP9Qf&#43;u7VsJ/yDx/1wX&#43;lbVopJWRKepO32fy7hvsUGY42ZfvdRn39qhsviX4i0exSxsHtobePOxPJBxkknk89Sakf/VXv/XF//Zq4&#43;XvU0Ypt3NI6xP/Z"
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/popos/1_popos_huaec8ce792172fe736a0955555a12f132_287881_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-10");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>Though 99% in shutdown state, I also keep a Windows 10 VM in case I need to use software which can’t run on Linux. I also keep a staging environment running on Debian for the remote tailscale exit node installed back home in Manila. Another one is an Ubuntu VM running GNS3 server for other side projects on kubernetes and networking. My VM disks are created in the NVMe drives allowing me to play around with VMs efficiently in terms of very quick load times. The NVMe drives are also mirrored in RAID1 so I don&rsquo;t have to worry about losing data when one of the two drives crashes.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-11-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-11" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/vms/1_vms.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="191"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAAoAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOwT4ceE5Fy2kKT05mk/&#43;Kpf&#43;FaeESOdHX/v9J/8VXSRfex29KmwPQUkNqzOUHw08I4H/EnXn/ptL/8AFU5fhx4UXkaSAQeP30v/AMVXU4HoKMD0FMR//9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/vms/1_vms_hua9e2729e8b72699486f64cb80d6c2ffb_57210_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-11");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>The last but not the least is my router! Running as a VM on Proxmox, it just works wonderfully. I did experience random reboots though, but this got fixed after upgrading the kernel from 5.4 to the new 6.1 together with the intel micro-code package. My longest uptime with it was around 40 days before I had to reboot for maintenance work. Inside OPNsense I am running Unbound caching DNS resolutions for faster downstream queries. The browsing experience became significanly better after enabling this. Websites started to load faster also after enabling DNS over HTTPS. I was trying to find an explanation to this but I will just leave it out in the cold for now. I also have my Cloudflare Dynamic DNS agent running here. For VPN, I have Wireguard allowing me unfiltered access to the entire network. I also use this on daily basis — tunneling at the very least my DNS traffic to Pihole for internet ads blocking.</p>
<p>



	





































<style>
	

	
</style>





<div id="gallery-ae85206290481cf425f0de9e51bc13d9-12-wrapper" class="gallery-wrapper">
<div id="gallery-ae85206290481cf425f0de9e51bc13d9-12" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/opnsense/1_proxmox.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="172"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAAkAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOEnk3GIsVYkDI59qgeNWySqg4OOfeol&#43;8v1qaT/AFR&#43;jfzoAhSQROGXCsOQRUpvpQdwkJPqarN1/AfyptAH/9k="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/opnsense/1_proxmox_hu404439e683d15f1a256076f01f33b5d1_114853_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/opnsense/2_pihole.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="341"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABIAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOJS/laVFuGeaIfeQucH8Kc1zZCQsLMFSCNvpVQD98NvWrhN6EyLZiP&#43;uX/1q0bbjYx5XGanFbfd92w63vLCFHSaw8xieGPBWoJJ7d9ypblAWyCPTPTFJPJMshE0KhgTnKinpJcnIWAfd5xGOn5Viqai7/qVUlOorSS/L9Cr0k44q19quP8AnvL/AN9mqh/1lTVqNpPck&#43;0THrM//fRpDPNgjzXwevzGmUGgLI//2Q=="
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/opnsense/2_pihole_huc1baf80b03675390fa184597999e401c_134483_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/state-of-the-network-the-first-120-days/opnsense/3_opnsense.png" 
					class="galleryImg"
					
					>
					<img			
						width="600" height="302"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIABAAIAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APS5PC&#43;hMihtIsCc43G3X1&#43;lNTwvoIYp/Ymn4HQ/ZVP9K1Xzt6/xH&#43;dJlg5IUkE45x&#43;dAGYfDGhKTjRtO4x/y6r/AIVc/s6yihSJbWER42BFT5QMdMelWXDEHA6470sgJCYycN60Af/Z"
							
							class="lazy"
							data-src="/posts/state-of-the-network-the-first-120-days/opnsense/3_opnsense_hu9a5b718c683341ed8bca172d4a1f4c76_139117_600x600_fit_q90_lanczos_3.png"
						

						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-ae85206290481cf425f0de9e51bc13d9-12");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<br></p>
<p>I think that&rsquo;s all of it for the network&rsquo;s status quo. Over the next few weeks I will have to wrap my head around Terraform, Ansible, and Kubernetes. This is because I&rsquo;ll be migrating most, if not all, of these services to a &ldquo;production&rdquo; k8s cluster with the help of automation. I&rsquo;ve been sitting on the fence about this for quite some time now, but I finally decided go with it to have hands-on experience with Infrastructure-as-Code. Is it necessary? Not really, but it sure is interesting.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Keepalived with Pihole for DNS HA</title>
      <link>https://luislogs.com/posts/keepalived-with-pihole-for-dns-ha/</link>
      <pubDate>Sun, 02 Jul 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/keepalived-with-pihole-for-dns-ha/</guid>
      <description>In my previous post about my DNS, I mentioned there that I migrated Pihole from Unraid to my Proxmox hosting my router. But in fact, on top of that, I left an instance of Pihole on Unraid running inside a LXC container. Together with that I also configured keepalived for high availability. Hosting the DNS on the same hypervisor as my router should already be sufficient (I&amp;rsquo;d say my OPNsense VM is more likely to face issues than a LXC container) but I still wanted to try out an use case for keepalived.</description>
      <content:encoded><![CDATA[<p>In my previous post about my DNS, I mentioned there that I migrated Pihole from Unraid to my Proxmox hosting my router. But in fact, on top of that, I left an instance of Pihole on Unraid running inside a LXC container. Together with that I also configured keepalived for high availability. Hosting the DNS on the same hypervisor as my router should already be sufficient (I&rsquo;d say my OPNsense VM is more likely to face issues than a LXC container) but I still wanted to try out an use case for keepalived.</p>
<h3 id="install-gravity-sync">Install Gravity Sync</h3>
<p>Keep in mind that the following IP addresses were used for the setup:</p>
<table>
<thead>
<tr>
<th>IP address</th>
<th>Hostname</th>
<th>Host</th>
</tr>
</thead>
<tbody>
<tr>
<td>10.0.0.80</td>
<td>pihole-ha-ip</td>
<td>-</td>
</tr>
<tr>
<td>10.0.0.81</td>
<td>pihole1b</td>
<td>Proxmox LXC</td>
</tr>
<tr>
<td>10.0.0.81</td>
<td>pihole2b</td>
<td>Unraid LXC</td>
</tr>
</tbody>
</table>
<p>
    <img src="/posts/keepalived-with-pihole-for-dns-ha/pihole-proxmox.png" alt="Proxmox Pihole">
  </p>
<p>
    <img src="/posts/keepalived-with-pihole-for-dns-ha/pihole-unraid.png" alt="Unraid Pihole">
  </p>
<p>I already had one pihole instance running on 10.0.0.80. I just had to shut this down to test my new HA-configured pihole.</p>
<p>To keep the configuration of both piholes in sync, we can install <a href="https://github.com/vmstan/gravity-sync/wiki/Installing">gravity-sync</a>. I faced permission issues when I ran this as root. Make sure you have <code>PermitRootLogin yes</code> set in the <code>/etc/ssh/sshd_config</code> file. Then only execute below installation script.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sSL https://raw.githubusercontent.com/vmstan/gs-install/main/gs-install.sh <span class="p">|</span> bash
</span></span></code></pre></div><p>The installation procedure is straightforward. Just input the required information and wait for it to complete. You can then conduct a quick test of pulling or pushing configuration.</p>
<p>Create a new domain in pihole1 and ensure it&rsquo;s not reflected on pihole2.</p>
<p>
    <img src="/posts/keepalived-with-pihole-for-dns-ha/keepalived_test.png" alt="Test configuration">
  </p>
<p>You can then execute <code>gravity-sync compare</code> to see if there is any configuration mismatch.</p>
<p>
    <img src="/posts/keepalived-with-pihole-for-dns-ha/gravity_compare.png" alt="Gravity Sync Compare">
  </p>
<p>Then you can execute <code>gravity-sync pull</code> from the pihole2 and check if the delta configuration is reflected.</p>
<p>
    <img src="/posts/keepalived-with-pihole-for-dns-ha/gravity_pull.png" alt="Gravity Sync Pull">
  </p>
<h3 id="automate">Automate</h3>
<p>Execute <code>gravity-sync auto</code> on both pihole instances.</p>
<p>By default pihole should sync after 5 mins. For my setup I will set it hourly.</p>
<p><code>gravity-sync auto hour</code></p>
<h3 id="install-keepalived">Install keepalived</h3>
<p>I found this tutorial on reddit <a href="https://www.reddit.com/r/pihole/comments/d5056q/tutorial_v2_how_to_run_2_pihole_servers_in_ha/">link here</a> shared by <a href="https://www.reddit.com/user/Panja0/">Panja0</a> on how to make use of keepalived by monitoring the pihole-FTL service with a simple bash script. I wanted to try and do it from scratch to add to my knowledge but I have decided to park this for now and test out this tutorial. For those interested, the keepalived documentation can be found <a href="https://keepalived.readthedocs.io/en/latest/installing_keepalived.html">here</a>.</p>
<p>Install libipset13 and keepalived.</p>
<p><code>yum install -y libipset13 keepalived</code></p>
<p>Create a new scripts directory in /etc.</p>
<p><code>mkdir /etc/scripts</code></p>
<p>Create a new file and copy-paste the content from <a href="https://pastebin.com/npw6tcuk">https://pastebin.com/npw6tcuk</a>.</p>
<p>Add permision to execute the file:</p>
<p><code>chmod u+x /etc/scripts/chk_ftl</code></p>
<p>You need to edit keepalived.conf on both pihole instances. This file is located in <code>/etc/keepalived/</code>. The config files can be downloaded in the following links:
<a href="https://pastebin.com/nsBnkShi">keealived.conf for master</a>
<a href="https://pastebin.com/HbdsUc07">keealived.conf for master</a></p>
<p>Modify the following fields according to your IP configuration. The auth_pass field is a password that should be match between both configuration files.
unicast_src_ip
unicast_peer
auth_pass
virtual_ipaddress</p>
<p>Restart the keepalived service and now the IP should be assigned to your primary pihole!</p>
<p><code>sudo systemctl restart keepalived</code></p>
<p>You should now be able to see DNS queries coming in.</p>
<p>
    <img src="/posts/keepalived-with-pihole-for-dns-ha/pihole2a-running.png" alt="">
  </p>
<h3 id="testing-for-auto-fail-over">Testing for auto fail-over</h3>
<p>Shutdown the other pihole. Now pihole2b started accepting traffic</p>
<p>
    <img src="/posts/keepalived-with-pihole-for-dns-ha/pihole-failover.png" alt="">
  </p>
<p>Let’s see if it will preempt. Poweron other pihole and check the keepalived. You can also check 10.0.0.80/admin (virtual ip) and see if it’s routed to the primary .</p>
<p>
    <img src="/posts/keepalived-with-pihole-for-dns-ha/pihole-preempt.png" alt="">
  </p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Deploying my first 4G EPC network</title>
      <link>https://luislogs.com/posts/deploying-my-first-4g-epc-network/</link>
      <pubDate>Sun, 25 Jun 2023 00:01:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/deploying-my-first-4g-epc-network/</guid>
      <description>For the past few weeks, I&amp;rsquo;ve been working on making our home network stable after experiencing random reboots on my router and bombing the connection at home just right before leaving for work. All problems have been fixed now since the start of this month so I&amp;rsquo;ve had the time to continue with this other home project. Continuing from where I got off on k3s, I finally attempted to run a 4G EPC network in a small virtualized cluster.</description>
      <content:encoded><![CDATA[<p>For the past few weeks, I&rsquo;ve been working on making our home network stable after experiencing random reboots on my router and bombing the connection at home just right before leaving for work. All problems have been fixed now since the start of this month so I&rsquo;ve had the time to continue with this other home project. Continuing from where I got off on k3s, I finally attempted to run a 4G EPC network in a small virtualized cluster.</p>
<p>Before going further, first, I&rsquo;d like to thank the author of the tutorial, Infinity Don over at Dev.to, for sharing this cool idea together with the docker images. Ever since my interest in virtualization and containerization started, I have been looking for a weekend past-time for getting my hands dirty with, and luckily I stumbled upon his <a href="https://dev.to/infinitydon/virtual-4g-simulation-using-kubernetes-and-gns3-3b7k">article here</a>.</p>
<p>The article details on how to get everything running but I somehow still faced few minor issues. I&rsquo;d still recommend to refer to the tutorial if you are someone who takes troubleshooting as a fun challenge.</p>
<p>I like recording everything I execute so if you have the intention of creating your own network and don&rsquo;t want to be spoiled, then I&rsquo;d suggest not to continue and just go read the tutorial above instead.</p>
<p>If you are just interested in how I got things working, then feel free to continue.</p>
<h2 id="the-objective">The objective</h2>
<p>In bullet points:</p>
<ul>
<li>Create a cluster for running EPC components as simple as possible.</li>
<li>Use k3s as the kubernetes platform to have a smaller resource footprint.</li>
<li>Use Calico as the CNI instead of flannel (comes with k3s by default).</li>
<li>Use GNS3 for network simulation so I can start getting familiar with it for future projects.</li>
<li>Learn packet core!</li>
</ul>
<h2 id="vms-and-networking">VMs and Networking</h2>
<p>
    <img src="/posts/deploying-my-first-4g-epc-network/20230625_2.png" alt="GNS3 network">
  </p>
<p>The k3s cluster is running on Ubuntu 22.04 VMs. Openairinterface5G as the RAN and UE simulator as in the tutorial, though both are bifurcated into two VMs (the tutorial makes use of a single VM with the help of a second loopback address). For the latter, Ubuntu 18.04 was used to avoid any incompatibility issue since the documentation stated only this version. All Ubuntu VMs are assigned 4GB RAM. Ubuntu 22 VMs are assigned 2 vCPUs, and the two Ubuntu 18 VMs, with 4 vCPUs to speed up the build time of the simulator. For the router, the same Vyos router is used with separate connections for the management interface and the internet. I could have made use of the management interface as my internet gateway but I faced slow throughput when downloading packages on the VMs. The suggestion I got in the GNS3 forum is to use a NAT interface instead.</p>
<h2 id="preparing-the-environment">Preparing the environment</h2>
<p>I am running a remote GNS3 server inside another Ubuntu VM. For this I had to enable nested virtualization (Intel VT-x in my case) in the BIOS. I also had to passthrough the CPU instruction set extensions to my GNS3 host, and from the host to the Ubuntu 18.04 VMs, to clear some errors I faced in the initial attempt of building the eNB and UE simulator. To make it easier I just configured CPU passthrough on both my hypervisor and the GNS3 server.</p>
<p>You can check if your VMs are having the CPU extended instruction set flags using the command below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat /proc/cpuinfo <span class="p">|</span> grep -i avx
</span></span></code></pre></div><p>VM images were created with already updated packages so I don&rsquo;t have to keep on updating everytime I have to re-instantiate a VM. For updating manually:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get update -y <span class="o">&amp;&amp;</span> sudo apt-get upgrade -y 
</span></span></code></pre></div><p>As for the kubernetes platform, I used k3s. It comes with flannel as the CNI but it can be installed with Calico. The steps I followed for this are logged in another <a href="https://luislogs.com/posts/install-k3s-with-calico-and-disable-traefik/">post</a>.</p>
<h2 id="vyos-configuration">Vyos configuration</h2>
<p>Configuring VyOS is fairly simple though prior CLI experience with routers such as cisco and the likes will surely help. The documentation can be found <a href="https://docs.vyos.io/en/equuleus/installation/virtual/gns3.html">here</a> for installing Vyos on GNS3, and the configuration guide in this <a href="https://docs.vyos.io/en/latest/configuration/index.html">link</a>.</p>
<p>To provide internet connectivity to my VMs I had to masquerade internal VM IPs towards the NAT GW. A static default route is set towards this GW, and the rest of routes are mainly for management access from the different external VLANs I have in my local network.</p>
<p>As for the BGP configuration, the command in the tutorial seems to be a little outdated. The following commands were used to enable BGP:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">set</span> protocols bgp neighbor 10.200.0.12 remote-as <span class="m">63400</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> protocols bgp system-as <span class="m">63400</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> protocols bgp neighbor 10.200.0.12 address-family ipv4-unicast 
</span></span></code></pre></div><p>I faced some minor timing issue when merging pcap files. To fix, all I had to do was to ensure the same NTP server is used across all nodes.</p>
<p>Snippet of my Vyos configuration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">interfaces <span class="o">{</span>
</span></span><span class="line"><span class="cl">    ethernet eth0 <span class="o">{</span>
</span></span><span class="line"><span class="cl">        address 10.200.0.1/24
</span></span><span class="line"><span class="cl">        hw-id 0c:0a:6b:f7:00:00
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    ethernet eth1 <span class="o">{</span>
</span></span><span class="line"><span class="cl">        address 172.16.0.1/24
</span></span><span class="line"><span class="cl">        hw-id 0c:0a:6b:f7:00:01
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    ethernet eth2 <span class="o">{</span>
</span></span><span class="line"><span class="cl">        hw-id 0c:0a:6b:f7:00:02
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    ethernet eth3 <span class="o">{</span>
</span></span><span class="line"><span class="cl">        address 10.20.0.20/24
</span></span><span class="line"><span class="cl">        hw-id 0c:0a:6b:f7:00:03
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    ethernet eth4 <span class="o">{</span>
</span></span><span class="line"><span class="cl">        address dhcp
</span></span><span class="line"><span class="cl">        hw-id 0c:0a:6b:f7:00:04
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    loopback lo <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">nat <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">source</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        rule <span class="m">100</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            outbound-interface eth4
</span></span><span class="line"><span class="cl">            <span class="nb">source</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                address 10.200.0.0/24
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            translation <span class="o">{</span>
</span></span><span class="line"><span class="cl">                address masquerade
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        rule <span class="m">101</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            outbound-interface eth4
</span></span><span class="line"><span class="cl">            <span class="nb">source</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                address 172.16.0.0/24
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            translation <span class="o">{</span>
</span></span><span class="line"><span class="cl">                address masquerade
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">protocols <span class="o">{</span>
</span></span><span class="line"><span class="cl">    bgp <span class="o">{</span>
</span></span><span class="line"><span class="cl">        neighbor 10.200.0.12 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            address-family <span class="o">{</span>
</span></span><span class="line"><span class="cl">                ipv4-unicast <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">            remote-as <span class="m">63400</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        system-as <span class="m">63400</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    static <span class="o">{</span>
</span></span><span class="line"><span class="cl">        route 0.0.0.0/0 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            next-hop 192.168.122.1 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        route 10.0.0.0/24 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            next-hop 10.20.0.1 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        route 10.10.0.0/24 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            next-hop 10.20.0.1 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        route 10.30.0.0/24 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            next-hop 10.20.0.1 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        route 10.120.0.0/24 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            next-hop 10.20.0.1 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        route 10.130.0.0/24 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            next-hop 10.20.0.1 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        route 10.140.0.0/24 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            next-hop 10.20.0.1 <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">service <span class="o">{</span>
</span></span><span class="line"><span class="cl">    ntp <span class="o">{</span>
</span></span><span class="line"><span class="cl">        allow-client <span class="o">{</span>
</span></span><span class="line"><span class="cl">            address 0.0.0.0/0
</span></span><span class="line"><span class="cl">            address ::/0
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        server ntp.ubuntu.com <span class="o">{</span>
</span></span><span class="line"><span class="cl">            prefer
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    ssh <span class="o">{</span>
</span></span><span class="line"><span class="cl">        port <span class="m">22</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><h2 id="installing-open5gs">Installing Open5gs</h2>
<p>Once the environment is ready, then it&rsquo;s time to install Open5gs. Clone the repository to your local directory from <a href="https://bitbucket.org/infinitydon/virtual-4g-simulator/src/master/open5gs/">Infinity Don&rsquo;s repository</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://bitbucket.org/infinitydon/virtual-4g-simulator/src/master/open5gs/
</span></span></code></pre></div><p>The yaml files are based on older versions of kubernetes, so to make this work with the latest one at the time of this writing, that is v1.26.3, I had to modify hss-database/mongo-statefulset.yaml. Just update the apiVersion to <code>rbac.authorization.k8s.io/v1</code> (remove <code>beta1</code>).</p>
<p>I had to specify the mongodb version as well in hss-database/monogo-statefulset.yaml to avoid mongodb going to crashloopback state (reference <a href="https://stackoverflow.com/questions/64458588/crashloopbackoff-mongo-in-docker-kubernetes-failed-to-start-up-wiredtiger-un">here</a>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</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">mongo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mongo:4.2.10</span><span class="w">
</span></span></span></code></pre></div><p>Port 2123 on the SMF had to be opened to allow GTP-C connections with SGW. Update smf/smf-deploy.yaml and add the following.</p>
<p><strong>smf-deploy.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="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">gtpc</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="m">2123</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>If BGP is to be used then this next step is optional. This how I established the connection of the eNB to the MME before the realizing I would need BGP eventually. Here I just modified the mme-deploy.yaml to expose the MME IP for the s1ap connectivity.</p>
<p><strong>mme-deploy.conf</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="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">name</span><span class="p">:</span><span class="w"> </span><span class="l">open5gs-mme-svc-pool</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">open5gs</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">epc-mode</span><span class="p">:</span><span class="w"> </span><span class="l">mme</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">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#  - name: s1ap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#    port: 36412</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#    protocol: SCTP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#    nodePort: 36412</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">s6a</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="m">3868</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">nodePort</span><span class="p">:</span><span class="w"> </span><span class="m">30001</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">NodePort</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">epc-mode</span><span class="p">:</span><span class="w"> </span><span class="l">mme</span><span class="w">
</span></span></span></code></pre></div><p>The <strong>mme-configmap.yaml</strong> was also updated to set TAC to 1.</p>
<p>For the BGP configuration, the same yaml files from the tutorial were used. These yaml files are not present in the repository. Created a new directory and two yaml files with following configuration:</p>
<p><strong>calico-bgpconfig.yaml</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apiVersion: projectcalico.org/v3
</span></span><span class="line"><span class="cl">kind: BGPConfiguration
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: default
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">  asNumber: <span class="m">63400</span>
</span></span></code></pre></div><p><strong>calico-bgppeer.yaml</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apiVersion: projectcalico.org/v3
</span></span><span class="line"><span class="cl">kind: BGPPeer
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">    name: bgppeer-vyos
</span></span><span class="line"><span class="cl">spec:
</span></span><span class="line"><span class="cl">    asNumber: <span class="m">63400</span>
</span></span><span class="line"><span class="cl">    peerIP: 10.200.0.1
</span></span></code></pre></div><p>Time to spin up the core! I tried to execute in small steps but then encountered pods getting stuck in crashloopback state. This is because each pod was trying to bring up the peer connectivity with other pods.</p>
<p>First create the namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl create ns open5gs
</span></span></code></pre></div><p>Then execute all at once!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f hss-database/
</span></span><span class="line"><span class="cl">kubectl apply -f web-ui/
</span></span><span class="line"><span class="cl">kubectl apply -f hss/
</span></span><span class="line"><span class="cl">kubectl apply -f mme/
</span></span><span class="line"><span class="cl">kubectl apply -f upf
</span></span><span class="line"><span class="cl">kubectl apply -f sgw-u/
</span></span><span class="line"><span class="cl">kubectl apply -f sgw-c/
</span></span><span class="line"><span class="cl">kubectl apply -f smf/
</span></span><span class="line"><span class="cl">kubectl apply -f pcrf/
</span></span><span class="line"><span class="cl">kubectl apply -f nrf/
</span></span><span class="line"><span class="cl">kubectl apply -f calico/
</span></span></code></pre></div><p>Expect some pods to be restarting and wait until you see all in running state.</p>
<p>
    <img src="/posts/deploying-my-first-4g-epc-network/20230625_3.png" alt="k3s">
  </p>
<p>Check the WebUI if you are able to access with default credentials <code>admin</code> / <code>1423</code>.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/20230625_4.png" alt="HSS WebUI">
      <figcaption>HSS WebUI</figcaption>
    </figure>
  </p>
<h2 id="install-oaisim5g">Install OAISIM5G</h2>
<p>Next is to install the simulator. I exhausted a lot of time trying to make it work with the latest available version only to realize that the <a href="https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/L2NFAPI_S1.md">article</a> I was referring to is no longer valid after a certain development tag.</p>
<p>Download and extract the latest package suggested in the article:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wget https://gitlab.eurecom.fr/oai/openairinterface5g/-/archive/2021.w51_c/openairinterface5g-2021.w51_c.tar.gz
</span></span><span class="line"><span class="cl">tar -zxvf openairinterface5g-2021.w51_c.tar.gz
</span></span></code></pre></div><h3 id="enodeb-configuration-file">enodeB configuration file</h3>
<p><strong>ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">    ////////// MME parameters:
</span></span><span class="line"><span class="cl">    <span class="nv">mme_ip_address</span>      <span class="o">=</span> <span class="o">(</span> <span class="o">{</span> <span class="nv">ipv4</span>       <span class="o">=</span> <span class="s2">&#34;10.200.0.11&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                              <span class="nv">ipv6</span>       <span class="o">=</span> <span class="s2">&#34;192:168:30::17&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                              <span class="nv">port</span>       <span class="o">=</span> <span class="m">30002</span> <span class="p">;</span>
</span></span><span class="line"><span class="cl">                              <span class="nv">active</span>     <span class="o">=</span> <span class="s2">&#34;yes&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                              <span class="nv">preference</span> <span class="o">=</span> <span class="s2">&#34;ipv4&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                            <span class="o">}</span>
</span></span><span class="line"><span class="cl">                          <span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">...                          
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">    NETWORK_INTERFACES :
</span></span><span class="line"><span class="cl">    <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">ENB_INTERFACE_NAME_FOR_S1_MME</span>            <span class="o">=</span> <span class="s2">&#34;ens4&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">ENB_IPV4_ADDRESS_FOR_S1_MME</span>              <span class="o">=</span> <span class="s2">&#34;172.16.0.30&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">ENB_INTERFACE_NAME_FOR_S1U</span>               <span class="o">=</span> <span class="s2">&#34;ens4&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">ENB_IPV4_ADDRESS_FOR_S1U</span>                 <span class="o">=</span> <span class="s2">&#34;172.16.0.30&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">ENB_PORT_FOR_S1U</span>                         <span class="o">=</span> 2152<span class="p">;</span> <span class="c1"># Spec 2152</span>
</span></span><span class="line"><span class="cl">        <span class="nv">ENB_IPV4_ADDRESS_FOR_X2C</span>                 <span class="o">=</span> <span class="s2">&#34;172.16.0.30&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">ENB_PORT_FOR_X2C</span>                         <span class="o">=</span> 36422<span class="p">;</span> <span class="c1"># Spec 36422</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl"><span class="nv">MACRLCs</span> <span class="o">=</span> <span class="o">(</span>
</span></span><span class="line"><span class="cl">        <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">num_cc</span> <span class="o">=</span> 1<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">local_s_if_name</span>  <span class="o">=</span> <span class="s2">&#34;ens4&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">remote_s_address</span> <span class="o">=</span> <span class="s2">&#34;172.16.0.40&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">local_s_address</span>  <span class="o">=</span> <span class="s2">&#34;172.16.0.30&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">local_s_portc</span>    <span class="o">=</span> 50001<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">remote_s_portc</span>   <span class="o">=</span> 50000<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">local_s_portd</span>    <span class="o">=</span> 50011<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">remote_s_portd</span>   <span class="o">=</span> 50010<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">tr_s_preference</span> <span class="o">=</span> <span class="s2">&#34;nfapi&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">tr_n_preference</span> <span class="o">=</span> <span class="s2">&#34;local_RRC&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span></code></pre></div><p>Optional aliases for running OAISIM eNB and checking the logs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">oaisimenb</span><span class="o">=</span><span class="s1">&#39;cd ~/openairinterface5g-2021.w51_c/cmake_targets/ran_build/build; sudo -E ./lte-softmodem -O ../../../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf &gt; enb.log 2&gt;&amp;1&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">oaisimenblogs</span><span class="o">=</span><span class="s1">&#39;tail -f ~/openairinterface5g-2021.w51_c/cmake_targets/ran_build/build/enb.log&#39;</span>
</span></span></code></pre></div><h3 id="ue-configuration">UE configuration</h3>
<p><strong>ci-scripts/conf_files/ue.nfapi.conf with your preferred editor</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl"><span class="nv">L1s</span> <span class="o">=</span> <span class="o">(</span>
</span></span><span class="line"><span class="cl">        <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">num_cc</span> <span class="o">=</span> 1<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">tr_n_preference</span> <span class="o">=</span> <span class="s2">&#34;nfapi&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">local_n_if_name</span>  <span class="o">=</span> <span class="s2">&#34;ens4&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">remote_n_address</span> <span class="o">=</span> <span class="s2">&#34;172.16.0.30&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">local_n_address</span>  <span class="o">=</span> <span class="s2">&#34;172.16.0.40&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">local_n_portc</span>    <span class="o">=</span> 50000<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">remote_n_portc</span>   <span class="o">=</span> 50001<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">local_n_portd</span>    <span class="o">=</span> 50010<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">remote_n_portd</span>   <span class="o">=</span> 50011<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>Optional aliases for running OAISIM UE and checking the logs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">oaisimue</span><span class="o">=</span><span class="s1">&#39;cd ~/openairinterface5g-2021.w51_c/cmake_targets/ran_build/build; nohup sudo -E ./lte-uesoftmodem -O ../../../ci-scripts/conf_files/ue.nfapi.conf --L2-emul 3 --num-ues 1 --nums_ue_thread 1 &gt; ue.log 2&gt;&amp;1&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">oaisimuelogs</span><span class="o">=</span><span class="s1">&#39;tail -f ~/openairinterface5g-2021.w51_c/cmake_targets/ran_build/build/ue.log&#39;</span>
</span></span></code></pre></div><h3 id="building-the-simulators">Building the simulators</h3>
<p>When building the simulators the first time, it&rsquo;s important to execute with the <code>-I</code> flag so it will also install the package dependencies. If you are building without <code>-I</code> after a fresh Ubuntu install, you will face some errors:
e.g. for eNB:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./build_oai -c -I --eNB
</span></span></code></pre></div><p>e.g. for UE:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./build_oai -c -I --UE
</span></span></code></pre></div><h2 id="define-the-hss-profile-and-run-the-simulator">Define the HSS profile and run the simulator</h2>
<p>The UE information can be taken from <code>openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf</code>. This should be defined in the HSS via the WebUI.</p>
<p>
    <img src="/posts/deploying-my-first-4g-epc-network/20230625_5.png" alt="HSS WebUI">
  </p>
<p>Once that is done, fire up the eNB and the UE. I lose ssh access to the VM whenever I start the UE simulator . I have to resort to VNC access to initiate some traffic like ping or iperf. The VM should have the PGW provided IP assigned to oaitun_ue1.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/logs_smf.png" alt="UE IP">
      <figcaption>SMF logs</figcaption>
    </figure>
  </p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/ue_ip_a.png" alt="UE IP">
      <figcaption>UE IP</figcaption>
    </figure>
  </p>
<p>Below is a sample trace on the Vyos router for an UE ping request to the Google DNS. The first IP in each packet is the eNB IP and the second one of the UE&rsquo;s.</p>
<p>
    <img src="/posts/deploying-my-first-4g-epc-network/pcap_vyos_ping_ue-googledns.png" alt="Ping to Google DNS">
  </p>
<table>
<thead>
<tr>
<th>Packet No.</th>
<th>Event</th>
</tr>
</thead>
<tbody>
<tr>
<td>2563</td>
<td>eNB forwards to the Vyos router</td>
</tr>
<tr>
<td>2564</td>
<td>Vyos forwards to SGW</td>
</tr>
<tr>
<td>2565</td>
<td>GTP stripped off and worker-1 hosting UPF forwards ICMP to Vyos</td>
</tr>
<tr>
<td>2566</td>
<td>Vyos performs NAT and sends to internet</td>
</tr>
<tr>
<td>2567</td>
<td>Ping response received</td>
</tr>
<tr>
<td>2568</td>
<td>Response is forwarded to worker-1</td>
</tr>
<tr>
<td>2569</td>
<td>SGW forwards to Vyos the response encapsulated in GTP</td>
</tr>
<tr>
<td>2570</td>
<td>Vyos forwards to eNB</td>
</tr>
</tbody>
</table>
<p>Below some throughput testing to a public IPerf server. First one is from the VM&rsquo;s physical interface, second one over GTP.</p>
<p>
    <img src="/posts/deploying-my-first-4g-epc-network/iperf3.png" alt="Iperf to public server">
  </p>
<h2 id="my-sentiments">My Sentiments:</h2>
<p>I originally planned of avoiding BGP and save that for a later learning. But it was realized along the way that the eNB needs to be able to reach the pod IP address of the SGW. I initially configured a NAT on the Vyos router for this &ndash;  DNAT-ing UDP traffic for the uplink from the SGW Pod IP (<a href="https://www.etsi.org/deliver/etsi_ts/136400_136499/136413/15.03.00_60/ts_136413v150300p.pdf">3GPP TS 36.413</a>) to an exposed NodePort. While this worked with the eNB accepting the traffic, the response packets are coming out from a different SGW/worker source port. This shouldn&rsquo;t be the case. The response destination port must be copied from the request source port according to <a href="https://www.etsi.org/deliver/etsi_ts/129200_129299/129281/16.00.00_60/ts_129281v160000p.pdf">3GPP TS 29.281</a>. I tried to configure another NAT for the response but the Vyos router just dropped the packets. Enabling the NAT logs didn&rsquo;t help either because no logs were populating.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/s1ap_transportlayeraddress.png" alt="s1ap transport layer address">
      <figcaption>S1AP transport layer address</figcaption>
    </figure>
  </p>
<p>The solution was to resort to configuring BGP &ndash; exposing the Pod IP to the Vyos router so the eNB can reach SGW without any translation.</p>
<p>Talking about if this kind of setup will be helpful for learning EPC, I can&rsquo;t really say yet. I published this post before I forget the logistics and everything, though I&rsquo;m sure there&rsquo;s a lot more to explore and it does feel like I can do a lot more with it. I also do not have much experience on the packet core side of things so it&rsquo;s a bit hard to gauge but doing this project alone made me refer to 3GPP specs out of minimal curiousity. If playing around with these existing docker images will be tough, the option to install open5gs with VMs is always there. Another idea is to build docker images with the latest version of open5gs. In the end it all really depends on your objective. But whether what you plan to do is something simple or something complex, I am sure there will always be learning along the way.</p>
<h2 id="other-errors-faced">Other errors faced</h2>
<p>I was able to capture some of the errors (not all) faced during the deployment. One of the first ones that showed up is the AVX warning error in the first attempt of building the OAISIM5G simulator. I was clueless at first since google wasn&rsquo;t showing anything until I found a PDF stating that OAISIM5G needs a CPU that supports AVX. Realized that this must be listed under the extended instruction sets. So when I checked and found it there, I just had to make sure those flags are propagated all the way to my Ubuntu VMs.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/avx-error.png" alt="AVX warning error">
      <figcaption>AVX warning error</figcaption>
    </figure>
  </p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/avx-flags.png" alt="AVX flags">
      <figcaption>AVX in the CPU extended instruction sets</figcaption>
    </figure>
  </p>
<p>If you try to build the simulator without the <code>-I</code> flag, you will also face the following error. While you can also get away with it by manually installing the listed packages, it&rsquo;s just better to do it by the official documentation to avoid potential problems later on.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/package-dependency.png" alt="OAISIM build error ">
      <figcaption>OAISIM build error </figcaption>
    </figure>
  </p>
<p>At one point I could see tunneled interface getting created on the UE simulator but with no IP getting assigned. When I checked the SGW logs it was showing no response is getting received for create session request. This is where I had to open up port 2123 for the connection to go through.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/sgw-gtpc-connection-error.png" alt="SGW GTP-C connection error">
      <figcaption>SGW GTP-C connection error</figcaption>
    </figure>
  </p>
<p>Below is also a snapshot showing the UDP response for GTP of SGW-U coming out of a different source IP due to the worker node performing a NAT on the source Pod IP. While you may be able to find a work around with it by configuring another NAT, I&rsquo;ve had no luck trying to make it work. I retired from doing so since anyway a Pod IP (at least in this kind of setup) isn&rsquo;t really best paired with a NAT.</p>
<p>
    <figure>
      <img loading="lazy" src="/posts/deploying-my-first-4g-epc-network/sgw-diff-port.png" alt="SGW different source port">
      <figcaption>SGW response with a translated source IP</figcaption>
    </figure>
  </p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Replaced Swag with Traefik as my reverse proxy</title>
      <link>https://luislogs.com/posts/migrated-from-swarm-to-traefik-for-my-reverse-proxy/</link>
      <pubDate>Sun, 18 Jun 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/migrated-from-swarm-to-traefik-for-my-reverse-proxy/</guid>
      <description>I&amp;rsquo;ve been using Swag since I started with self-hosting. It&amp;rsquo;s been working flawlessly and I never had any issue with it. The only thing is if I wanted to expose a new service to the internet, I would have to configure it manually on a separate file. Swag already comes with the sample configuration files for most of popular services, though, it still felt like a chore I had to do before going to bed.</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve been using Swag since I started with self-hosting. It&rsquo;s been working flawlessly and I never had any issue with it. The only thing is if I wanted to expose a new service to the internet, I would have to configure it manually on a separate file. Swag already comes with the sample configuration files for most of popular services, though, it still felt like a chore I had to do before going to bed. I started looking for alternatives and this is when I came to know more about Traefik. The first time I came across it was when I configured a small k3s cluster. It came by default but I just uninsatalled it due to lack of familiarity.</p>
<h3 id="looking-more-into-traefik">Looking more into Traefik</h3>
<p>I took reference from two sources to understand how Traefik works and how it can be used with my docker containers on Unraid. The yaml template I used is also sourced from the Ibracorp link.</p>
<ul>
<li><a href="https://doc.traefik.io/traefik/">Traefik documentation</a></li>
<li><a href="https://docs.ibracorp.io/traefik/master/unraid">Ibracorp tutorial</a></li>
</ul>
<p>In summary, Traefik can query docker via API with the help of container labels. Rules depend on those labels and so as long as you have the correct yaml configuration, you can treat it as set and forget. For those containers that you will be running in the times ahead, you just need to follow the same label structure.</p>
<p>The tutorial in the link includes configuring MFA by setting up a middleware. But I have a basic requirement of just providing external access to my services. So I will be saving this part for later in case I still require it in the future.</p>
<p>I had the following goals in mind:</p>
<ol>
<li>Be able to access containers named &lt;container_name&gt; externally from the internet via &lt;container_name&gt;.su-root.net.</li>
<li>Be able to access &lt;container_name&gt; externally from the internet via &lt;container_name&gt;.su-root.net.</li>
<li>Configure #1 and #2 with the least manual configuration required (but still secure).</li>
</ol>
<p>Given this, I will only go through the necessary configuration in as little footprint as possible.</p>
<h3 id="starting-with-traefik">Starting with traefik</h3>
<p>There are two different configuration files, a static one read at startup, and a dynamic one used during runtime.</p>
<p>Static configuration (traefik.yml) -&gt; Define the entry points, providers, certificate provider.</p>
<p>Dynamic coniguration (fileConfig.yml) -&gt; Define the routing and middleware.</p>
<p>There are multiple providers supported by Traefik but I will only focus on docker that is natively used by Unraid.</p>
<p>I can simply make use of the labels if I want to route traffic to specific containers that have multiple ports or those having a different container name against the cname I want to use.</p>
<h3 id="traefikyml-configuration">traefik.yml configuration</h3>
<p>The first part is to define the entry points where incoming traffic coming is expected.</p>
<ul>
<li>Define http entry point and redirect to https</li>
<li>Define https entry point to use a certificate generated by Let&rsquo;s Encrypt.</li>
<li>Define the middleware so we enforce the addition of STS header.</li>
</ul>
<p>Next is to define the providers:</p>
<ul>
<li>fileConfig.yml file that contains the dynamic routing configuration</li>
<li>Docker by quering using API via dockerproxy.</li>
</ul>
<p>Enable the UI and print info level logs.</p>
<p>And the last but not the least, the settings for Let&rsquo;s Encrypt, the certificate provider.</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">global</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">checkNewVersion</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">sendAnonymousUsage</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></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">serversTransport</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">insecureSkipVerify</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></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">entryPoints</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></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">address</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;:80&#39;</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">redirections</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">entryPoint</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">to</span><span class="p">:</span><span class="w"> </span><span class="l">https</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">scheme</span><span class="p">:</span><span class="w"> </span><span class="l">https</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">https</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">address</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;:443&#39;</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">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">certResolver</span><span class="p">:</span><span class="w"> </span><span class="l">letsencrypt</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">domains</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">main</span><span class="p">:</span><span class="w"> </span><span class="l">mydomain.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">sans</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="s1">&#39;*.mydomain.com&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">middlewares</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="s1">&#39;securityHeaders@file&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">providers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">providersThrottleDuration</span><span class="p">:</span><span class="w"> </span><span class="l">2s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">file</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">filename</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/traefik/fileConfig.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">watch</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></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">docker</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">watch</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="c"># Default host rule to containername.domain.example</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">defaultRule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Host(`{{ lower (trimPrefix `/` .Name )}}.mydomain.com`)&#34;</span><span class="w">    </span><span class="c"># Replace with your domain</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">exposedByDefault</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">endpoint</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;tcp://dockersocket:2375&#34;</span><span class="w"> </span><span class="c"># Uncomment if you are using docker socket proxy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># Enable traefik ui </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">api</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dashboard</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">insecure</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></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">log</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">level</span><span class="p">:</span><span class="w"> </span><span class="l">INFO</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># Use letsencrypt to generate ssl serficiates</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">certificatesResolvers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">letsencrypt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">acme</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">mail@mydomain.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/traefik/acme.json</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">dnsChallenge</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">provider</span><span class="p">:</span><span class="w"> </span><span class="l">cloudflare</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># Used to make sure the dns challenge is propagated to the rights dns servers</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">resolvers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="s2">&#34;1.1.1.1:53&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="s2">&#34;1.0.0.1:53&#34;</span><span class="w">
</span></span></span></code></pre></div><p>Before I go to the other next yaml file, it&rsquo;s important to note that I use IPvlan networking for all my docker containers. This provides a good level of network isolation against the host and I just find it way easier to manage my applications with unique IP addresses. The need to mention is due to the fact that I cannot get Traefik to detect the exposed port on the external IP address unless I explicitly define it with a label. I cannot find any mention of this in the documentation and I haven&rsquo;t done enough experiments to say that this works without a label when using a different network type such a bridge or a custom docker networks, and if it makes sense, even with an untagged ipvlan network.</p>
<p>Sample error I get without the loadbalancer label:</p>
<blockquote>
<p>&ldquo;level=error msg=&ldquo;service &quot;kuma&quot; error: port is missing&rdquo; providerName=docker container=kuma-cf32b&hellip;.&rdquo;</p>
</blockquote>
<h3 id="fileconfigyml-configuration">fileConfig.yml configuration</h3>
<p>Moving over to the dynamic configuration, if you want to route to non-docker services OR if for any reason you require specific routing for a container, you can define them here.</p>
<p>First, you have to specify the entry point for the router. Next is the rule that needs to fulfilled for this router to be activated. And third, the mapped service containing the destination and port.</p>
<p>The middleware you defined in the first yaml file will also be referenced from here. TLS options can also be in this file but it is not a requirement. I haven&rsquo;t gone through each of header but since I can see this enables STS, then I will just leave it as it is.</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">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">routers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">opnsense</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">entryPoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">https</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">rule</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Host(`opnsense.mydomain.com`)&#39;</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 class="l">opnsense</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">proxmox</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">entryPoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">https</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">rule</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Host(`proxmox.mydomain.com`)&#39;</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 class="l">proxmox</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">opnsense</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">loadBalancer</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">servers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">https://10.100.0.1/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">proxmox</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">loadBalancer</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">servers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">https://10.100.0.2:8006/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">middlewares</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">securityHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">headers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">customResponseHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">X-Robots-Tag</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;none,noarchive,nosnippet,notranslate,noimageindex&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">X-Forwarded-Proto</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">customRequestHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">X-Forwarded-Proto</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">sslProxyHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">X-Forwarded-Proto</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">referrerPolicy</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;same-origin&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">hostsProxyHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="s2">&#34;X-Forwarded-Host&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">contentTypeNosniff</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">browserXssFilter</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">forceSTSHeader</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">stsIncludeSubdomains</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">stsSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">63072000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">stsPreload</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></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># Only use secure ciphers - https://ssl-config.mozilla.org/#server=traefik&amp;version=2.6.0&amp;config=intermediate&amp;guideline=5.6              </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">options</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">default</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">minVersion</span><span class="p">:</span><span class="w"> </span><span class="l">VersionTLS12</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">cipherSuites</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305</span><span class="w">
</span></span></span></code></pre></div><h3 id="exposing-containers">Exposing containers</h3>
<p>Traefik will automatically and implicitly create a router and a service for each of the exposed container and the only thing you need is a set of labels:</p>
<ol>
<li>
<p>For traefik to detect the container
<code>traefik.enable=true</code></p>
</li>
<li>
<p>To define the allowed entry point
<code>traefik.http.routers.APP_NAME.entryPoints=https</code></p>
</li>
<li>
<p>So traefik knows to which port it has to route the packets
<code>traefik.http.services.APP_NAME.loadbalancer.server.port=APP_PORT</code></p>
</li>
<li>
<p>To tell traefik to use some another router name. (optional)
<code>traefik.http.routers.APP_NAME.rule=Host(\</code>APP_NAME.mydomain.com`)`</p>
</li>
</ol>
<p>The last one in the list is required only if you want traefik to route a sub-domain that is different from the container name. e.g. you want traefik to route kuma.mydomain.net to a container named uptime-kuma.</p>
<p>Below is an example of a container having three labels:</p>
<p>
    <img src="/posts/migrated-from-swarm-to-traefik-for-my-reverse-proxy/20230618_2.png" alt="Uptime-Kuma">
  </p>
<p>Make sure you have the respective cnames on your DNS pointing to Traefik, and once that is all set, you can start accessing your services over https. I would also suggest to test the redirection at least for the first service you are exposing.</p>
<p>When you have multiple services exposed, you might want to check out the Traefik UI to get a good visibility of your configuration. Do note that you will not be able to do any action from here and it&rsquo;s solely for checking the active routes.</p>
<p>
    <img src="/posts/migrated-from-swarm-to-traefik-for-my-reverse-proxy/20230618_1.png" alt="Uptime-Kuma">
  </p>
<p>Understanding Traefik might be a bit confusing at first but the more you read about the documentation, the more it gets interesting. Perhaps I&rsquo;ll be able to use more of its other functions in future projects!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Tailscale for tunneling torrent traffic to a remote server behind a NAT</title>
      <link>https://luislogs.com/posts/tailscale-for-tunneling-torrent-traffic-to-a-remote-server-behind-a-nat/</link>
      <pubDate>Sat, 20 May 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/tailscale-for-tunneling-torrent-traffic-to-a-remote-server-behind-a-nat/</guid>
      <description>I was finally able to deploy a remote server in my parent’s house with the help of my sister. Guided her to install Debian, connect the LAN, and download a script I created to get tailscale up and running. Once the server was connected to my tailscale network, I was able to ssh from there and do all the remaining configuration. The server is planned for 24/7 uptime and since I only intended this to act as an exit node, I just sourced a cheapo HP T620 Thinclient with 2-cores, 8GB RAM, and 128GB SSD from some online store back home.</description>
      <content:encoded><![CDATA[<p>I was finally able to deploy a remote server in my parent’s house with the help of my sister. Guided her to install Debian, connect the LAN, and download a script I created to get tailscale up and running. Once the server was connected to my tailscale network, I was able to ssh from there and do all the remaining configuration. The server is planned for 24/7 uptime and since I only intended this to act as an exit node, I just sourced a cheapo HP T620 Thinclient with 2-cores, 8GB RAM, and 128GB SSD from some online store back home.</p>
<p>When the remote server was all ready and configured together with VNC, I then created an LXC container, installed Deluge, and configured the tailscale client to use the remote server as an exit node.</p>
<p>
    <img src="/posts/tailscale-for-tunneling-torrent-traffic-to-a-remote-server-behind-a-nat/20230520_2.png" alt="Tailscale for tunneling torrent">
  </p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo tailscale up --exit-node<span class="o">=</span>&lt;tailscale_ip&gt; --exit-node-allow-lan-access<span class="o">=</span><span class="nb">true</span>
</span></span></code></pre></div><p>
    <img src="/posts/tailscale-for-tunneling-torrent-traffic-to-a-remote-server-behind-a-nat/20230520_3.png" alt="Tailscale for tunneling torrent">
  </p>
<p>Do note that setting “–exit-node-allow-lan-access” to true is required or else you wouldn’t be able to access the Deluge WebUI. This parameter will only recognize the local configured subnet so if your container is sitting inside a different VLAN like mine, then you will also need to configure an outbound NAT rule on your firewall or router. You should configure it in a way that any private local IP (outside the container subnet) that tries to reach your container, should have the Source IP translated to the Vlan interface IP of your router.</p>
<p>
    <img src="/posts/tailscale-for-tunneling-torrent-traffic-to-a-remote-server-behind-a-nat/20230520_4.png" alt="Tailscale for tunneling torrent">
  </p>
<p>I am sure there are other better ways to implement this but if you simply need a torrent client with tunneled traffic, then I believe this is the best and simplest it can be done.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Install K3s with Calico and disable Traefik</title>
      <link>https://luislogs.com/posts/install-k3s-with-calico-and-disable-traefik/</link>
      <pubDate>Sat, 13 May 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/install-k3s-with-calico-and-disable-traefik/</guid>
      <description>For those who want to use Calico with k3s instead of Flannel, I am sharing here the steps I followed. For the project I am working on, I had to re-install k3s with Calico upon learning that flannel works on layer-2, whereas I need layer-3 routing for BGP. I am not sure if there’s a way to advertise BGP with flannel as the CNI but it looks like Calico already runs it natively.</description>
      <content:encoded><![CDATA[<p>For those who want to use Calico with k3s instead of Flannel, I am sharing here the steps I followed. For the project I am working on, I had to re-install k3s with Calico upon learning that flannel works on layer-2, whereas I need layer-3 routing for BGP. I am not sure if there’s a way to advertise BGP with flannel as the CNI but it looks like Calico already runs it natively. That should reduce any additional configuration that might be required going further if your project also requires BGP routing.</p>
<p>Uninstall k3s
To uninstall execute the following as root:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/usr/local/bin/k3s-uninstall.sh
</span></span><span class="line"><span class="cl">rm -rf /var/lib/rancher
</span></span></code></pre></div><h3 id="reinstall-k3s">Reinstall k3s</h3>
<p>Execute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sfL https://get.k3s.io <span class="p">|</span> <span class="nv">INSTALL_K3S_EXEC</span><span class="o">=</span><span class="s2">&#34;--flannel-backend=none --disable-network-policy --cluster-cidr=192.168.0.0/16&#34;</span> sh -s - --docker
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_2.png" alt="Install k3s with calico">
  </p>
<p>Copy the k3s yaml file to your home directory to allow remote access. Ensure correct ownership:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir /home/luis/.kube/ <span class="c1">#(Only required if you don&#39;t have the .kube directory in your home folder yet)</span>
</span></span><span class="line"><span class="cl">cp  /etc/rancher/k3s/k3s.yaml /home/luis/.kube/config
</span></span><span class="line"><span class="cl">chown -R luis:luis /home/luis/.kube/
</span></span></code></pre></div><p>Check nodes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get node
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_3.png" alt="Install k3s with calico">
  </p>
<p>Get the token of your master node:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat /var/lib/rancher/k3s/server/node-token
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_4.png" alt="Install k3s with calico">
  </p>
<p>Use this token and the IP of your master node in the installation command to be executed in your worker nodes. Execute as root:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sfL https://get.k3s.io <span class="p">|</span> <span class="nv">K3S_URL</span><span class="o">=</span>https://serverip:6443 <span class="nv">K3S_TOKEN</span><span class="o">=</span>mytoken sh -s - --docker
</span></span></code></pre></div><p>Execute this in the worker node as root.</p>
<p>You will get the error:</p>
<blockquote>
<p>The connection to the server localhost:8080 was refused – did you specify the right host or port?</p>
</blockquote>
<p>This is because flannel was disabled and there is no CNI running.</p>
<p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_5.png" alt="Install k3s with calico">
  </p>
<p>And if you try to check pods from the master node the status will only be in ContainerCreating:</p>
<p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_6.png" alt="Install k3s with calico">
  </p>
<h3 id="install-calico">Install Calico</h3>
<p>From here you will need to install calico. To do so execute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/tigera-operator.yaml
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_7.png" alt="Install k3s with calico">
  </p>
<p>Then install the required custom resources:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/custom-resources.yaml
</span></span></code></pre></div><p>Now check the pods</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pod -o wide --all-namespaces
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_8.png" alt="Install k3s with calico">
  </p>
<p>You will notice that some traefik containers are in error or CrashLoopBackOff state. I am not sure why but I don’t really need it. To disable Traefik:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl -n kube-system delete helmcharts.helm.cattle.io traefik traefik-crd
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_9.png" alt="Install k3s with calico">
  </p>
<p>Stop k3s services.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl stop k3s
</span></span><span class="line"><span class="cl">sudo systemctl status k3s
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_10.png" alt="Install k3s with calico">
  </p>
<p>Modify below the k3s service configuration file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vi /etc/systemd/system/k3s.service
</span></span></code></pre></div><p>And add the following line:</p>
<p><code>'--disable=traefik' \</code></p>
<p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_11.png" alt="Install k3s with calico">
  </p>
<p>Reload the service and delete the Traefik yaml file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl daemon-reload
</span></span><span class="line"><span class="cl">sudo rm /var/lib/rancher/k3s/server/manifests/traefik.yaml
</span></span></code></pre></div><p>Start k3s:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl start k3s
</span></span><span class="line"><span class="cl">sudo systemctl status k3s
</span></span></code></pre></div><p>Check nodes</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get node -o wide
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_12.png" alt="Install k3s with calico">
  </p>
<p>Check the pods regularly.</p>
<p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_13.png" alt="Install k3s with calico">
  </p>
<p>You might notice that some calico pods are in crashloopback state. Check again after a few minutes and they should be in running state</p>
<p>
    <img src="/posts/install-k3s-with-calico-and-disable-traefik/20230513_14.png" alt="Install k3s with calico">
  </p>
<p>K3s with Calico should be running fine now at this point!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Run Pihole in an LXC container in Proxmox</title>
      <link>https://luislogs.com/posts/run-pihole-in-an-lxc-container-in-proxmox/</link>
      <pubDate>Mon, 08 May 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/run-pihole-in-an-lxc-container-in-proxmox/</guid>
      <description>Pihole as our local DNS at home has been working perfectly fine. It blocks almost every ad there is whenever I am browsing. I was initially running it as a docker container within my Unraid server. The problem is every time I would restart Unraid for some experimental work, the internet of the entire house also goes down momentarily. I already tried defining a secondary DNS on my DHCP server but noticed some of my devices would randomly pick a DNS if there are multiple defined.</description>
      <content:encoded><![CDATA[<p>Pihole as our local DNS at home has been working perfectly fine. It blocks almost every ad there is whenever I am browsing. I was initially running it as a docker container within my Unraid server. The problem is every time I would restart Unraid for some experimental work, the internet of the entire house also goes down momentarily. I already tried defining a secondary DNS on my DHCP server but noticed some of my devices would randomly pick a DNS if there are multiple defined. Now that I have a Proxmox running OPNsense, I just thought of running Pihole there instead, since anyway I rarely restart that box.</p>
<p>Proxmox already supports LXC containers by default and in this case, running Pihole on LXC provides some advantage in terms of flexibility and ease of configuration. This is especially for those who have less experience working with docker but only with Linux in general. I will share one good use case for this later on but for now let’s start setting up Pihole on Proxmox.</p>
<p>The hardware requirements according to the pihole documentation:
Minimum of 2GB disk (4GB recommended)
512MB RAM</p>
<p>First you need to download an LXC template. I like using debian as the base image since it’s very lightweight and it’s where Ubuntu is based from. If you don’t have it yet, you can download it by going to Datacenter &gt; pve &gt; storage &gt; CT templates &gt; Templates and search for Debian 11 Bullseye.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_2.png" alt="alt text">
  </p>
<p>Click on Create CT and input a container ID, hostname, and the root password. Check Unprivileged container and nesting (optional). Note that nesting is not really required but in my case the proxmox terminal will keep on printing permission errors if I leave this unchecked.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_3.png" alt="alt text">
  </p>
<p>Select debian as the template.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_4.png" alt="alt text">
  </p>
<p>For disk, I assigned 6GB. For RAM, double the recommended, since I have enough.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_5.png" alt="alt text">
  </p>
<p>Assign a static IP to the container. Gateway should also be defined.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_6.png" alt="alt text">
  </p>
<p>For DNS I will assign my unbound IP which is the same as my OPNsense gateway IP. (Unbound is a DNS caching tool built-into OPNsense). This will basically be my upstream DNS for my Pihole. If you don’t have Unbound running then you can input any public DNS like Google (8.8.8.8, 8.8.4.4) or Cloudflare (1.1.1.1).</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_7.png" alt="alt text">
  </p>
<p>Confirm</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_8.png" alt="alt text">
  </p>
<p>Enable start on boot flag.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_9.png" alt="alt text">
  </p>
<p>Login as root</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_10.png" alt="alt text">
  </p>
<p>Update and upgrade</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt-get update -y <span class="o">&amp;&amp;</span> apt-get upgrade -y
</span></span></code></pre></div><p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_11.png" alt="alt text">
  </p>
<p>Install curl:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt-get install curl -y
</span></span></code></pre></div><p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_12.png" alt="alt text">
  </p>
<p>Install Pihole:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sSL https://install.pi-hole.net <span class="p">|</span> bash
</span></span></code></pre></div><p>Install custom upstream and point to unbound IP</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_13.png" alt="alt text">
  </p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_14.png" alt="alt text">
  </p>
<p>Install the default blacklist.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_15.png" alt="alt text">
  </p>
<p>Install admin interface and lighthttpd.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_16.png" alt="alt text">
  </p>
<p>Query logging or any other option is fine.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_17.png" alt="alt text">
  </p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_18.png" alt="alt text">
  </p>
<p>Reset the pihole password.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pihole -a -p
</span></span></code></pre></div><p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_19.png" alt="alt text">
  </p>
<p>Now go to your container IP and append /admin (e.g. http://10.0.0.88/admin)</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_20.png" alt="alt text">
  </p>
<p>If you are running it on proxmox like me you’d probably get the same error as in the snap below. You can ignore this especially if you have multi-core host. If you want to be sure you can check your CPU utilization with the top command.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_21.png" alt="alt text">
  </p>
<p>After changing your DNS to pihole, check with nslookup if your device is able to send and receive to and from the Pihole IP.</p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_22.png" alt="alt text">
  </p>
<p>
    <img src="/posts/run-pihole-in-an-lxc-container-in-proxmox/20230508_23.png" alt="alt text">
  </p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Monitor Proxmox with Telegraf, InfluxDB, and Grafana</title>
      <link>https://luislogs.com/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/</link>
      <pubDate>Tue, 02 May 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/</guid>
      <description>Grafana has always been on the top of my learning list ever since it was introduced to me in 2018. I was just a user back then and never really had the chance to configure a monitoring stack along with it.
Proxmox already has its own dashboard showing sufficient information, but since I would need to be monitoring multiple systems at once, then I feel like this is the perfect time to start learning Grafana.</description>
      <content:encoded><![CDATA[<p>Grafana has always been on the top of my learning list ever since it was introduced to me in 2018. I was just a user back then and never really had the chance to configure a monitoring stack along with it.</p>
<p>Proxmox already has its own dashboard showing sufficient information, but since I would need to be monitoring multiple systems at once, then I feel like this is the perfect time to start learning Grafana.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_2.png" alt="alt TIG">
  </p>
<p>I will not go into detail since I myself still need to go through more on the documentation, but here I will walk you through how to get InfluxDB (for storing metrics), and Grafana up and running with Proxmox in the simplest way possible. Taking another mile we are also going to install Telegraf on the Proxmox host to send additional data (e.g. CPU core temperature) which is not monitored and sent by Proxmox by default.</p>
<p>I will be running both InfluxDB and Grafana as two separate containers on my main server. If you are not using Unraid as your docker manager but something else like Portainer, the same steps should still be applicable on high level. As for Telegraf it will be running inside an LXC container on my PVE host.</p>
<h3 id="install-influxdb">Install InfluxDB</h3>
<p>You can install the latest version of InfluxDB2. On Unraid you can get the one from atribe’s repository.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_3.png" alt="alt TIG">
  </p>
<p>You will need to assign a static IP so your endpoints will send to a single destination. You can select the bridge and assign a unique IP in the same subnet as your host, or as a better option, run your docker containers on a separate vlan. Delete the second host port and leave 8086 exposed. Everything else in default.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_4.png" alt="alt TIG">
  </p>
<p>Open the InfluxDB WebUI. Create a username, assign an organization name and a bucket name.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_5.png" alt="alt TIG">
  </p>
<p>On the next screen take note of your token. You can use this token later on or you can generate a new one.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_6.png" alt="alt TIG">
  </p>
<p>To generate a new token, go to Data &gt; API &gt; Generate API token. Add a description and save.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_7.png" alt="alt TIG">
  </p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_8.png" alt="alt TIG">
  </p>
<p>Go to Proxmox &gt; Datacenter &gt; Metrics &gt; Add &gt; InfluxDB. Input the same information during user creation and the token created for proxmox. Input the InfluxDB IP and port 8086 in the server field.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_9.png" alt="alt TIG">
  </p>
<h3 id="install-grafana">Install Grafana</h3>
<p>Install the official Grafana docker.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_10.jpg" alt="alt TIG">
  </p>
<p>Assign a unique IP and an admin password. The server URL should also have the IP address. Leave default port of 3000.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_11.png" alt="alt TIG">
  </p>
<p>On Unraid if you go to the dockers tab, you will see that Grafana is not running and. If you check logs it will show some permission error.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_12.jpg" alt="alt TIG">
  </p>
<p>Open the terminal and go to /mnt/user/appdata. Check the permissions of the grafana directory. Enable the write permission on group and others level.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_13.jpg" alt="alt TIG">
  </p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ls -lrt /mnt/user/appdata | grep grafana
</span></span><span class="line"><span class="cl">chmod -R 777 /mnt/user/appdata/grafana
</span></span></code></pre></div><p>Restart Grafana and enter the WebUI. You can go to http://&lt;grafana_ip&gt;:3000.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_14.jpg" alt="alt TIG">
  </p>
<p>Go to Administration &gt; Data sources &gt; Add data source. Search InfluxDB.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_15.jpg" alt="alt TIG">
  </p>
<p>Set query language to Flux. Basic auth should be disabled. Enter InfluxDB URL and port.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_16.jpg" alt="alt TIG">
  </p>
<p>Next thing to do is to import a dashboard created by one community member (mephisto). This is one of the good things about Grafana. Since it’s widely used by many, we can also get a plenty ton of different pre-created dashboards shared by the community members in the Grafana dashboard repository. For more information on the dashboard we will be importing, you can find it here. Don’t forget to throw in some stars!</p>
<p>Go to Dashboards &gt; New &gt; Import &gt; Import via grafana.com &gt; 15356 (Note that 15356 is the dashboard ID at this time of writing but it can change. It’s still best to go to the link and copy the ID from there.) Select the proxmox datasource. Select the proxmox bucket. You can then save this dashboard.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_17.jpg" alt="alt TIG">
  </p>
<p>Testing real time metrics. You can do a speed test and see if it will show increase in I/O in Grafana.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_18.jpg" alt="alt TIG">
  </p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_19.jpg" alt="alt TIG">
  </p>
<p>Now we have InfluxDB and Grafana working with Proxmox. Do note that this doesn’t include the CPU temperature. It’s not included in the Proxmox dashboard either. To get this to show in Grafana we will need to install a package called lm-sensors and install Telegraf – a backend service that will push temperature metrics to InfluxDB.</p>
<h3 id="install-telegraf-and-lm-sensors">Install Telegraf and lm-sensors</h3>
<p>Telegraf can send the default linux metrics but this doesn’t include the CPU core temps. We need to install the lm-sensors package for this. We will run it on an LXC container as a safer practice.</p>
<p>Crate a new container. 256MB of RAM should be sufficient. Don’t mind the CPU core count set to 1 since the lm-sensors tool will still be able to monitor the physical cores attached to the host.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_20.png" alt="alt TIG">
  </p>
<p>Enable start at boot.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_21.png" alt="alt TIG">
  </p>
<p>Do necessary checks post creation. Always check the disk and memory after container creation and before doing any change. Knowing the initial state of a system will always save you time and effort in the long run once issues arise.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_22.png" alt="alt TIG">
  </p>
<p>Update and upgrade packages. Install lm-sensors.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apt-get update -y &amp;&amp; apt-get upgrade -y
</span></span><span class="line"><span class="cl">apt-get install -y lm-sensors
</span></span><span class="line"><span class="cl">sensors
</span></span></code></pre></div><p>The last command will output the current temperature for each physical core.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_23.png" alt="alt TIG">
  </p>
<p>Checking the Influx documentation we can install telegraf with the following commands. As a pre-requisite we need to install the commands wget, gpg, and sudo. Installing sudo is optional since we are running as root but we’ll add it anyway.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apt-get install -y wget gpg sudo 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># influxdata-archive_compat.key GPG fingerprint:
</span></span><span class="line"><span class="cl">#     9D53 9D90 D332 8DC7 D6C8 D3B9 D8FF 8E1F 7DF8 B07E
</span></span><span class="line"><span class="cl">wget -q https://repos.influxdata.com/influxdata-archive_compat.key
</span></span><span class="line"><span class="cl">echo &#39;393e8779c89ac8d958f81f942f9ad7fb82a25e133faddaf92e15b16e6ac9ce4c influxdata-archive_compat.key&#39; | sha256sum -c &amp;&amp; cat influxdata-archive_compat.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg &gt; /dev/null
</span></span><span class="line"><span class="cl">echo &#39;deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg] https://repos.influxdata.com/debian stable main&#39; | sudo tee /etc/apt/sources.list.d/influxdata.list
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo apt-get update &amp;&amp; sudo apt-get install telegraf
</span></span></code></pre></div><p>Have a quick check on the disk utilization to ensure you have sufficient space.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_24.png" alt="alt TIG">
  </p>
<p>At this point we still have above 55% remaining. This should be enough!</p>
<p>Checking metrics to be sent by default:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">grep &#34;\[\[inputs&#34; telegraf.conf | grep -v \#
</span></span></code></pre></div><p>This will show the uncommented plugins. If we keep the config like this then Telegraf will also send these metrics to InfluxDB.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_25.png" alt="alt TIG">
  </p>
<p>For now we just want the core temperatures. We have to comment everything and uncomment the [[inputs.sensors]] plugin. First, backup original configuration file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">mkdir ~/backup
</span></span><span class="line"><span class="cl">cp /etc/telegraf/telegraf.conf ~/backup/telegraf.conf
</span></span></code></pre></div><p>Now you have the option to use the telegraf.conf I uploaded here or you can do it manually.</p>
<p>Once that is done Initiate a dry-run to verify the metrics that will be sent with the updated config file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">telegraf -config telegraf.conf -test
</span></span></code></pre></div><p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_26.png" alt="alt TIG">
  </p>
<p>You can also copy paste the content of this telegraf.conf file which has the above steps implemented.</p>
<p>Next is to input the InfluxDB details. Uncomment the following lines</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[[outputs.influxdb_v2]]
</span></span><span class="line"><span class="cl">urls
</span></span><span class="line"><span class="cl">tokens
</span></span><span class="line"><span class="cl">organization
</span></span><span class="line"><span class="cl">bucket
</span></span><span class="line"><span class="cl">timeout
</span></span></code></pre></div><p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_27.png" alt="alt TIG">
  </p>
<p>Start and enable telegraf. Verify the status after a few seconds.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">systemctl start telegraf
</span></span><span class="line"><span class="cl">systemctl enable telegraf
</span></span><span class="line"><span class="cl">systemctl status telegraf
</span></span></code></pre></div><p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_28.png" alt="alt TIG">
  </p>
<p>Go back to InfluxDB &gt; Data Explorer. From here you can create a query with the help of the script editor and by selecting the metrics you want to be sent.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_29.png" alt="alt TIG">
  </p>
<p>If you don’t want to do this you can just copy the Flux query shared below. You might want to add more to the four CPU core counts already included. Do note that I am using an Intel processor and it might not work if you are using AMD. If for any reason it doesn’t work for you, then I would suggest creating the query manually in InfluxDB.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">from(bucket: &#34;proxmox&#34;)
</span></span><span class="line"><span class="cl">  |&gt; range(start: v.timeRangeStart, stop: v.timeRangeStop)
</span></span><span class="line"><span class="cl">  |&gt; filter(fn: (r) =&gt; r[&#34;_measurement&#34;] == &#34;sensors&#34;)
</span></span><span class="line"><span class="cl">  |&gt; filter(fn: (r) =&gt; r[&#34;_field&#34;] == &#34;temp_input&#34;)
</span></span><span class="line"><span class="cl">  |&gt; filter(fn: (r) =&gt; r[&#34;feature&#34;] == &#34;core_0&#34; or r[&#34;feature&#34;] == &#34;core_1&#34; or r[&#34;feature&#34;] == &#34;core_2&#34; or r[&#34;feature&#34;] == &#34;core_3&#34;)
</span></span><span class="line"><span class="cl">  |&gt; filter(fn: (r) =&gt; r[&#34;feature&#34;] == &#34;core_0&#34; or r[&#34;feature&#34;] == &#34;core_1&#34; or r[&#34;feature&#34;] == &#34;core_2&#34; or r[&#34;feature&#34;] == &#34;core_3&#34;)
</span></span><span class="line"><span class="cl">  |&gt; aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
</span></span><span class="line"><span class="cl">  |&gt; yield(name: &#34;mean&#34;)
</span></span></code></pre></div><p>Go back to the Grafana dashboard and add a new visualization. Paste the Flux query at the bottom panel. Assign the panel title and the units in celsius.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_30.png" alt="alt TIG">
  </p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_31.png" alt="alt TIG">
  </p>
<p>If the legend labels are too long, you can make use of the override options to create change the display name. Add field override &gt; Fields with name. Add override property &gt; Standard options &gt; Display name.</p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_32.png" alt="alt TIG">
  </p>
<p>
    <img src="/posts/monitor-proxmox-with-telegraf-influxdb-and-grafana/20230502_33.png" alt="alt TIG">
  </p>
<p>You now have complete TIG stack running for Proxmox! The same steps can be followed for other platforms and if in case they don’t have any built-in function to send metrics to InfluxDB, you can just install Telegraf instead.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Virtualized OPNsense on Proxmox as my homelab router</title>
      <link>https://luislogs.com/posts/virtualized-opnsense-on-proxmox-as-my-homelab-router/</link>
      <pubDate>Tue, 25 Apr 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/virtualized-opnsense-on-proxmox-as-my-homelab-router/</guid>
      <description>So this little thing arrived last week. Ever since I got my Unraid box up and running 24/7, I just felt I needed more control over the entire network. This is primarily because my existing home router would randomly drop packets and anyone who has planned to work from our home won’t be able to do so.
As for the specs, it’s a fanless appliance running a quad-core Intel 12th-gen J6412 (2 GHz base, 2.</description>
      <content:encoded><![CDATA[<p>So this little thing arrived last week. Ever since I got my Unraid box up and running 24/7, I just felt I needed more control over the entire network. This is primarily because my existing home router would randomly drop packets and anyone who has planned to work from our home won’t be able to do so.</p>
<p>
    <img src="/posts/virtualized-opnsense-on-proxmox-as-my-homelab-router/20230425_1.webp" alt="alt text">
  </p>
<p>
    <img src="/posts/virtualized-opnsense-on-proxmox-as-my-homelab-router/20230425_2.webp" alt="alt text">
  </p>
<p>As for the specs, it’s a fanless appliance running a quad-core Intel 12th-gen J6412 (2 GHz base, 2.6 GHz boost) with 5x Intel i226 2.5Gbe NICs. I chose the bare minimum option so I can avoid the crappy RAM and storage that might come with it. For the RAM I was able to source second-hand 2x8GB Samsung DDR4 SODIMMs and for the storage I didn’t any more bother and just ordered a 240GB SSD from Amazon since these come cheap these days.</p>
<p>
    <img src="/posts/virtualized-opnsense-on-proxmox-as-my-homelab-router/20230425_3.webp" alt="alt text">
  </p>
<p>Installed Proxmox and spawned a VM running a router software based on freeBSD called OPNsense. I can’t believe I’ve been missing on this piece of software for the longest time. It feels like I’ve rekindled my love for networking ever since I switched to a different role in my career graduating from configuring routers and switches.</p>
<p>
    <img src="/posts/virtualized-opnsense-on-proxmox-as-my-homelab-router/20230425_4.webp" alt="alt text">
  </p>
<p>
    <img src="/posts/virtualized-opnsense-on-proxmox-as-my-homelab-router/20230425_5.webp" alt="alt text">
  </p>
<p>The entire setup process was very straightforward. I just got confused a tad bit in the beginning when I was trying to figure out which interface to assign for management access. On my first attempt, I assigned the first interface in the list, expecting that it would be mapped to the first physical port. But when I tried to plug an ethernet cable from my laptop, for some reason I couldn’t ping the management IP I set. Long story short I re-installed Proxmox again only to know that the first installation was already correct. Maybe some lose cable?</p>
<p>
    <img src="/posts/virtualized-opnsense-on-proxmox-as-my-homelab-router/20230425_6.webp" alt="alt text">
  </p>
<p>Anyway the router has been running stable for the past week. This is with virtio running as a driver for the Intel i226 NICs. On average I’m getting about 500Mbps for internet (I used to get about 700MBps with a TP-Link A10 router and yes I can deal with that 200Mbps difference for now!). I’ll explore physically passing through the NICs later on once I get the time. But as far as I can tell, I’m just loving it. On top of that Proxmox has also been remarkably gaining my interest now that I have a pi-hole running in an LXC container.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Install k3s on Ubuntu Server 22.04</title>
      <link>https://luislogs.com/posts/install-k3s-on-ubuntu-server-22-04/</link>
      <pubDate>Sat, 15 Apr 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/install-k3s-on-ubuntu-server-22-04/</guid>
      <description>While working on creating my own kubernetes cluster I initially wanted to go with the official documentation for the installation process. But then I thought why not explore other distributions that can even simplify and reduce the time of the whole deployment process. This is when I came across k3s which claims to be an easier way of installing and managing kubernetes.
K3s makes use of containerd as the default container platform but I opted to go with docker since I want to become more familiar with it.</description>
      <content:encoded><![CDATA[<p>While working on creating my own kubernetes cluster I initially wanted to go with the official documentation for the installation process. But then I thought why not explore other distributions that can even simplify and reduce the time of the whole deployment process. This is when I came across k3s which claims to be an easier way of installing and managing kubernetes.</p>
<p>K3s makes use of containerd as the default container platform but I opted to go with docker since I want to become more familiar with it. Note that at this time docker is still considered experimental with k3s as written in the official documentation but I still went on with it anyway. In the following steps I also included how you can execute the kubectl commands without the need of sudo and how they can be executed from a remote machine.</p>
<h3 id="installation-on-master-node">Installation on master node</h3>
<p>I have the following setup at the moment where my cluster has a node subnet of 20.0.0.0/24.</p>
<p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_1.png" alt="alt text">
  </p>
<p>Before anything else, modify the hosts on file on each node to reflect the IP and hostnames of the your entire cluster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">127.0.0.1 localhost
</span></span><span class="line"><span class="cl">20.0.0.10 k8s-master m
</span></span><span class="line"><span class="cl">20.0.0.11 k8s-worker-1 w1
</span></span><span class="line"><span class="cl">20.0.0.12 k8s-worker-2 w2
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_2.png" alt="alt text">
  </p>
<p>Update and upgrade ubuntu packages:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt upgrade
</span></span><span class="line"><span class="cl">sudo reboot
</span></span></code></pre></div><p>Install required tools and docker on both master and worker nodes. First add the docker repository.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install apt-transport-https ca-certificates curl software-properties-common -y
</span></span><span class="line"><span class="cl">curl -fsSL https://download.docker.com/linux/ubuntu/gpg <span class="p">|</span> sudo apt-key add -
</span></span><span class="line"><span class="cl">sudo add-apt-repository <span class="s2">&#34;deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable&#34;</span>
</span></span></code></pre></div><p>Install docker:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update <span class="o">&amp;&amp;</span> sudo apt install -y docker-ce
</span></span></code></pre></div><p>Run docker and enable persistence in case of server reboot:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl start docker
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> docker
</span></span></code></pre></div><p>Check if docker is running:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl status docker
</span></span></code></pre></div><p>Install k3s on the master node:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sfL https://get.k3s.io <span class="p">|</span> sh -s - --docker
</span></span></code></pre></div><p>Check status of k3s:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl status k3s
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_3.png" alt="alt text">
  </p>
<p>Check if node is running:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo kubectl get node -o wide
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_4.png" alt="alt text">
  </p>
<p>As an optional step, unblock ports used by kubernetes. By default firewall will be inactive but in case you have enabled it previously, then you need to perform the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ufw allow 6443/tcp
</span></span><span class="line"><span class="cl">sudo ufw allow 443/tcp
</span></span></code></pre></div><p>You can also check the status of your firewall with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ufw status
</span></span></code></pre></div><p>Disable with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ufw disable
</span></span></code></pre></div><p>Extract the token to be used for joining worker nodes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo cat /var/lib/rancher/k3s/server/node-token
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_5.png" alt="alt text">
  </p>
<h3 id="installation-on-worker-node">Installation on worker node</h3>
<p>Next is to install k3s on the worker nodes. Here you will need the master node IP address as well as the token from the previous step.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sfL http://get.k3s.io <span class="p">|</span> <span class="nv">K3S_URL</span><span class="o">=</span>https://&lt;master_IP&gt;:6443 <span class="nv">K3S_TOKEN</span><span class="o">=</span>&lt;join_token&gt; sh -s - --docker
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_6.png" alt="alt text">
  </p>
<p>Verify all nodes are joined to the cluster</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo kubectl get nodes -o wide
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_7.png" alt="alt text">
  </p>
<p>Now you can try to deploy a test nginx pod</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo kubectl run --image nginx nginx-test
</span></span><span class="line"><span class="cl">sudo kubectl get pod nginx-test -o wide
</span></span></code></pre></div><p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_8.png" alt="alt text">
  </p>
<h3 id="permit-non-root-user-to-execute-kubectl-commands">Permit non-root user to execute kubectl commands</h3>
<p>Now if you try to execute kubectl commands without sudo, you would probably face the error below.</p>
<p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_9.png" alt="alt text">
  </p>
<p>This is because kubernetes will be installed with root credentials and any other user to execute kubectl will have to be permitted. The easy way is to copy the k3s.yaml file in the /etc/rancher/k3s directory to your user’s kube config file. First create the directory (with your non-root user) if you don’t have it yet. Note of the user and group variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir ~/.kube
</span></span><span class="line"><span class="cl">cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
</span></span><span class="line"><span class="cl">chown &lt;user&gt;:&lt;group&gt; ~/.kube/config
</span></span></code></pre></div><p>Then you have to export the kube config every time so to do this automatically upon login modify your .bashrc and add the line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">KUBECONFIG</span><span class="o">=</span>~/.kube/config
</span></span></code></pre></div><p>For you to be able to execute kubectl commands with just typing ‘k’ you can also add the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">source</span> &lt;<span class="o">(</span>kubectl completion bash<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">k</span><span class="o">=</span>kubectl
</span></span><span class="line"><span class="cl"><span class="nb">complete</span> -o default -F __start_kubectl k
</span></span></code></pre></div><p>Lastly if you want to be able to execute kubectl commands from a remote machine outside your cluster just ensure you have the same kube config file in the previous step and change the server IP from localhost to the actual one.</p>
<p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_10.png" alt="alt text">
  </p>
<p>
    <img src="/posts/install-k3s-on-ubuntu-server-22-04/20230415_11.png" alt="alt text">
  </p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Install Ubuntu Server 22.04 &amp;　GNS3 on Unraid</title>
      <link>https://luislogs.com/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/</link>
      <pubDate>Sat, 01 Apr 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/</guid>
      <description>Unraid is a hypervisor by itself. So aside from just running docker containers, you can also spawn VMs. I wanted to install Ubuntu Server 22.04 so I could run the GNS3 remote server on it.
Ubuntu Server Installation You can download the Ubuntu iso image from this page. It’s either you download to your local machine or using Unraid with the help of the CLI or some docker app.
While waiting for the download to complete.</description>
      <content:encoded><![CDATA[<p>Unraid is a hypervisor by itself. So aside from just running docker containers, you can also spawn VMs. I wanted to install Ubuntu Server 22.04 so I could run the GNS3 remote server on it.</p>
<h3 id="ubuntu-server-installation">Ubuntu Server Installation</h3>
<p>You can download the Ubuntu iso image from this page. It’s either you download to your local machine or using Unraid with the help of the CLI or some docker app.</p>
<p>While waiting for the download to complete. Go to Settings &gt; VM Manager and set ‘Enable VMs’ to yes. You can also toggle the advanced view to have a more detailed look on the settings. Here you will see the directory of the iso files and the storage so be sure to have your shares properly configured.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_1.png" alt="alt text">
  </p>
<p>Just to give you an idea my iso directory is saved to a cache that doesn’t have redundancy. Image files that are not intended to be used any sooner can either be deleted or transferred to an archive directory on your main array.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_2.png" alt="alt text">
  </p>
<p>It is important to transfer the iso file to the iso share or else you won’t be able to select this later on.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_3.png" alt="alt text">
  </p>
<p>Go to VMs &gt; Add VM &gt; Ubuntu. Select the CPU cores and memory as well as the ISO file.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_4.png" alt="alt text">
  </p>
<p>Assign the disk size.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_5.png" alt="alt text">
  </p>
<p>For network model ensure that br0 is selected. For VM console port Unraid starts with the default port of VNC but just to avoid any conflict later on when you run appliances on GNS3 just choose a different port range from 5900. Uncheck ‘Start VM after creation’. Everything else can be kept in default. Create the VM.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_6.png" alt="alt text">
  </p>
<p><em>Note network-model should be set to virtio-net to avoid any networking issues with the docker containers.</em></p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_7.png" alt="alt text">
  </p>
<p>Go back and edit this VM and toggle from form to XML view on the upper right hand side. To enable nested virtualization insert the following line in the <cpu> part.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_8.png" alt="alt text">
  </p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;feature</span> <span class="na">policy=</span><span class="s">&#39;require&#39;</span> <span class="na">name=</span><span class="s">&#39;vmx&#39;</span><span class="nt">/&gt;</span>
</span></span></code></pre></div><p>Update, start the VM, and open the console. Install the OS how you normally do it.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_9.png" alt="alt text">
  </p>
<p>As for me I always choose Ubuntu Server (not minimized) everytime to avoid any package dependencies later on.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_10.png" alt="alt text">
  </p>
<p>Be sure the VM is assigned a DHCP IP when you get to the network settings or assign a static one if you wish. I always prefer the latter.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_11.png" alt="alt text">
  </p>
<p>Modify disk to utilize all. There really is no use case to use LVM if you are just running GNS3 server.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_12.png" alt="alt text">
  </p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_13.png" alt="alt text">
  </p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_14.png" alt="alt text">
  </p>
<p>Also ensure OpenSSH to be installed!</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_15.png" alt="alt text">
  </p>
<p>Reboot the server after installation and check your IP within the VNC console. Try to login via ssh.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_16.png" alt="alt text">
  </p>
<p>Perform basic checks especially the disk space! The root directory should be assigned the biggest chunk of the disk size.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_17.png" alt="alt text">
  </p>
<h3 id="install-gns3-server">Install GNS3 Server</h3>
<p>I am sharing the link to the official documentation here for reference. It says to execute the following commands as root:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo -i
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /tmp
</span></span><span class="line"><span class="cl">curl https://raw.githubusercontent.com/GNS3/gns3-server/master/scripts/remote-install.sh &gt; gns3-remote-install.sh
</span></span><span class="line"><span class="cl">bash gns3-remote-install.sh --with-openvpn --with-iou --with-i386-repository
</span></span></code></pre></div><p>Now if we check the status of the gns3 services. It would always show it’s activating</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_18.png" alt="alt text">
  </p>
<p>If you check the log file mentioned there you would see the following</p>
<blockquote>
<p>CRITICAL web_server.py:88 Could not start the server: [Errno 99] error while attempting to bind on address (‘172.16.253.1’, 3080): cannot assign requested address</p>
</blockquote>
<p>Let’s check the configuration file getting loaded and see if we can see anything there.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_19.png" alt="alt text">
  </p>
<p>The controller config file can be found in the highlighted path shown above. At the same time we can see GNS3 is trying to listen on 172.16.253.1.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_20.png" alt="alt text">
  </p>
<p>This seems to be the default IP configured in the installation. Change this to the local IP of your VM.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_21.png" alt="alt text">
  </p>
<p>Restart GNS3 and check the status again. It should be running now.</p>
<p>
    <img src="/posts/install-ubuntu-server-22-04-gns3-server-on-unraid/unraid_22.png" alt="alt text">
  </p>
<p>At this point you should be able to connect from a remote GNS3 GUI to this server.</p>
<p>Lastly to start GNS3 server automatically every reboot, just enable the service:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl <span class="nb">enable</span> gns3
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Moving files within Unraid</title>
      <link>https://luislogs.com/posts/moving-files-within-unraid/</link>
      <pubDate>Tue, 28 Mar 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/moving-files-within-unraid/</guid>
      <description>Moving files within Unraid One of the first questions I asked myself is how can I transfer files from an external drive to one of the shares in my array. This is so I can use that external SSD as another cache pool in my Unraid setup. And SpaceInvaderOne saves the day again. I summarized his video below. Follow at your own risk or better yet just watch SpaceInvaderOne’s video here.</description>
      <content:encoded><![CDATA[<p>Moving files within Unraid
One of the first questions I asked myself is how can I transfer files from an external drive to one of the shares in my array. This is so I can use that external SSD as another cache pool in my Unraid setup. And SpaceInvaderOne saves the day again. I summarized his video below. Follow at your own risk or better yet just watch SpaceInvaderOne’s video here.</p>
<p>In bullet points:</p>
<ul>
<li>All disks and shares can be found in /mnt/.</li>
<li>/mnt/disk1/ is location of disk1.</li>
</ul>
<p>
    <img src="/posts/moving-files-within-unraid/unraid_1.png" alt="alt text">
  </p>
<ul>
<li>/mnt/user0 contains all of the shares but only those of which are in the array and not the cache.</li>
<li>Shares are spread across the disks.</li>
<li>All shares on unRAID can be found in /mnt/user. Try to click on any of the share and it will show you the path.</li>
<li>If you mount an external drive from the Unassigned Devices, the path will be in /mnt/disks/</li>
<li>If you try to mount a remote share (e.g. a SMB share from another unraid system), the path will be in /mnt/remotes/.</li>
<li>There are different tools which can be used for data transfer e.g. Krusader, rsync, remote shares, or by using the cli.</li>
</ul>
<h3 id="how-to-use-krusader">How to use Krusader</h3>
<p>Install binhex krusader.</p>
<p>Delete /media</p>
<p>
    <img src="/posts/moving-files-within-unraid/unraid_2.png" alt="alt text">
  </p>
<p>We will create three different folders. One to access local shares, one for unassigned disks, and another one for remote shares.</p>
<p>On the bottom part click ‘Add another path, port, variable, label or device.’</p>
<p>Follow as below:</p>
<p>
    <img src="/posts/moving-files-within-unraid/unraid_3.png" alt="alt text">
  </p>
<p>Add another one as below:</p>
<p>
    <img src="/posts/moving-files-within-unraid/unraid_4.png" alt="alt text">
  </p>
<p>And another one as below:</p>
<p>
    <img src="/posts/moving-files-within-unraid/unraid_5.png" alt="alt text">
  </p>
<p>Click Apply and wait for installation to complete.</p>
<p>Go to Docker &gt; Krusader &gt; WebUI</p>
<p>
    <img src="/posts/moving-files-within-unraid/unraid_6.png" alt="alt text">
  </p>
<p>Go through the wizard by clicking on OK.</p>
<p>On both the left and right side panel go to the / directory. And from here add a profile called ‘startup’.</p>
<p>
    <img src="/posts/moving-files-within-unraid/unraid_7.png" alt="alt text">
  </p>
<p>Go to Settings &gt; Configure Krusader &gt; Startup profile &gt; choose startup. Apply.</p>
<p>
    <img src="/posts/moving-files-within-unraid/unraid_8.png" alt="alt text">
  </p>
<p>Now you can navigate to your directories and copy or move files across the panels. One good thing to note is even if you close the browser, the copying or moving of files will continue as long as the docker application is running. For attaching external drives via USB, you will have to mount them on the ‘Main’ tab before it appears in /mnt/disks/ directory.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Configure port forwarding on your home router</title>
      <link>https://luislogs.com/posts/configure-port-forwarding-on-your-home-router/</link>
      <pubDate>Mon, 20 Mar 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/configure-port-forwarding-on-your-home-router/</guid>
      <description>While setting up Nextcloud on my Unraid server, there was a part where I had to port forward incoming traffic to a reverse proxy. Here I will show you how to set it up on my TP-Link router.
The port forwarding function can be found in Advanced Settings &amp;gt; NAT forwarding &amp;gt; Virtual Server. Do note the name and location of this function can vary between brands and even models of the same brand.</description>
      <content:encoded><![CDATA[<p>While setting up Nextcloud on my Unraid server, there was a part where I had to port forward incoming traffic to a reverse proxy. Here I will show you how to set it up on my TP-Link router.</p>
<p>The port forwarding function can be found in Advanced Settings &gt; NAT forwarding &gt; Virtual Server. Do note the name and location of this function can vary between brands and even models of the same brand.</p>
<p>
    <img src="/posts/configure-port-forwarding-on-your-home-router/router_1.png" alt="alt text">
  </p>
<p>Here I just need to make a new entry to point the external port 443 to my Unraid internal IP with port 1443.</p>
<p>
    <img src="/posts/configure-port-forwarding-on-your-home-router/router_2.png" alt="alt text">
  </p>
<p>To put it simply how this works, anyone who tries to access your public IP address via https or port 443 will be redirected to whatever local IP and port you specify. Now you can try to access your external IP with http or whatever and your connection should be forwarded to the correct service. In the example below nc.su-root.net is pointing to my public IP and internally getting forwarded to Swag.</p>
<p>
    <img src="/posts/configure-port-forwarding-on-your-home-router/router_3.png" alt="alt text">
  </p>
]]></content:encoded>
    </item>
    
    <item>
      <title>New NAS and Homelab setup using Unraid</title>
      <link>https://luislogs.com/posts/new-nas-and-homelab-setup-using-unraid/</link>
      <pubDate>Sat, 18 Mar 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/new-nas-and-homelab-setup-using-unraid/</guid>
      <description>Just very recently I managed to assemble my second NAS — a beefed up version of my first one back in 2012. I decided to settle on an 11th-gen Intel system.
Motherboard: Asrock H570M-ITX/AC CPU: Intel Core i7-11700 RAM: Kingston Fury DDR4 3200MT/s 16GB x 2 Drives: 2x 8 TB WD Red Plus, 2x 1TB Samsung 980 NVMe, 1x 500GB Samsung 860 EVO PSU: Fractal Design ION SFX-L 500W 80PLUS Gold Case: Jonsbo N1 Below you can find the steps I followed to install unRAID v6.</description>
      <content:encoded><![CDATA[<p>Just very recently I managed to assemble my second NAS — a beefed up version of my first one back in 2012. I decided to settle on an 11th-gen Intel system.</p>
<ul>
<li>Motherboard: Asrock H570M-ITX/AC</li>
<li>CPU: Intel Core i7-11700</li>
<li>RAM: Kingston Fury DDR4 3200MT/s 16GB x 2</li>
<li>Drives: 2x 8 TB WD Red Plus, 2x 1TB Samsung 980 NVMe, 1x 500GB Samsung 860 EVO</li>
<li>PSU: Fractal Design ION SFX-L 500W 80PLUS Gold</li>
<li>Case: Jonsbo N1</li>
</ul>




	




































  	
	

	
		<script src="/shortcode-gallery/jquery-3.7.0.min.js"></script>
	
	
	
		<script src="/shortcode-gallery/lazy/jquery.lazy.min.js"></script>
	

	<script src="/shortcode-gallery/swipebox/js/jquery.swipebox.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/swipebox/css/swipebox.min.css">

	<script src="/shortcode-gallery/justified_gallery/jquery.justifiedGallery.min.js"></script>
	<link rel="stylesheet" href="/shortcode-gallery/justified_gallery/justifiedGallery.min.css"/>


<style>
	

	
</style>





<div id="gallery-21f3079619a363458d8ea513db596d29-0-wrapper" class="gallery-wrapper">
<div id="gallery-21f3079619a363458d8ea513db596d29-0" class="justified-gallery">
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_1.jpg" 
					class="galleryImg"
					
						

						
							data-description=" + <br/>mm f/ sec ISO "
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOetPDcUOrxJPqke9V3qiqMscdD2H49aypYNRg1Rri63BoSBjeSygdMHr24NS2&#43;oXGoJEsSrKLcNvZQA4XPUE&#43;melbNzepq&#43;mxWsMQN6hKrcbx8yehHv6cY4PFLbcuyexoaNqFla6zJbXN0kkssai3kGMEHnacdG6ex/Sul8yP1H5ivFLqF7aYgSK4BxuU5Ge4qL7RJ6/pVOF9TncLs0NLma2lMsbGOZRlT/AHq63TNGEmkyz4NrG6jeR98E87sdx&#43;ny&#43;xrH1LQGhvJYnkMbH5om7N6GrLeKmXS4oSJTeQjy5kbkMoHOcdiQPoee3Keq0NmmtDQl8O21xpu65kiQW5WMbOWdR2OO4HT1qj/wjeg/37v8j/hWppVwk0tqmpXEMFvMplEJfYAACQCewz2PX9DuY8Of897D/wACEpJ20NOW&#43;5//2Q=="
							
							class="lazy"
							data-src="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_1_hu502a9500bb1a9b943faea08ca89d4aac_245901_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_2.jpg" 
					class="galleryImg"
					
						

						
							data-description=" + <br/>mm f/ sec ISO "
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOd0/XZ9qwresdkUiW/8AVhjaO3UVfhmn1DSJlmZ2udrDc4O4DPHX2NcKkZMfmA8jgqehrt9H1Sx0xbgXUMkuWJTy03EjGD&#43;GBRO7dxw7HJ6hpqbVuY22ngkDpVHD/8APT&#43;daWuXE0czJEGjgkJIR1AYA8gH0PNY3mv6mnFaCle5ftSzSPCkTSs74VF6nNd34ethpVzHE8QeaTH71WJUN/EB6ds9/wAMVxeg3/2O&#43;YhF3OBhyMleecCvQ9MvLQ6e9yv7zafuggMWA4HsfSplcqDSepQ&#43;JGnRsks7Li4DgjH8S44H4V5r5cn92u41y4a&#43;kuZXuBIjBQq5PyLtAI9Mda537FY/3v8Ax4URdkOSP//Z"
							
							class="lazy"
							data-src="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_2_hub2c550302bac5fd45f7929f1d478f373_229477_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_3.jpg" 
					class="galleryImg"
					
						

						
							data-description=" + <br/>mm f/ sec ISO "
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AOS8OOZp40RG8lhk5O4DHBB6YBGBz6D1q5q3hWO1D31m4mixhwhyYmPr7e/r&#43;FdBoVhp9hstoY/lChhuHLH1z3q9dvZWF008s6rCIylwpHylSOAfU&#43;1Kxa8zxu7SSO4YSMWOeCe9Q7q6jxBp8U6C7tLe4it5ctCZ0wSP6/59a577Dcf882/75NVF6EtHY6R4mWW0EshcXcY8vZGuTLn07c45z&#43;FdBpthc3bLPqkaYfmOFhlYwPX1NcDpGqQ2OwhHWRG3CRSDg&#43;69x17jrXdadrUOoxQ3UG4bTiRB95Tjp&#43;PBB9u1JiS7nQzQw3kD2GoRGS3mG0MBynpj0qh/wgPhz/npc/8AfysXXvFzQo1tp7KZhwzg8J7D1P8AKuc/4SrX/wDn8b/vs1LLV7H/2Q=="
							
							class="lazy"
							data-src="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_3_hu85637e8f45c63fbf3d6468419f54d66f_209538_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_4.jpg" 
					class="galleryImg"
					
						

						
							data-description=" + <br/>mm f/ sec ISO "
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/ALVt4vghtI4ptPkjMKLHzIOSB7DHakl8dRpJGn9jynIBUmcAH8cda46xuplNyLhykzgq6bATnjsehH9O9WLSeO5kkt5rgW8bMdrMoERbJ&#43;V8cjPrkUnFEqETcvvFBu55Zf7NeMsBgGUHGBj0rO/t2f8A59//ACJ/9aqdyvm5TT38uRflltpiWKt6q/dTjv8AnzVT7HrH9yL/AL6T/wCKotYqNOJUv9Fv4y1xNIOuWEeePw4q5o9xGuk3cjhHlBYuX5LDgn&#43;tdBPbS3Ez&#43;aI4ZD8zLNIFwT169f8APSucuvD1y8l3LYzRHYMSRoTzkduMGlzp9S5Qa&#43;EpQ6pdg&#43;SHZgAW2j9fwqT&#43;1Jv7h/I1St7GeWceWreachgnRexBJ6Vc/sS8/wCebf8Af6qbSErn/9k="
							
							class="lazy"
							data-src="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_4_hu6f80a1014ee310c05e5be6e7e297d998_240949_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_5.jpg" 
					class="galleryImg"
					
						

						
							data-description=" + <br/>mm f/ sec ISO "
						

						
					
					>
					<img			
						width="450" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/APMtN8O3&#43;pL5qoILcDJnmO1APX3rahsNL0q1ku4bV9UaL71w64hU&#43;w/i/wA81oXcWp29qtxqOnvfGcgwZVvKjAyT8g79OfSp4/Dt1rKQzapdRwxnlIYkGEU8n0/H&#43;dJtlpdjJ0HxHfw&#43;KoLm6nKRpgNEV2KFJHQfkfXivSv&#43;E007/n5j/OvO/FGjpawiJJkkniUYJIJZBnn8f8a5TyZvb86Vkx3aPb5PKk0QrJfXN3M43qjxhhEeeMgAnIwNuSax726uLeZbdFltZsDzAgV5jgcAMOFz1J657jFchpHiaOw0q8tpY2uQcsnmrvI3cEc8fj7nikvvFF9fWyRSyYdBj92cHZ2DMenPc880WHzF/WoGKOTKsV0ASGklMjsPRjzz/nNc9tv/APn9j/77qld6ip4DCZv7vRB/Vj9ePaqn29/&#43;eFv/AN&#43;hVEtn/9k="
							
							class="lazy"
							data-src="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_5_hu1ef7542de73363a875a7dfa6a4194309_664266_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
		
		
				
			
			
			
				
			

			
			
				
					
				
			


			
			
			

			
			


			<div>
				
				
					
				
				<a href="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_6.jpg" 
					class="galleryImg"
					
						

						
							data-description=" + <br/>mm f/ sec ISO "
						

						
					
					>
					<img			
						width="380" height="600"

						
							
							style="filter: blur(25px);"
							
								src="data:image/jpeg;base64,/9j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4&#43;JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIACAAFAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5&#43;gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4&#43;Tl5ufo6ery8/T19vf4&#43;fr/2gAMAwEAAhEDEQA/AM6fUbKa/aJ28mNWU5WMIAMHoPxretdM8M3dnEkN08LxqAcHLPjvjpXnr3K3JEo7gAj0OK0/D0xiecqcEsMn2xR0KWrs0ejrKz7mRztLHGRjil3yf89K56PxZZ2cawSwXDso&#43;8kQYHPPXcP5U/8A4TXT/wDn2uv/AAHH/wAXWfKzBp32OK1gWUd9tsFxFsGcdCcnNMs5fKtJ2BwTIB&#43;lZ&#43;p/a7KbLxiSMceYpyD/AIU60llvLdoolKKzZZz24xge9WtjoesmJM2rPKZIDOI25XaTj0/pUf8AxO/W5/M1qpAY0Chm4GODS&#43;W395vzquaRXs4n/9k="
							
							class="lazy"
							data-src="/posts/new-nas-and-homelab-setup-using-unraid/images/unraid_6_hub08adc2356c684ea6288757b7e3d889a_172267_600x600_fit_q90_lanczos.jpg"
						

						
							
								
							
						
					>
				</a>
			</div>
		
	
</div>
</div>

<script>
	if (!jQuery) {
		alert("jquery is not loaded");
	}

	$( document ).ready(() => {
		const gallery = $("#gallery-21f3079619a363458d8ea513db596d29-0");
		

		
		let swipeboxInstance = null;

		
		
		gallery.on('jg.complete', () => {
			
				
				
				$(() => {
					$('.lazy').Lazy({
						visibleOnly: true,
						afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
					});
				});
			

			swipeboxInstance = $('.galleryImg').swipebox(
				jQuery.extend({},
					{  }
				)
			);
		});

		
		gallery.justifiedGallery({
			rowHeight : "150",
			margins : "5",
			border : 0,
			randomize :  false ,
			waitThumbnailsLoad : false,
			lastRow : "justify",
			captions : false,
			
			
		});

		
		
	});
</script>

<p>Below you can find the steps I followed to install unRAID v6.11.5 with the help of <a href="https://youtu.be/CcRwT7iHIcc">SpaceInvaderOne’s video</a>. Of course only follow at your own risk. I would even advise you to go to SpaceInvaderOne’s youtube channel and watch his videos instead.</p>
<h3 id="preparation">Preparation</h3>
<ol>
<li>Create a bootable USB with the Unraid USB creator and allow UEFI boot since there is no external GPU.</li>
<li>Change boot order to have USB as first priority.</li>
<li>Enable VD-T and all other virtualization settings in BIOS.</li>
<li>Disable built-in RAID function in BIOS.</li>
<li>Plug server to router and access using kamata.local (name-of-server.local)</li>
<li>Change to a static IP address by going under Settings &gt; Network Settings.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_7.webp" alt="alt text">
  </p>
<ol start="7">
<li>Go to plugins tab and install community plugins manager. You can get this from Unraid forum &gt; Plugin support &gt; <a href="https://forums.unraid.net/topic/38582-plug-in-community-applications/#comments">[Plug-In] Community Applications</a>. Acquire the URL under the INSTALLATION section.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_8.webp" alt="alt text">
  </p>
<ol start="8">
<li>Go back to Installed Plugins and click on the icon of Community Applications</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_9.webp" alt="alt text">
  </p>
<ol start="9">
<li>Go to Plugins &gt; Preclear Disk icon. Preclear only the mechanical disks and not NVMe or SSDs. Below the first one has already been started. You can leave everything else in default or you can just skip pre-read to save on time. My 8TB drives took about 13 hours to complete the zeroing process and another 13 hours to complete post-read.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_10.webp" alt="alt text">
  </p>
<ol start="10">
<li>Once preclear is done, you can create the array.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_11.webp" alt="alt text">
  </p>
<h3 id="creating-the-array">Creating the array</h3>
<p>It’s really surprising how easy it is to create the array in just a few clicks.</p>
<ol>
<li>In the Main tab, assign one of the mechanical disks as parity but do note the parity drive should always be equal or bigger than any of the other drives in your array. Assign another drive as disk 1.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_12.webp" alt="alt text">
  </p>
<ol start="2">
<li>Start the array and confirm the warning message prompt.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_13.webp" alt="alt text">
  </p>
<ol start="3">
<li>You will see that the disks are unmountable. This is because they haven’t been formatted yet. Go to the bottom part and formal all the disks. Ensure those are only disks part of the new array.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_14.webp" alt="alt text">
  </p>
<ol start="4">
<li>Once formatting is complete, the parity-sync will start automatically. This will take time depending on the drive capacity. As for my case it took around another 13 hours. Do note that you don’t have to wait for parity to complete before you can start using the array. You can already upload files at this point but you will not have redundancy not unless parity is complete.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_15.webp" alt="alt text">
  

    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_16.webp" alt="alt text">
  </p>
<ol start="5">
<li>When parity is completed you will see a green dot on the left side of device name. You can also scroll down and you should see a text showing parity is valid. At this point anything you upload into the disk will have redundancy. Just remember that parity needs to be calculated every time you modify something in the disk.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_17.webp" alt="alt text">
  </p>
<h3 id="creating-a-cache-pool">Creating a cache pool</h3>
<p>Now that you have the array running with parity-sync complete, you can start configuration of the cache pool. Here you will see that I still have my NVMe drives under unassigned devices. I plan have them running on RAID1 as well – same as the array.</p>
<ol>
<li>Stop the array so you can add a cache pool. I named it cache_protected to remind me this cache has redundancy (any name will do). Click on Add Pool and assign 2 slots. Select the one drive for each of the slot.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_18.webp" alt="alt text">
  </p>
<ol start="2">
<li>You can click on the name of the cache pool to see the settings. I kept mine on default. Start the array. It will show the the disks again are unmountable so you just have to format them the same way it was done for array disks.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_19.webp" alt="alt text">
  </p>
<ol start="3">
<li>Cache pool creation will not take so long. When that’s done the second device under Pool Devices will have a text saying that it is part of a pool.</li>
</ol>
<p>
    <img src="/posts/new-nas-and-homelab-setup-using-unraid/unraid_20.webp" alt="alt text">
  </p>
<p>This is the final step to running your array with a cache pool. If you want to move files into any of the shares you can use a docker application called Krusader. I just tried this today but haven’t had more time to explore so I am parking this for now.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Configure Dynamic DNS to reach your home network</title>
      <link>https://luislogs.com/posts/configure-dynamic-dns-to-reach-your-network-at-home/</link>
      <pubDate>Sun, 12 Mar 2023 18:40:30 +0900</pubDate>
      
      <guid>https://luislogs.com/posts/configure-dynamic-dns-to-reach-your-network-at-home/</guid>
      <description>For this first post I want to start with the basic question — how do you reach your server at home with a dynamic public IP? I am almost sure that this is the most asked question when starting with self-hosting especially if you have the mindset of being able to access your environment anytime, anywhere. Well, at least for me, this is the very first question I had in mind when I was just starting back then.</description>
      <content:encoded><![CDATA[<p>For this first post I want to start with the basic question — how do you reach your server at home with a dynamic public IP? I am almost sure that this is the most asked question when starting with self-hosting especially if you have the mindset of being able to access your environment anytime, anywhere. Well, at least for me, this is the very first question I had in mind when I was just starting back then.</p>
<p>For us who don&rsquo;t want to pay more for a static IP, luckily there is a service called dynamic DNS. This is, for most of the time, free and should be supported by most modern home routers.</p>
<h3 id="what-is-dynamic-dns-ddns">What is Dynamic DNS (DDNS)?</h3>
<p>Let me explain this in layman&rsquo;s terms. First, DNS stands for domain name service. A domain is the name you type when you want to access a website such as <a href="https://www.google.com">www.google.com</a> or <a href="https://www.su-root.net">www.su-root.net</a>. Domains will always be mapped to a public IP address as long as it&rsquo;s registered.</p>
<p>Normally, a website will have a static public IP throughout its lifetime unless it is moved to a different server. For websites hosted by hosting companies such as AWS or Bluehost, the mapping of the IP address to a domain name is most of the time managed by them. This is as long as the domain provider is configured to use their nameservers.</p>
<p>For private hosts running behind a normal internet service provider, this is where DDNS comes into picture. If your ISP keeps changing your IP address (e.g. daily or weekly), then DDNS keeps track of this and updates their records automatically (as long as your router supports this feature). This is the best way for people who don&rsquo;t want to acquire a static IP that incur additional costs on their monthly bill.</p>
<h3 id="how-do-you-configure-dynamic-dns">How do you configure Dynamic DNS?</h3>
<p>Now let me walk you through how to configure it with the help of my home router.
Before anything else you need to create an account from a DDNS provider such as no-ip.</p>
<p>
    <img src="/posts/configure-dynamic-dns-to-reach-your-network-at-home/noip.png" alt="alt text">
  </p>
<p>From the Dashboard, go to &gt; Dynamic DNS &gt; No-IP Hostnames then click Create Hostname.</p>
<p>
    <img src="/posts/configure-dynamic-dns-to-reach-your-network-at-home/noip_2.png" alt="alt text">
  </p>
<p>Input your preferred hostname and select any of the free domains. Keep Record Type as DNS Host (A), input your current public IP address, and proceed to create the hostname. You can check your IP here if you don&rsquo;t know it yet.</p>
<p>Next is to configure the DDNS on the router.</p>
<p>What I have is a TP-Link Archer A10 from 3 years ago and it still works perfectly fine without any hiccup. Like I said, most modern routers should support DDNS, but if yours don&rsquo;t and you plan to walk the path of self-hosting, then you can get a new or even a used one for just a couple of bucks.</p>
<p>
    <img src="/posts/configure-dynamic-dns-to-reach-your-network-at-home/router.png" alt="alt text">
  </p>
<p>Login to your router&rsquo;s web GUI. This is, for 99% of the time, the same as your default gateway IP. Mine happens to be &ldquo;http://10.0.0.1&rdquo;. You can also check your current WAN IP from here.</p>
<p>
    <img src="/posts/configure-dynamic-dns-to-reach-your-network-at-home/router2.png" alt="alt text">
  </p>
<p>Next, go to the dynamic DNS configuration. In my case it&rsquo;s under Advanced Settings &gt; Network.
Select NO-IP, input your credentials and the newly created domain. Save.</p>
<p>From here on your router will keep updating your DDNS (No-IP) every time your IP address changes. To verify if your configuration is working, you can do a DNS look up and check if it&rsquo;s correctly mapped to your IP.</p>
<p>
    <img src="/posts/configure-dynamic-dns-to-reach-your-network-at-home/whois.png" alt="alt text">
  </p>
<p>This is the first step to the path of self-hosting and once you have the services running on your server, then you can expose them later on by port forwarding which I will be covering in a different post.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
