<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anas Rhimi</title>
    <description>The latest articles on DEV Community by Anas Rhimi (@mealiclay01).</description>
    <link>https://dev.to/mealiclay01</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4057766%2F426475cc-a0b1-44ca-9300-970c8249ac6a.jpg</url>
      <title>DEV Community: Anas Rhimi</title>
      <link>https://dev.to/mealiclay01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mealiclay01"/>
    <language>en</language>
    <item>
      <title>How to Deploy a Secure Hub-and-Spoke Cluster Across Proxmox LXC</title>
      <dc:creator>Anas Rhimi</dc:creator>
      <pubDate>Sat, 01 Aug 2026 10:19:41 +0000</pubDate>
      <link>https://dev.to/mealiclay01/how-to-deploy-a-secure-hub-and-spoke-cluster-across-proxmox-lxc-45p1</link>
      <guid>https://dev.to/mealiclay01/how-to-deploy-a-secure-hub-and-spoke-cluster-across-proxmox-lxc-45p1</guid>
      <description>&lt;p&gt;If you are running multiple self-hosted services on a single Proxmox node, leaving them on a default flat network (&lt;code&gt;vmbr0&lt;/code&gt;) is a massive security vulnerability. If one container is compromised, the attacker gains lateral movement across your entire infrastructure.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Hub-and-Spoke&lt;/strong&gt; architecture solves this. The "Hub" container acts as the strict central router and reverse proxy. The "Spoke" containers hold your actual applications. Spokes cannot talk to the physical network directly—all traffic must pass through the Hub, where it is inspected, filtered, and logged.&lt;/p&gt;

&lt;p&gt;Here is a high-level look at the architecture we are building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Internet / Physical LAN ]
    │
    ▼ (eth0: 192.168.1.50)
┌───────────────────────────────┐
│       HUB (hub-router)        │  &amp;lt;-- HAProxy + CrowdSec
└───────────────────────────────┘
    │ (eth1: 10.0.0.1)
    ▼ [ Internal vmbr1 Bridge ]
   ┌───────────┴───────────┐
   ▼                       ▼
┌──────────────┐    ┌──────────────┐
│ Spoke 1      │    │ Spoke 2      │
│ (10.0.0.10)  │    │ (10.0.0.11)  │
└──────────────┘    └──────────────┘

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By combining this architecture with &lt;strong&gt;Proxmox LXC (Linux Containers)&lt;/strong&gt; and &lt;strong&gt;CrowdSec&lt;/strong&gt;, you get enterprise-grade isolation with virtually zero performance overhead.&lt;/p&gt;

&lt;p&gt;Here is exactly how to set it up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Proxmox VE node.&lt;/li&gt;
&lt;li&gt;Basic understanding of Linux networking.&lt;/li&gt;
&lt;li&gt;SSH or Console access to your Proxmox host.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create the Isolated Spoke Network
&lt;/h2&gt;

&lt;p&gt;By default, Proxmox uses &lt;code&gt;vmbr0&lt;/code&gt; to bridge directly to your physical network. We need a second, internal-only bridge for the Spokes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Proxmox Web GUI, go to &lt;strong&gt;Node -&amp;gt; System -&amp;gt; Network&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create -&amp;gt; Linux Bridge&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name it &lt;code&gt;vmbr1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Leave the &lt;strong&gt;IPv4/CIDR&lt;/strong&gt; and &lt;strong&gt;Bridge ports&lt;/strong&gt; completely blank. (This ensures it has no direct physical internet access).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;, then click &lt;strong&gt;Apply Configuration&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Provision the Hub Container
&lt;/h2&gt;

