<?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 dKade's notes</title><link>https://dkade.com/posts/</link><description>Recent content in Posts on dKade's notes</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>dkade@dkade.com (Daniel Loureiro)</managingEditor><webMaster>dkade@dkade.com (Daniel Loureiro)</webMaster><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://dkade.com/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Running HomeAssistant Core in FreeBSD Jails with BastilleBSD</title><link>https://dkade.com/posts/homeassistant_freebsd_jail/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><author>dkade@dkade.com (Daniel Loureiro)</author><guid>https://dkade.com/posts/homeassistant_freebsd_jail/</guid><description>A complete guide to running HomeAssistant Core in a VNET FreeBSD jail managed with BastilleBSD, with separate jails for Mosquitto MQTT and Zigbee2MQTT with USB passthrough</description><content:encoded><![CDATA[<p>I run HomeAssistant Core on FreeBSD inside a jail managed with BastilleBSD — no Docker, no VM, just a lightweight VNET jail with its own IP on the LAN. This post covers the full setup: three jails, a bootstrap script, USB passthrough for Zigbee, and WiFi devices on the same subnet.</p>
<h2 id="architecture">Architecture</h2>
<pre tabindex="0"><code>FreeBSD 15.1-RELEASE Host
│
├── homeassistant (192.168.2.50)  — HA Core :8123
├── mosquitto    (192.168.2.52)  — MQTT broker :1883
└── zigbee2mqtt  (192.168.2.51)  — Zigbee2MQTT, Sonoff USB passthrough
</code></pre><p>All three jails are VNET on the same <code>/24</code> subnet. HA Core talks to Mosquitto over MQTT, Zigbee2MQTT publishes device states to Mosquitto, and HA subscribes. Shelly devices connect directly over WiFi and are auto-discovered by HA on the LAN.</p>
<h2 id="why-jails">Why jails?</h2>
<ul>
<li>Native FreeBSD — no Docker overlay, no Linux emulation</li>
<li>VNET gives each jail a real IP on the LAN — devices discover HA directly</li>
<li>ZFS snapshots for quick rollbacks before upgrades</li>
<li>Each service in its own jail — clean isolation, restart one without affecting others</li>
</ul>
<h2 id="creating-the-jails">Creating the jails</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bastille create -V homeassistant 15.1-RELEASE 192.168.2.50/24 vtnet0
</span></span><span class="line"><span class="cl">bastille create -V mosquitto 15.1-RELEASE 192.168.2.52/24 vtnet0
</span></span><span class="line"><span class="cl">bastille create -V zigbee2mqtt 15.1-RELEASE 192.168.2.51/24 vtnet0
</span></span></code></pre></div><p>The <code>-V</code> flag gives each jail a VNET interface with its own IP and routing table — they live on the same broadcast domain as the host.</p>
<h2 id="homeassistant-jail">HomeAssistant jail</h2>
<p>A bootstrap script handles the full setup:</p>
<ul>
<li><strong>Repository</strong>: <a href="https://codeberg.org/dkade/BSD/src/branch/main/FreeBSD/install_ha_freebsd.sh">codeberg.org/dkade/BSD</a></li>
<li><strong>Method</strong>: Python 3.14 virtual environment under <code>/usr/local/etc/homeassistant/venv</code></li>
<li><strong>User</strong>: dedicated <code>hass</code> system user</li>
<li><strong>Service</strong>: FreeBSD rc.d script using <code>daemon -u hass</code></li>
<li><strong>Enable</strong>: <code>sysrc homeassistant_enable=YES</code></li>
</ul>
<p>What the script does:</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"># Inside the jail as root</span>
</span></span><span class="line"><span class="cl">fetch https://codeberg.org/dkade/BSD/raw/branch/main/FreeBSD/install_ha_freebsd.sh
</span></span><span class="line"><span class="cl">sh install_ha_freebsd.sh
</span></span></code></pre></div><p>It installs system packages (<code>python314</code>, <code>gcc12</code>, <code>rust</code>, <code>dbus</code>, <code>libffi</code>, <code>openssl</code>, etc.), creates a venv, and installs <code>homeassistant</code> along with <code>numpy</code>, <code>zlib_ng</code>, and <code>isal</code> for performance. The rc.d script starts HA as the <code>hass</code> user and listens on port 8123.</p>
<p>After first boot, complete the onboarding at <code>http://192.168.2.50:8123</code>.</p>
<h2 id="mosquitto-jail">Mosquitto jail</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bastille console mosquitto
</span></span><span class="line"><span class="cl">pkg install mosquitto
</span></span><span class="line"><span class="cl">sysrc <span class="nv">mosquitto_enable</span><span class="o">=</span>YES
</span></span><span class="line"><span class="cl">service mosquitto start
</span></span></code></pre></div><p>No authentication is needed on a local subnet — configure <code>listener 1883 192.168.2.52</code> in <code>/usr/local/etc/mosquitto/mosquitto.conf</code> to bind to the jail IP.</p>
<h2 id="zigbee2mqtt-jail">Zigbee2MQTT jail</h2>
<p>This jail needs access to the Sonoff Zigbee coordinator plugged into the host.</p>
<h3 id="usb-passthrough-with-devfs">USB passthrough with devfs</h3>
<p>On the <strong>host</strong> (<code>/etc/devfs.rules</code>):</p>
<pre tabindex="0"><code>[bastille_usb=101]
add path &#39;usbctl&#39; mode 0660
add path &#39;usb/*&#39; mode 0660
add path &#39;ugen*&#39; mode 0660
add path &#39;cuaU*&#39; mode 0660
add path &#39;ttyU*&#39; mode 0660
</code></pre><p>Set the rule in <code>/usr/local/bastille/jails/zigbee2mqtt/jail.conf</code>:</p>
<pre tabindex="0"><code>devfs_ruleset = &#34;bastille_usb&#34;;
</code></pre><p>Then find the Sonoff device:</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"># On the host</span>
</span></span><span class="line"><span class="cl">ls /dev/cuaU*
</span></span><span class="line"><span class="cl"><span class="c1"># /dev/cuaU0 &lt;- this is the Sonoff</span>
</span></span></code></pre></div><p>Restart the jail and you should see the device inside:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bastille restart zigbee2mqtt
</span></span><span class="line"><span class="cl">bastille console zigbee2mqtt
</span></span><span class="line"><span class="cl">ls /dev/cuaU*
</span></span></code></pre></div><h3 id="installing-and-configuring">Installing and configuring</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bastille console zigbee2mqtt
</span></span><span class="line"><span class="cl">pkg install node npm mosquitto-libs
</span></span><span class="line"><span class="cl">npm install -g zigbee2mqtt
</span></span></code></pre></div><p>Create <code>/usr/local/etc/zigbee2mqtt/configuration.yaml</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">mqtt</span><span class="p">:</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">mqtt://192.168.2.52:1883</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">serial</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="l">/dev/cuaU0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">frontend</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></code></pre></div><p>rc.d script at <code>/usr/local/etc/rc.d/zigbee2mqtt</code> to start via <code>daemon</code>, then <code>sysrc zigbee2mqtt_enable=YES &amp;&amp; service zigbee2mqtt start</code>.</p>
<h2 id="device-integration">Device integration</h2>
<ul>
<li><strong>Shelly</strong> (WiFi): connect them to your WiFi. HA auto-discovers them via the LAN once the Shelly integration is added in the UI.</li>
<li><strong>Zigbee</strong> (Sonoff sensors, bulbs, etc.): pair through the Zigbee2MQTT frontend (<code>http://192.168.2.51:8080</code>). HA receives device states via MQTT auto-discovery.</li>
</ul>
<h2 id="upgrading">Upgrading</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># HA Core</span>
</span></span><span class="line"><span class="cl">bastille console homeassistant
</span></span><span class="line"><span class="cl">service homeassistant stop
</span></span><span class="line"><span class="cl">su -l hass -c <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s1">&#39;HOME=/usr/local/etc/homeassistant PIP_CACHE_DIR=/tmp/pip-cache \
</span></span></span><span class="line"><span class="cl"><span class="s1">   /usr/local/etc/homeassistant/venv/bin/pip install --upgrade homeassistant&#39;</span>
</span></span><span class="line"><span class="cl">service homeassistant start
</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"><span class="c1"># zigbee2mqtt</span>
</span></span><span class="line"><span class="cl">bastille console zigbee2mqtt
</span></span><span class="line"><span class="cl">service zigbee2mqtt stop
</span></span><span class="line"><span class="cl">npm update -g zigbee2mqtt
</span></span><span class="line"><span class="cl">service zigbee2mqtt start
</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"><span class="c1"># mosquitto</span>
</span></span><span class="line"><span class="cl">bastille console mosquitto
</span></span><span class="line"><span class="cl">pkg upgrade mosquitto
</span></span><span class="line"><span class="cl">service mosquitto restart
</span></span></code></pre></div><h2 id="gotchas">Gotchas</h2>
<ul>
<li><strong>Python compilation</strong>: HA Core compiles C and Rust extensions during <code>pip install</code>. Make sure <code>gcc12</code>, <code>rust</code>, <code>libffi</code>, and <code>openssl</code> are installed in the jail before installing HA.</li>
<li><strong>dbus</strong>: HA Core expects D-Bus to be running (<code>service dbus start</code>). The rc.d script has <code>REQUIRE: dbus</code>, so dbus starts first.</li>
<li><strong>USB devfs</strong>: The devfs ruleset must be applied <em>before</em> the jail starts. Double-check the ruleset number matches in both <code>/etc/devfs.rules</code> and the jail config.</li>
<li><strong>VNET routing</strong>: If jails can&rsquo;t reach the internet for <code>pkg install</code>, check that <code>gateway_enable=&quot;YES&quot;</code> is set on the host and NAT/pf is configured for the VNET subnet.</li>
<li><strong>Persistent pip cache</strong>: The script sets <code>PIP_CACHE_DIR=/tmp/pip-cache</code> so you don&rsquo;t re-download wheels on every upgrade.</li>
</ul>
<h2 id="summary">Summary</h2>
<p>Three FreeBSD jails managed with BastilleBSD running HA Core, Mosquitto, and Zigbee2MQTT — each with its own IP, rc.d service, and clean isolation. The bootstrap script at <code>codeberg.org/dkade/BSD</code> automates the HA jail from scratch, and the rest is just standard FreeBSD package management.</p>
<hr>
<p><em>Disclaimer: I use AI as a productivity tool. For a senior engineer, AI is incredibly powerful as one can focus on the solution design and conceptualization and leave the boring part that is implementation to the AI.</em></p>
]]></content:encoded></item><item><title>Tips and lessons learnt by using AWS FMS and WAFv2</title><link>https://dkade.com/posts/aws_tips_waf_fms/</link><pubDate>Thu, 11 Mar 2021 00:00:00 +0000</pubDate><author>dkade@dkade.com (Daniel Loureiro)</author><guid>https://dkade.com/posts/aws_tips_waf_fms/</guid><description>&lt;ul&gt;
&lt;li&gt;Properly plan for your WAF logs, this will be the hardest part;&lt;/li&gt;
&lt;li&gt;Logs are activated per WebACL so each account needs to have a properly configured Kinesis Data Firehose. It is possible to enable the logs from FMS&lt;/li&gt;
&lt;li&gt;When creating Cloudformation stack make sure you make resources dependent on another or WAF will quickly rate limit Cloudformation and your stack will fail&lt;/li&gt;
&lt;li&gt;Use JSON in Cloudformation if you have accounts starting with 0. PyYAML has a &lt;a href="https://github.com/yaml/pyyaml/issues/98"&gt;bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Instead of Cloudformation you can use Terraform (when I started building the solution this wasn&amp;rsquo;t possible)&lt;/li&gt;
&lt;li&gt;Before activating an account on a FMS Policy, make sure that account has AWS Config enabled&lt;/li&gt;
&lt;li&gt;Use AWS Managed rules as they are created with standards in mind for example protecting from &lt;a href="https://owasp.org/www-project-top-ten/"&gt;OWASP Top 10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS WAF uses web ACL capacity units and you are limited to 1500, you can request increase but it&amp;rsquo;s not simple, so plan accordingly because if you need to change the WCU capacity of a Rule group you have to create a new one and delete the old one&lt;/li&gt;
&lt;li&gt;Rate limit rules can ONLY be added to WebACLs and are related to that WebACL. There is no option at the moment to add them to FMS, we are working with AWS to change this;&lt;/li&gt;
&lt;/ul&gt;</description><content:encoded><![CDATA[<ul>
<li>Properly plan for your WAF logs, this will be the hardest part;</li>
<li>Logs are activated per WebACL so each account needs to have a properly configured Kinesis Data Firehose. It is possible to enable the logs from FMS</li>
<li>When creating Cloudformation stack make sure you make resources dependent on another or WAF will quickly rate limit Cloudformation and your stack will fail</li>
<li>Use JSON in Cloudformation if you have accounts starting with 0. PyYAML has a <a href="https://github.com/yaml/pyyaml/issues/98">bug</a></li>
<li>Instead of Cloudformation you can use Terraform (when I started building the solution this wasn&rsquo;t possible)</li>
<li>Before activating an account on a FMS Policy, make sure that account has AWS Config enabled</li>
<li>Use AWS Managed rules as they are created with standards in mind for example protecting from <a href="https://owasp.org/www-project-top-ten/">OWASP Top 10</a></li>
<li>AWS WAF uses web ACL capacity units and you are limited to 1500, you can request increase but it&rsquo;s not simple, so plan accordingly because if you need to change the WCU capacity of a Rule group you have to create a new one and delete the old one</li>
<li>Rate limit rules can ONLY be added to WebACLs and are related to that WebACL. There is no option at the moment to add them to FMS, we are working with AWS to change this;</li>
</ul>
]]></content:encoded></item><item><title>How OLX Europe Fights Millions of Bots with AWS</title><link>https://dkade.com/posts/olx_aws_post_/</link><pubDate>Mon, 08 Mar 2021 00:00:00 +0000</pubDate><author>dkade@dkade.com (Daniel Loureiro)</author><guid>https://dkade.com/posts/olx_aws_post_/</guid><description>&lt;p&gt;My latest work done in OLX was featured by AWS on their own blog! I&amp;rsquo;m really happy with it. I talk about using AWS Firewall Manager + WAFv2 and in house tools to fight attacks. A big kudos to &lt;a href="https://www.linkedin.com/in/gabrielsoltz/"&gt;Gabril Soltz&lt;/a&gt; for his amazing work with the WAFBot.&lt;/p&gt;
&lt;p&gt;Read more at -&amp;gt; &lt;a href="https://aws.amazon.com/blogs/architecture/field-notes-how-olx-europe-fights-millions-of-bots-with-aws/"&gt;https://aws.amazon.com/blogs/architecture/field-notes-how-olx-europe-fights-millions-of-bots-with-aws/&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>My latest work done in OLX was featured by AWS on their own blog! I&rsquo;m really happy with it. I talk about using AWS Firewall Manager + WAFv2 and in house tools to fight attacks.  A big kudos to <a href="https://www.linkedin.com/in/gabrielsoltz/">Gabril Soltz</a> for his amazing work with the WAFBot.</p>
<p>Read more at -&gt; <a href="https://aws.amazon.com/blogs/architecture/field-notes-how-olx-europe-fights-millions-of-bots-with-aws/">https://aws.amazon.com/blogs/architecture/field-notes-how-olx-europe-fights-millions-of-bots-with-aws/</a></p>
]]></content:encoded></item></channel></rss>