<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Tailscale on Sysadmin Tales</title>
    <link>https://blog.ssb-tech.net/tags/tailscale/</link>
    <description>Recent content in Tailscale on Sysadmin Tales</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 02 Oct 2026 21:39:10 -0400</lastBuildDate>
    <atom:link href="https://blog.ssb-tech.net/tags/tailscale/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Set up auto-renewing HTTPS certificates on a GLiNet KVM</title>
      <link>https://blog.ssb-tech.net/posts/glinet-tailscale-certs/</link>
      <pubDate>Fri, 02 Oct 2026 20:21:30 -0400</pubDate>
      <guid>https://blog.ssb-tech.net/posts/glinet-tailscale-certs/</guid>
      <description>&lt;p&gt;I&amp;rsquo;m quite fond of the small little IP KVMs that have become more and more available lately.&lt;/p&gt;&#xA;&lt;p&gt;My favorites are the JetKVM and the GliNet Comet series.&lt;/p&gt;&#xA;&lt;p&gt;I have 2 of the Comets that I use internally, and I mostly access them via their Tailscale ts.net names.&lt;/p&gt;&#xA;&lt;p&gt;I &lt;em&gt;could&lt;/em&gt; do I what I usually do and put them behind a reverse proxy, but there&amp;rsquo;s one of these that I keep in my work bag and take with me onto client sites, so there&amp;rsquo;s no reverse proxy in that scenario.&lt;/p&gt;&#xA;&lt;p&gt;I could just ignore the certificate error, but where&amp;rsquo;s the fun in that when we can fix it?&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Activate and log into Tailscale on your device.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Ensure that HTTPS is enabled for your TailNet. This can be done &lt;a href=&#34;https://console.tailscale.com/admin/settings/general&#34;&gt;here.&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;SSH into the GliNet KVM.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Update Tailscale, you heathen.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tailscale update&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Once Tailscale is updated, lets create the renewal script.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vi /etc/tailscale-cert-renew.sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the contents of the script:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;set -eu&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HOSTNAME&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;your-kvm.your-tailnet.ts.net&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CERT_DIR&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/etc/kvmd/user/ssl&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TMP_DIR&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tmp/tailscale-cert&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RENEWAL_WINDOW&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1209600&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 14 days&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Exit if the existing certificate is valid for more than 14 days.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; openssl x509 &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -checkend &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$RENEWAL_WINDOW&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -noout &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -in &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$CERT_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;/server.crt&amp;#34;&lt;/span&gt; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    exit &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Certificate expires within 14 days. Renewing...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm -rf &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$TMP_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$TMP_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CERT&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$TMP_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;/server.crt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;KEY&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$TMP_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;/server.key&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tailscale cert &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    --cert-file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$CERT&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    --key-file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$KEY&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$HOSTNAME&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Make sure we actually received a valid certificate.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openssl x509 &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -in &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$CERT&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -noout &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -subject &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -issuer &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -dates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Replace the existing certificate and key.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$CERT&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$CERT_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;/server.crt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$KEY&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$CERT_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;/server.key&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm -rf &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$TMP_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Restart the KVM&amp;#39;s nginx instance.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/etc/init.d/S99kvmd-nginx restart&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Tailscale certificate renewed and nginx restarted.&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Make the script executable.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chmod &lt;span style=&#34;color:#ae81ff&#34;&gt;700&lt;/span&gt; /etc/tailscale-cert-renew.sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Run the script once to test it.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/etc/tailscale-cert-renew.sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should be able to reload the GLiNet web interface and see that you have a valid LetsEncrypt certificate for your Tailscale MagicDNS name.&lt;/p&gt;&#xA; &lt;div class=&#34;callout callout-aside&#34; role=&#34;note&#34;&gt;&lt;div class=&#34;callout-title&#34;&gt;Aside&lt;/div&gt;&lt;div class=&#34;callout-content&#34;&gt;Make sure you&amp;rsquo;re accessing the device at &lt;code&gt;your-kvm.yourtailnet.ts.net&lt;/code&gt; and not it&amp;rsquo;s IP address, or the certificate won&amp;rsquo;t validate.&lt;/div&gt;&lt;/div&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Now, lets create a cron job to run the script automatically. The GliNet KVMs already run a cron daemon (at least the ones that I&amp;rsquo;ve used do) - so you should be able to directly edit the crontab.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;crontab -e&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To run the script once a day at 0300:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0 3 * * * /etc/tailscale-cert-renew.sh&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;You should now have a GliNet KVM that automatically renews your Tailscale certificates when needed. :)&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Routing to Tailscale clients from a local network with OPNSense</title>
      <link>https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/</link>
      <pubDate>Sat, 15 Aug 2026 16:45:12 -0400</pubDate>
      <guid>https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/</guid>
      <description>&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;&#xA;&lt;p&gt;Effectively, what we&amp;rsquo;re trying to accomplish here today is the opposite of a Tailscale &lt;a href=&#34;https://tailscale.com/docs/features/subnet-routers&#34;&gt;Subnet Router&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;As in, allowing clients on our local network to talk to devices via their &lt;a href=&#34;https://tailscale.com/docs/concepts/tailscale-ip-addresses&#34;&gt;Tailscale subnet IP addresses&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;When I started looking into how to do this, I could not find a complete guide on the topic.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ll do my best to do this start to finish so that anyone else can follow along with me.&lt;/p&gt;&#xA;&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;You already have an OPNSense gateway setup and enabled as a Subnet Router on your Tailnet.&lt;/li&gt;&#xA;&lt;li&gt;You&amp;rsquo;ll also need to have approved the subnets that you told the Subnet Router to advertise in your admin console.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;recommendations&#34;&gt;Recommendations&lt;/h2&gt;&#xA;&lt;p&gt;Create an alias for the Tailscale subnet range so you can avoid typing it in 10x times.&lt;/p&gt;&#xA;&lt;p&gt;Firewall &amp;gt; Aliases&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Showing alias for tailscale subnet&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/images/tailscale-alias.png&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;explanation&#34;&gt;Explanation&lt;/h2&gt;&#xA;&lt;p&gt;Tailscale Subnet Routers by default use SNAT (Source NAT).&lt;/p&gt;&#xA;&lt;p&gt;This means that packets sent from your Tailscale devices &lt;em&gt;to&lt;/em&gt; the Subnet Router will go through Network Address Translation on their way to the local subnets you&amp;rsquo;ve advertised.&lt;/p&gt;&#xA;&lt;p&gt;So, any devices &lt;em&gt;inside&lt;/em&gt; your LAN by default will only ever see the IP address of your OPNSense gateway - they will not see the IP addresses of the individual Tailscale clients. This is the first thing that we will need to disable.&lt;/p&gt;&#xA;&lt;p&gt;Once we have this disabled, we&amp;rsquo;ll need to configure both a static route as well as firewall rules to allow the traffic to pass.&lt;/p&gt;&#xA;&lt;h2 id=&#34;step-by-step&#34;&gt;Step by step&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Log into your OPNSense router and navigate to VPN &amp;gt; Tailscale &amp;gt; Settings. You&amp;rsquo;ll need to toggle on &amp;ldquo;Advanced Mode&amp;rdquo;. Check the box for &amp;ldquo;Disable SNAT&amp;rdquo;.&#xA;&lt;img alt=&#34;Image showing how to disable SNAT in Tailscale&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/images/tailscale-disable-snat.png&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Now, what we need to do is create a static route - but before we can do that, we need to take care of some other items:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Add the Tailscale interface by going to Interfaces &amp;gt; Assignments. Click on the + button and add the Tailscale interface from the dropdown. Don&amp;rsquo;t forget to enable the interface - by default it will be added in a disabled state.&#xA;&lt;img alt=&#34;Tailscale interface in OPNSense&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/images/tailscale-interface.png&#34;&gt;&#xA;&lt;img alt=&#34;Tailscale interface configuration&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/images/tailscale-interface-2.png&#34;&gt;&lt;/li&gt;&#xA;&lt;li&gt;Create a new gateway - System &amp;gt; Gateways &amp;gt; Configuration. The interface should be &amp;ldquo;Tailscale&amp;rdquo;. Gateway name is technically arbitrary, but for the sake of simplicity I&amp;rsquo;ve also called it &amp;ldquo;Tailscale&amp;rdquo;. Make sure you disable gateway monitoring.&#xA;&lt;img alt=&#34;Tailscale Gateway setup&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/images/tailscale-gateway.png&#34;&gt;&lt;/li&gt;&#xA;&lt;li&gt;Now that we&amp;rsquo;ve done this, we can finally define our static route (System &amp;gt; Routes &amp;gt; Configuration). The Tailscale subnet is 100.64.0.0/10:&#xA;&lt;img alt=&#34;Tailscale static route configuration page&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/images/tailscale-route.png&#34;&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Now that we have the routing taken care of, we need to create some firewall rules to allow the traffic to pass. You&amp;rsquo;ll need to create at minimum 2 rules on the OPNSense side (Firewall &amp;gt; Rules):&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;On your LAN interface, define a rule that allows communication from your LAN to your Tailnet. For example -&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Interface: LAN (Or whatever yours is called)&#xA;Action: Pass&#xA;Source: LAN network&#xA;Destination: 100.64.0.0/10&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&#xA;&lt;li&gt;On your Tailscale interface, define the inverse:&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Interface: Tailscale&#xA;Action: Pass&#xA;Source: 100.64.0.0/10&#xA;Destination: LAN network&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Rule 1 may not be needed if you have the default &amp;ldquo;Allow All&amp;rdquo; rule in your configuration on the LAN interface. I do not, so I needed to allow that traffic.&lt;/p&gt;&#xA;&lt;p&gt;Feel free to restrict this traffic however you would like. Personally, I only allow through a few kinds of traffic - DNS, HTTP/HTTPS, and ICMP echo-request / echo-reply.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;You&amp;rsquo;ll also need to go into your &lt;a href=&#34;https://console.tailscale.com/admin/acls/visual/general-access-rules&#34;&gt;Tailscale ACL controls&lt;/a&gt; and allow access there from your local subnets.&#xA;&lt;img alt=&#34;Tailscale ACL screenshot&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/images/tailscale-acl.png&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Any device that you wish to reach via this connection needs to have the &lt;code&gt;--accept-routes&lt;/code&gt; option enabled when you run &lt;code&gt;tailscale up&lt;/code&gt;. The device you&amp;rsquo;re trying to access needs to have a route to get &lt;em&gt;back&lt;/em&gt; to your LAN subnet, after all.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;--accept-routes&lt;/code&gt; is enabled by default on MacOS and Windows, but disabled by default on Linux. Once enabled, the device should be reachable from any device on your LAN.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;You may also want to configure MSS clamping to avoid unnecessary IP fragmentation. Under Firewall &amp;gt; Settings &amp;gt; Normalization, click the + to add a new rule:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Interface: Select your assigned Tailscale interface.&#xA;Direction: Any&#xA;Max MSS: 1240 (for standard Tailscale 1280 MTU) or 1380 (if your Tailnet uses 1420 MTU).&#xA;Description: Clamp MSS for Tailscale traffic.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;I highly recommend thinking carefully about your firewall rules and Tailscale ACLs, and what you allow to connect to what.&lt;/p&gt;&#xA;&lt;p&gt;Now that I&amp;rsquo;ve gotten this working I&amp;rsquo;ll be spending some time narrowing my LAN to Tailnet access ACL down to specific tags. But that&amp;rsquo;s a whole other post.&lt;/p&gt;&#xA;&lt;h2 id=&#34;exit-node&#34;&gt;Exit Node&lt;/h2&gt;&#xA;&lt;p&gt;If, like me, you are also using your home OPNSense gateway as a Tailscale &lt;a href=&#34;https://tailscale.com/docs/features/exit-nodes&#34;&gt;Exit Node&lt;/a&gt;, you&amp;rsquo;ll quickly realize that this process broke it.&lt;/p&gt;&#xA;&lt;p&gt;The fix is simple, we need to add an Outbound NAT rule to replace the missing SNAT on the Tailscale service.&lt;/p&gt;&#xA;&lt;p&gt;Go to Firewall &amp;gt; NAT &amp;gt; Outbound.&lt;/p&gt;&#xA;&lt;p&gt;Your Outbound NAT mode must be set to &amp;ldquo;Hybrid&amp;rdquo; or &amp;ldquo;Manual&amp;rdquo;. If you don&amp;rsquo;t know what this means, set it to &amp;ldquo;Hybrid&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Configure a rule that looks like this:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Interface: WAN&#xA;Source address: 100.64.0.0/10&#xA;Destination address: any&#xA;Translation target: Interface address&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img alt=&#34;Example of outbound NAT rule&#34; loading=&#34;lazy&#34; src=&#34;https://blog.ssb-tech.net/posts/opnsense-lan-to-tailscale-connectivity/images/tailscale-nat.png&#34;&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Fluxer Over Tailscale</title>
      <link>https://blog.ssb-tech.net/posts/fluxer-over-tailscale/</link>
      <pubDate>Sat, 20 Jun 2026 21:36:21 -0400</pubDate>
      <guid>https://blog.ssb-tech.net/posts/fluxer-over-tailscale/</guid>
      <description>&lt;p&gt;This weekend I&amp;rsquo;ve been playing with setting up a self-hosted &lt;a href=&#34;https://fluxer.app&#34;&gt;Fluxer&lt;/a&gt; instance.&lt;/p&gt;&#xA;&lt;p&gt;In my traditional fashion, I made things much harder by myself by refusing to use their provided Caddy setup, and instead using Traefik.&lt;/p&gt;&#xA;&lt;p&gt;And for an additional challenge - I wanted this to be completely inaccessible from the public Internet, instead using my Tailscale mesh network.&lt;/p&gt;&#xA;&lt;p&gt;The setup required some heavy modifications to the Docker Compose file provided by Fluxer&amp;rsquo;s team, and a few tweaks to &lt;code&gt;livekit.yaml&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;callout callout-note&#34; role=&#34;note&#34;&gt;&lt;div class=&#34;callout-title&#34;&gt;Note&lt;/div&gt;&lt;div class=&#34;callout-content&#34;&gt;The Docker Compose file for this service is extremely long, so instead of posting it inline here as I normally do, I have created this &lt;a href=&#34;https://gist.github.com/BladeWDR/1b062af5eede7e0c46a3446d46b4f889&#34;&gt;Gist&lt;/a&gt; containing both the modified Compose project and &lt;code&gt;livekit.yaml&lt;/code&gt;.&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;With this setup, I&amp;rsquo;m able to access Fluxer at &lt;code&gt;fluxer.mytailnet.ts.net&lt;/code&gt; with full TLS encryption and valid certificates.&lt;/p&gt;&#xA;&lt;p&gt;I am using:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A Tailscale sidecar container&lt;/li&gt;&#xA;&lt;li&gt;The Tailscale Traefik provider for TLS certificates&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Effectively, what you need to do is tell Traefik and the Livekit service to share the Tailscale container&amp;rsquo;s network namespace.&lt;/p&gt;&#xA;&lt;p&gt;Then we tell Livekit to advertise the Tailscale container&amp;rsquo;s IP address instead of the one for the Docker bridge (see &lt;code&gt;livekit.yaml&lt;/code&gt;, it&amp;rsquo;s the &lt;code&gt;node_ip&lt;/code&gt; setting).&lt;/p&gt;&#xA;&lt;p&gt;Note - you&amp;rsquo;ll need to add the &lt;code&gt;TS_SOCKET: /var/run/tailscale/tailscaled.sock&lt;/code&gt; environment variable to the Tailscale container, and pass the Tailscale socket through to the Traefik container - it needs access to the Tailscale daemon to be able to grab your certificates.&lt;/p&gt;&#xA;&lt;p&gt;You&amp;rsquo;ll also need to have &lt;a href=&#34;https://tailscale.com/docs/how-to/set-up-https-certificates&#34;&gt;enabled HTTPS on your Tailnet&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;What works&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Text chat&lt;/li&gt;&#xA;&lt;li&gt;Voice chat&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;What doesn&amp;rsquo;t work (yet)&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Video chat - I think this has something to do with Livekit video codecs. I need to play a bit more with this and do some more testing.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
  </channel>
</rss>