&lt;p&gt;The Hub needs to talk to both the outside world (&lt;code&gt;vmbr0&lt;/code&gt;) and the internal isolated network (&lt;code&gt;vmbr1&lt;/code&gt;).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download a lightweight Debian or Ubuntu template (&lt;code&gt;local -&amp;gt; CT Templates -&amp;gt; Templates&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Create a new LXC container (CT ID: &lt;code&gt;100&lt;/code&gt;, Hostname: &lt;code&gt;hub-router&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Under the &lt;strong&gt;Network&lt;/strong&gt; tab, attach it to &lt;code&gt;vmbr0&lt;/code&gt; (your public network). Assign it a static IP (e.g., &lt;code&gt;192.168.1.50/24&lt;/code&gt;) and your standard gateway.&lt;/li&gt;
&lt;li&gt;Finish creating the container, but do not start it yet.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;hub-router&lt;/code&gt; container, go to &lt;strong&gt;Network&lt;/strong&gt;, and click &lt;strong&gt;Add&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add a second interface (&lt;code&gt;eth1&lt;/code&gt;), attach it to the new &lt;code&gt;vmbr1&lt;/code&gt; bridge, and assign an internal IP (e.g., &lt;code&gt;10.0.0.1/24&lt;/code&gt;). Leave the gateway blank.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start the Hub container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Provision the Spoke Containers
&lt;/h2&gt;

&lt;p&gt;Your Spokes will only live on the isolated internal network.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new LXC container for your application (CT ID: &lt;code&gt;101&lt;/code&gt;, Hostname: &lt;code&gt;spoke-app-1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Under the &lt;strong&gt;Network&lt;/strong&gt; tab, attach it &lt;em&gt;only&lt;/em&gt; to &lt;code&gt;vmbr1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Assign it an internal static IP: &lt;code&gt;10.0.0.10/24&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set its Gateway to the Hub's internal IP: &lt;code&gt;10.0.0.1&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you try to ping the internet from &lt;code&gt;spoke-app-1&lt;/code&gt; right now, it will fail. This is exactly what we want. It is completely isolated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Configure the Hub as a NAT Router
&lt;/h2&gt;

&lt;p&gt;To allow the Spokes to download software updates, the Hub must translate their internal traffic.&lt;/p&gt;

&lt;p&gt;Log into the &lt;code&gt;hub-router&lt;/code&gt; container console and enable IP forwarding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"net.ipv4.ip_forward=1"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/sysctl.conf
sysctl &lt;span class="nt"&gt;-p&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, set up &lt;code&gt;iptables&lt;/code&gt; to route the traffic from the &lt;code&gt;10.0.0.0/24&lt;/code&gt; subnet out through &lt;code&gt;eth0&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;iptables-persistent &lt;span class="nt"&gt;-y&lt;/span&gt;
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; POSTROUTING &lt;span class="nt"&gt;-s&lt;/span&gt; 10.0.0.0/24 &lt;span class="nt"&gt;-o&lt;/span&gt; eth0 &lt;span class="nt"&gt;-j&lt;/span&gt; MASQUERADE
iptables-save &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/iptables/rules.v4

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your Spokes can securely reach the internet to pull packages, but the internet cannot directly reach them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Secure Ingress with CrowdSec &amp;amp; HAProxy
&lt;/h2&gt;

&lt;p&gt;To route incoming web traffic securely to your Spokes, install HAProxy and CrowdSec on the Hub container.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install HAProxy:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;haproxy &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install the CrowdSec agent (which analyzes logs for attacks) and the HAProxy Bouncer (which drops malicious IPs):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;https://install.crowdsec.net]&lt;span class="o"&gt;(&lt;/span&gt;https://install.crowdsec.net&lt;span class="o"&gt;)&lt;/span&gt; | sh
apt &lt;span class="nb"&gt;install &lt;/span&gt;crowdsec crowdsec-custom-bouncer-haproxy &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CrowdSec will automatically detect HAProxy and start monitoring its logs for Layer 7 attacks (like brute-forcing or SQL injection). If a malicious IP is detected, the Bouncer instantly blocks it at the Hub layer, ensuring the bad traffic never even touches your internal Spoke containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;You now have a production-ready cluster architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Lateral Movement:&lt;/strong&gt; A compromised Spoke cannot scan your physical home/office network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized Security:&lt;/strong&gt; You only need to manage SSL certificates, firewall rules, and reverse proxies on the Hub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Defense:&lt;/strong&gt; CrowdSec blocks known bad actors before they reach your apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because this uses LXC instead of full KVM virtual machines, the overhead of this entire setup is less than 150MB of RAM.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>proxmox</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
