{"id":829,"date":"2025-12-26T17:53:24","date_gmt":"2025-12-26T22:53:24","guid":{"rendered":"https:\/\/carminebufano.com\/?p=829"},"modified":"2025-12-26T18:44:52","modified_gmt":"2025-12-26T23:44:52","slug":"how-to-get-real-public-ip-addresses-at-home-lab-wireguard-ip-transit-complete-guide","status":"publish","type":"post","link":"https:\/\/carminebufano.com\/index.php\/2025\/12\/26\/how-to-get-real-public-ip-addresses-at-home-lab-wireguard-ip-transit-complete-guide\/","title":{"rendered":"How to Get Real Public IP Addresses at your Home Lab: WireGuard IP Transit Complete Guide"},"content":{"rendered":"\n<p>A Complete Guide to why <a href=\"https:\/\/www.coretransit.net\/\">Core Transit&#8217;s<\/a> WireGuard IP Transit Service is so incredibly awesome for you lab <\/p>\n\n\n\n<p><em>Bypassing CG-NAT with style &#8211; a detailed technical walkthrough<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Problem: CG-NAT Hell<\/h2>\n\n\n\n<p>If you&#8217;re reading this, you probably know the pain. Your ISP puts you behind Carrier-Grade NAT (CG-NAT), which means:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No port forwarding<\/li>\n\n\n\n<li>No hosting servers at home<\/li>\n\n\n\n<li>No running services accessible from the internet<\/li>\n\n\n\n<li>Dynamic, shared IP addresses<\/li>\n<\/ul>\n\n\n\n<p>I was stuck behind CG-NAT on my home internet connection and needed real, routable public IP addresses for my homelab. After researching options (VPS with reverse proxy, Cloudflare Tunnel, etc.), I discovered <strong>Core Transit<\/strong> &#8211; a service that provides actual public IPv4 and IPv6 blocks via WireGuard tunnel.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Solution: Core Transit<\/h2>\n\n\n\n<p><a href=\"https:\/\/coretransit.io\" target=\"_blank\" rel=\"noreferrer noopener\">Core Transit<\/a> offers WireGuard-based IP transit. You get:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A dedicated public IPv4 block (I got a \/28 = 14 usable IPs)<\/li>\n\n\n\n<li>A public IPv6 block (I got a \/60 = 16x \/64 subnets)<\/li>\n\n\n\n<li>Traffic routed through their infrastructure via encrypted WireGuard tunnel<\/li>\n\n\n\n<li>Works through any NAT, including CG-NAT<\/li>\n<\/ul>\n\n\n\n<p>This guide documents every step I took to get it working on a Linux gateway\/router.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Hardware<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A Linux server\/router with at least 2 network interfaces<\/li>\n\n\n\n<li>One interface for WAN (internet connectivity)<\/li>\n\n\n\n<li>One interface for your DMZ\/public-facing network<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Software<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Linux with kernel 5.6+ (WireGuard built-in) or wireguard-tools<\/li>\n\n\n\n<li>Open vSwitch (optional, for bridge management)<\/li>\n\n\n\n<li>nftables or iptables for firewall<\/li>\n\n\n\n<li>dnsmasq (optional, for DHCP on DMZ)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">From Core Transit<\/h3>\n\n\n\n<p>After signing up, you&#8217;ll receive:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Your allocated IPv4 block (e.g., <code>203.0.113.176\/28<\/code>)<\/li>\n\n\n\n<li>Your allocated IPv6 block (e.g., <code>2001:db8:abcd:1e0::\/60<\/code>)<\/li>\n\n\n\n<li>Tunnel endpoint IP and port<\/li>\n\n\n\n<li>Tunnel point-to-point addresses<\/li>\n\n\n\n<li>Their WireGuard public key<\/li>\n\n\n\n<li>Your WireGuard keypair (or generate your own)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Network Architecture<\/h2>\n\n\n\n<p>Here&#8217;s what we&#8217;re building:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>                              INTERNET\n                                  \u2502\n                    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                    \u2502      CORE TRANSIT POP     \u2502\n                    \u2502    (Your tunnel endpoint) \u2502\n                    \u2502                           \u2502\n                    \u2502  Routes to you:           \u2502\n                    \u2502  \u2022 YOUR_IPV4_BLOCK\/28     \u2502\n                    \u2502  \u2022 YOUR_IPV6_BLOCK\/60     \u2502\n                    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                                  \u2502\n                         WireGuard Tunnel\n                        (ChaCha20-Poly1305)\n                                  \u2502\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n           YOUR NETWORK           \u2502\n                                  \u2502\n                    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                    \u2502      YOUR ISP GATEWAY     \u2502\n                    \u2502    (NAT \/ CG-NAT \/ etc)   \u2502\n                    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                                  \u2502\n            \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n            \u2502              YOUR LINUX GATEWAY           \u2502\n            \u2502                                           \u2502\n            \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510                            \u2502\n            \u2502  \u2502  eth-wan  \u2502 WAN interface (DHCP)       \u2502\n            \u2502  \u2502           \u2502 Default route to ISP       \u2502\n            \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2518                            \u2502\n            \u2502        \u2502                                  \u2502\n            \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2510     \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510   \u2502\n            \u2502  \u2502wg-transit \u2502     \u2502     br-dmz       \u2502   \u2502\n            \u2502  \u2502           \u2502     \u2502  (OVS Bridge)    \u2502   \u2502\n            \u2502  \u2502Tunnel IPs \u2502     \u2502                  \u2502   \u2502\n            \u2502  \u2502           \u2502     \u2502 Public Gateway   \u2502   \u2502\n            \u2502  \u2502           \u2502     \u2502 YOUR_BLOCK.177   \u2502   \u2502\n            \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518     \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518   \u2502\n            \u2502                             \u2502             \u2502\n            \u2502         Policy Routing      \u2502             \u2502\n            \u2502         (Table 100)         \u2502             \u2502\n            \u2502                   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518             \u2502\n            \u2502                   \u2502                       \u2502\n            \u2502              \u250c\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510                  \u2502\n            \u2502              \u2502 eth-dmz \u2502 Physical port    \u2502\n            \u2502              \u2514\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2518                  \u2502\n            \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2502\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                                \u2502\n                           DMZ Network\n                                \u2502\n                    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                    \u2502                       \u2502\n             \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2510         \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n             \u2502   Server    \u2502         \u2502   Server    \u2502\n             \u2502  .178       \u2502         \u2502  .179       \u2502\n             \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518         \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Key Concepts<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>WireGuard Tunnel<\/strong>: Encrypted connection to Core Transit carrying your public IP traffic<\/li>\n\n\n\n<li><strong>Policy Routing<\/strong>: Traffic FROM your public IPs routes through the tunnel, not your normal ISP<\/li>\n\n\n\n<li><strong>DMZ Bridge<\/strong>: Internal network where you assign public IPs to servers<\/li>\n\n\n\n<li><strong>Firewall<\/strong>: Protect your gateway from the public internet<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Install WireGuard Tools<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Ubuntu\/Debian<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>apt update\napt install wireguard-tools\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">RHEL\/CentOS\/Rocky<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>dnf install wireguard-tools\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Verify Installation<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Check if WireGuard kernel module is available\nmodprobe wireguard\nlsmod | grep wireguard\n\n# Check wg command exists\nwhich wg\nwg --version\n<\/code><\/pre>\n\n\n\n<p><strong>What this does:<\/strong> Installs the userspace tools (<code>wg<\/code>, <code>wg-quick<\/code>) needed to configure WireGuard interfaces. Modern kernels (5.6+) have WireGuard built-in; older kernels may need the DKMS module.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Generate WireGuard Keys (If Not Provided)<\/h2>\n\n\n\n<p>If Core Transit didn&#8217;t generate keys for you:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Generate private key\nwg genkey &amp;gt; \/etc\/wireguard\/private.key\nchmod 600 \/etc\/wireguard\/private.key\n\n# Derive public key from private key\ncat \/etc\/wireguard\/private.key | wg pubkey &amp;gt; \/etc\/wireguard\/public.key\n\n# View your public key (send this to Core Transit)\ncat \/etc\/wireguard\/public.key\n<\/code><\/pre>\n\n\n\n<p><strong>What this does:<\/strong> Creates a cryptographic keypair. The private key stays on your server (never share it!). The public key gets registered with Core Transit so they can authenticate your tunnel.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Create WireGuard Configuration<\/h2>\n\n\n\n<p>Create the configuration file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nano \/etc\/wireguard\/wg-coretransit.conf\n<\/code><\/pre>\n\n\n\n<p>Paste the following (replace placeholders with your actual values):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Core Transit WireGuard Tunnel Configuration\n# Replace all YOUR_* values with actual values from Core Transit\n\n&#91;Interface]\n# Your private key (keep this secret!)\nPrivateKey = YOUR_PRIVATE_KEY_HERE\n\n# Tunnel point-to-point addresses (provided by Core Transit)\n# These are the IPs assigned to YOUR end of the tunnel\nAddress = YOUR_TUNNEL_IPV4\/31, YOUR_TUNNEL_IPV6\/64\n\n# MTU - Start with 1420, we may need to lower this later\nMTU = 1420\n\n# Use a separate routing table for policy routing\nTable = 100\n\n# Policy routing rules - traffic FROM your public block uses table 100\nPostUp = ip rule add from YOUR_IPV4_BLOCK\/28 table 100 priority 5\nPostUp = ip -6 rule add from YOUR_IPV6_BLOCK\/60 table 100 priority 5\n\nPostDown = ip rule del from YOUR_IPV4_BLOCK\/28 table 100 priority 5\nPostDown = ip -6 rule del from YOUR_IPV6_BLOCK\/60 table 100 priority 5\n\n&#91;Peer]\n# Core Transit's public key (provided by them)\nPublicKey = CORETRANSIT_PUBLIC_KEY_HERE\n\n# Core Transit endpoint (IP:port provided by them)\nEndpoint = CORETRANSIT_ENDPOINT_IP:PORT\n\n# Route ALL traffic through tunnel (for table 100 only)\nAllowedIPs = 0.0.0.0\/0, ::\/0\n\n# Keep tunnel alive through NAT\nPersistentKeepalive = 25\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Example with Placeholder Values<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Interface]\nPrivateKey = aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890abcdefg=\nAddress = 203.0.113.253\/31, 2001:db8:1000:23::2\/64\nMTU = 1420\nTable = 100\n\nPostUp = ip rule add from 203.0.113.176\/28 table 100 priority 5\nPostUp = ip -6 rule add from 2001:db8:abcd:1e0::\/60 table 100 priority 5\nPostDown = ip rule del from 203.0.113.176\/28 table 100 priority 5\nPostDown = ip -6 rule del from 2001:db8:abcd:1e0::\/60 table 100 priority 5\n\n&#91;Peer]\nPublicKey = XyZ0987654321AbCdEfGhIjKlMnOpQrStUvWxYz1234=\nEndpoint = 198.51.100.1:10305\nAllowedIPs = 0.0.0.0\/0, ::\/0\nPersistentKeepalive = 25\n<\/code><\/pre>\n\n\n\n<p><strong>What this does:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>PrivateKey<\/code>: Authenticates your side of the tunnel<\/li>\n\n\n\n<li><code>Address<\/code>: IPs assigned to the tunnel interface itself (point-to-point link)<\/li>\n\n\n\n<li><code>MTU<\/code>: Maximum packet size (more on this later)<\/li>\n\n\n\n<li><code>Table = 100<\/code>: Routes learned from this tunnel go into routing table 100, not main<\/li>\n\n\n\n<li><code>PostUp\/PostDown<\/code>: Policy routing rules that send traffic FROM your public IPs through the tunnel<\/li>\n\n\n\n<li><code>PublicKey<\/code>: Core Transit&#8217;s identity<\/li>\n\n\n\n<li><code>Endpoint<\/code>: Where to send encrypted packets<\/li>\n\n\n\n<li><code>AllowedIPs = 0.0.0.0\/0, ::\/0<\/code>: Accept any destination (full tunnel for table 100)<\/li>\n\n\n\n<li><code>PersistentKeepalive<\/code>: Keeps NAT mappings alive<\/li>\n<\/ul>\n\n\n\n<p>Set proper permissions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chmod 600 \/etc\/wireguard\/wg-coretransit.conf\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Enable IP Forwarding<\/h2>\n\n\n\n<p>Your gateway needs to forward packets between interfaces:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Enable immediately\nsysctl -w net.ipv4.ip_forward=1\nsysctl -w net.ipv6.conf.all.forwarding=1\n\n# Make persistent across reboots\necho \"net.ipv4.ip_forward=1\" &amp;gt;&amp;gt; \/etc\/sysctl.conf\necho \"net.ipv6.conf.all.forwarding=1\" &amp;gt;&amp;gt; \/etc\/sysctl.conf\n<\/code><\/pre>\n\n\n\n<p><strong>What this does:<\/strong> Allows your Linux box to act as a router, forwarding packets between the tunnel interface, DMZ interface, and WAN interface.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Bring Up the WireGuard Tunnel<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code># Start the tunnel\nwg-quick up wg-coretransit\n\n# Verify it's running\nwg show wg-coretransit\n<\/code><\/pre>\n\n\n\n<p>Expected output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>interface: wg-coretransit\n  public key: YOUR_PUBLIC_KEY\n  private key: (hidden)\n  listening port: 51820\n\npeer: CORETRANSIT_PUBLIC_KEY\n  endpoint: 198.51.100.1:10305\n  allowed ips: 0.0.0.0\/0, ::\/0\n  latest handshake: 5 seconds ago    &amp;lt;-- THIS IS THE KEY LINE\n  transfer: 1.23 MiB received, 456.78 KiB sent\n  persistent keepalive: every 25 seconds\n<\/code><\/pre>\n\n\n\n<p><strong>Success indicator:<\/strong> You should see &#8220;latest handshake: X seconds ago&#8221;. If this never appears, the tunnel isn&#8217;t establishing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Test Tunnel Connectivity<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Ping Core Transit's tunnel IP (their side of the point-to-point link)\n# This IP is typically YOUR_TUNNEL_IP - 1 or as provided by Core Transit\nping YOUR_CORETRANSIT_TUNNEL_PEER_IP\n\n# Example:\nping 203.0.113.252\n<\/code><\/pre>\n\n\n\n<p>If this works, your tunnel is up!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6: Create the DMZ Bridge (Optional but Recommended)<\/h2>\n\n\n\n<p>Using Open vSwitch to create a bridge for your public-facing network:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Install OVS if not present\napt install openvswitch-switch\n\n# Create the bridge\novs-vsctl add-br br-dmz\n\n# Add your physical DMZ interface to the bridge\n# Replace eth-dmz with your actual interface name\novs-vsctl add-port br-dmz eth-dmz\n\n# Bring up interfaces\nip link set eth-dmz up\nip link set br-dmz up\n<\/code><\/pre>\n\n\n\n<p><strong>Alternative without OVS<\/strong> (using standard Linux bridge):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Create bridge\nip link add br-dmz type bridge\n\n# Add physical interface\nip link set eth-dmz master br-dmz\n\n# Bring up\nip link set eth-dmz up\nip link set br-dmz up\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 7: Assign Public IPs to the DMZ Bridge<\/h2>\n\n\n\n<p>The bridge interface becomes your gateway for the public IP block:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Assign the first usable IP as the gateway\n# For a \/28 block, .176 is network, .177 is first usable, .191 is broadcast\nip addr add YOUR_GATEWAY_IP\/28 dev br-dmz\nip addr add YOUR_IPV6_GATEWAY\/64 dev br-dmz\n\n# Example:\nip addr add 203.0.113.177\/28 dev br-dmz\nip addr add 2001:db8:abcd:1e0::1\/64 dev br-dmz\n<\/code><\/pre>\n\n\n\n<p><strong>Important:<\/strong> Set the MTU to match your WireGuard tunnel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip link set br-dmz mtu 1420\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 8: Configure Policy Routing<\/h2>\n\n\n\n<p>This is the magic that makes it work. Traffic FROM your public IPs must route through the tunnel, not your regular ISP.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Verify Policy Routing Rules<\/h3>\n\n\n\n<p>The WireGuard PostUp commands should have created these:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip rule show\n<\/code><\/pre>\n\n\n\n<p>Expected output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0:      from all lookup local\n5:      from 203.0.113.176\/28 lookup 100       &amp;lt;-- Your IPv4 block\n10:     from all lookup main\n...\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>ip -6 rule show\n<\/code><\/pre>\n\n\n\n<p>Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0:      from all lookup local\n5:      from 2001:db8:abcd:1e0::\/60 lookup 100  &amp;lt;-- Your IPv6 block\n...\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Add Local Routes to Table 100<\/h3>\n\n\n\n<p>Traffic within your public block should stay local, not go through the tunnel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Local DMZ network (so hosts can talk to each other)\nip route add YOUR_IPV4_BLOCK\/28 dev br-dmz table 100\nip -6 route add YOUR_IPV6_BLOCK\/64 dev br-dmz table 100\n\n# Tunnel point-to-point network\nip route add YOUR_TUNNEL_SUBNET\/31 dev wg-coretransit table 100\n\n# Example:\nip route add 203.0.113.176\/28 dev br-dmz table 100\nip route add 203.0.113.252\/31 dev wg-coretransit table 100\nip -6 route add 2001:db8:abcd:1e0::\/64 dev br-dmz table 100\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Verify Routing Table 100<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>ip route show table 100\n<\/code><\/pre>\n\n\n\n<p>Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>default dev wg-coretransit scope link           &amp;lt;-- All other traffic via tunnel\n203.0.113.176\/28 dev br-dmz scope link          &amp;lt;-- Local DMZ stays local\n203.0.113.252\/31 dev wg-coretransit scope link  &amp;lt;-- Tunnel network\n<\/code><\/pre>\n\n\n\n<p><strong>What this does:<\/strong> When a packet has a source IP in your public block (203.0.113.176\/28), the kernel consults table 100 instead of the main routing table. Table 100 sends everything through the WireGuard tunnel, except local DMZ traffic.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 9: Test Public IP Routing<\/h2>\n\n\n\n<p>This is the moment of truth:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Test outbound - does traffic from your public IP reach the internet?\nping -I YOUR_GATEWAY_IP 8.8.8.8\nping -I 203.0.113.177 8.8.8.8\n\n# Test IPv6\nping6 -I YOUR_IPV6_GATEWAY 2001:4860:4860::8888\nping6 -I 2001:db8:abcd:1e0::1 2001:4860:4860::8888\n\n# Verify your public IP is what you expect\ncurl -4 --interface YOUR_GATEWAY_IP ifconfig.me\ncurl -4 --interface 203.0.113.177 ifconfig.me\n# Should return: 203.0.113.177 (or similar from your block)\n<\/code><\/pre>\n\n\n\n<p>If this works, <strong>congratulations!<\/strong> Your public IPs are routing through Core Transit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 10: The MTU Problem (Critical!)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Symptoms of MTU Issues<\/h3>\n\n\n\n<p>After initial setup, you might experience:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pings work, but web pages hang or load very slowly<\/li>\n\n\n\n<li>SSH connects but freezes when displaying large output<\/li>\n\n\n\n<li>Small transfers work, large transfers stall<\/li>\n\n\n\n<li>HTTPS sites fail while HTTP works<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">The Cause<\/h3>\n\n\n\n<p>WireGuard adds ~60 bytes of overhead to each packet. If your underlying path has a reduced MTU (common with CG-NAT, PPPoE, mobile networks), large packets get silently dropped.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Diagnose the MTU<\/h3>\n\n\n\n<p>Find the maximum working packet size:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Start with a reasonable size and decrease until it works\n# The -M do flag prevents fragmentation\n\nping -c 1 -M do -s 1400 YOUR_CORETRANSIT_TUNNEL_PEER_IP  # Probably fails\nping -c 1 -M do -s 1350 YOUR_CORETRANSIT_TUNNEL_PEER_IP  # Maybe works\nping -c 1 -M do -s 1300 YOUR_CORETRANSIT_TUNNEL_PEER_IP  # Should work\n<\/code><\/pre>\n\n\n\n<p>Example diagnosis session:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ ping -c 1 -M do -s 1400 203.0.113.252\nPING 203.0.113.252 (203.0.113.252) 1400(1428) bytes of data.\n--- 203.0.113.252 ping statistics ---\n1 packets transmitted, 0 received, 100% packet loss   # &amp;lt;-- TOO BIG\n\n$ ping -c 1 -M do -s 1300 203.0.113.252\nPING 203.0.113.252 (203.0.113.252) 1300(1328) bytes of data.\n1328 bytes from 203.0.113.252: icmp_seq=1 ttl=64 time=25.3 ms  # &amp;lt;-- WORKS\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Calculate Correct MTU<\/h3>\n\n\n\n<p>The WireGuard MTU should be:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WG_MTU = Maximum_Working_Payload + 28 - 60\n<\/code><\/pre>\n\n\n\n<p>Where:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Maximum_Working_Payload = largest <code>-s<\/code> value that works<\/li>\n\n\n\n<li>+28 = IP header (20) + ICMP header (8) that ping adds<\/li>\n\n\n\n<li>-60 = WireGuard overhead<\/li>\n<\/ul>\n\n\n\n<p>Example: If <code>-s 1300<\/code> works:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Working packet = 1300 + 28 = 1328 bytes<\/li>\n\n\n\n<li>WireGuard MTU = 1328 &#8211; 60 = 1268 \u2192 round to <strong>1280<\/strong> (safe) or <strong>1320<\/strong> (aggressive)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Apply MTU Fix<\/h3>\n\n\n\n<p>Edit your WireGuard config:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nano \/etc\/wireguard\/wg-coretransit.conf\n<\/code><\/pre>\n\n\n\n<p>Change:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MTU = 1420\n<\/code><\/pre>\n\n\n\n<p>To:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MTU = 1320\n<\/code><\/pre>\n\n\n\n<p>Restart the tunnel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wg-quick down wg-coretransit\nwg-quick up wg-coretransit\n<\/code><\/pre>\n\n\n\n<p><strong>Also update the DMZ bridge MTU:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip link set br-dmz mtu 1320\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Add MSS Clamping (Belt and Suspenders)<\/h3>\n\n\n\n<p>To prevent MTU issues with TCP, clamp the MSS (Maximum Segment Size):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Using nftables\nnft add table inet mangle\nnft add chain inet mangle FORWARD '{ type filter hook forward priority -150; policy accept; }'\nnft add rule inet mangle FORWARD oifname \"wg-coretransit\" tcp flags syn tcp option maxseg size set 1280\nnft add rule inet mangle FORWARD iifname \"wg-coretransit\" tcp flags syn tcp option maxseg size set 1280\n<\/code><\/pre>\n\n\n\n<p><strong>What this does:<\/strong> Forces TCP connections through the tunnel to negotiate a smaller maximum segment size, preventing fragmentation issues.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 11: Configure Firewall<\/h2>\n\n\n\n<p>Your gateway is now reachable from the public internet. <strong>Lock it down.<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Basic nftables Rules<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Block inbound connections from the tunnel to your gateway\nnft add rule inet filter input iifname \"wg-coretransit\" ct state new drop\n\n# Block inbound connections from DMZ to your gateway (except DHCP\/DNS)\nnft add rule inet filter input iifname \"br-dmz\" udp dport { 67, 68 } accept\nnft add rule inet filter input iifname \"br-dmz\" udp dport 53 accept\nnft add rule inet filter input iifname \"br-dmz\" icmp type echo-request accept\nnft add rule inet filter input iifname \"br-dmz\" ct state new drop\n\n# Prevent DMZ from reaching your private LAN\nnft add rule inet filter forward iifname \"br-dmz\" oifname \"eth-lan\" drop\nnft add rule inet filter forward iifname \"wg-coretransit\" oifname \"eth-lan\" drop\n<\/code><\/pre>\n\n\n\n<p><strong>What this does:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Blocks the public internet from accessing services on your gateway<\/li>\n\n\n\n<li>Allows DHCP and DNS from DMZ hosts<\/li>\n\n\n\n<li>Prevents DMZ hosts from accessing your private LAN<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 12: DHCP Server for DMZ (Optional)<\/h2>\n\n\n\n<p>If you want to dynamically assign public IPs to DMZ hosts:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Install dnsmasq\napt install dnsmasq\n\n# Create config\nnano \/etc\/dnsmasq.d\/br-dmz.conf\n<\/code><\/pre>\n\n\n\n<p>Content:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># DMZ DHCP Configuration\ninterface=br-dmz\nbind-dynamic\n\n# DHCP range - only offer a few IPs from your block\n# Reserve most for static assignment\ndhcp-range=YOUR_DHCP_START,YOUR_DHCP_END,255.255.255.240,12h\n\n# Gateway (your br-dmz IP)\ndhcp-option=br-dmz,3,YOUR_GATEWAY_IP\n\n# DNS servers\ndhcp-option=br-dmz,6,1.1.1.1,8.8.8.8\n\n# Example:\n# dhcp-range=203.0.113.189,203.0.113.190,255.255.255.240,12h\n# dhcp-option=br-dmz,3,203.0.113.177\n<\/code><\/pre>\n\n\n\n<p>Restart dnsmasq:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl restart dnsmasq\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 13: Make It Persistent (Survive Reboots)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Create Startup Script<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>nano \/usr\/local\/bin\/coretransit-startup.sh\n<\/code><\/pre>\n\n\n\n<p>Content:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# Core Transit Startup Script\n\nset -e\n\necho \"Starting Core Transit configuration...\"\n\n# 1. Enable IP forwarding\nsysctl -w net.ipv4.ip_forward=1\nsysctl -w net.ipv6.conf.all.forwarding=1\n\n# 2. Ensure DMZ bridge exists\nif ! ip link show br-dmz &amp;amp;&amp;gt;\/dev\/null; then\n    # For OVS:\n    ovs-vsctl add-br br-dmz\n    ovs-vsctl add-port br-dmz eth-dmz\n    # Or for standard bridge:\n    # ip link add br-dmz type bridge\n    # ip link set eth-dmz master br-dmz\nfi\n\n# 3. Bring up interfaces\nip link set eth-dmz up\nip link set br-dmz up\n\n# 4. Assign IPs to DMZ bridge\nip addr add YOUR_GATEWAY_IP\/28 dev br-dmz 2&amp;gt;\/dev\/null || true\nip addr add YOUR_IPV6_GATEWAY\/64 dev br-dmz 2&amp;gt;\/dev\/null || true\n\n# 5. Start WireGuard tunnel\nif ! ip link show wg-coretransit &amp;amp;&amp;gt;\/dev\/null; then\n    wg-quick up wg-coretransit\nfi\n\n# 6. Add local routes to table 100\nip route add YOUR_IPV4_BLOCK\/28 dev br-dmz table 100 2&amp;gt;\/dev\/null || true\nip route add YOUR_TUNNEL_SUBNET\/31 dev wg-coretransit table 100 2&amp;gt;\/dev\/null || true\nip -6 route add YOUR_IPV6_SUBNET\/64 dev br-dmz table 100 2&amp;gt;\/dev\/null || true\n\n# 7. Set MTU on DMZ bridge\nip link set br-dmz mtu 1320\n\n# 8. Apply firewall rules\nnft add rule inet filter input iifname \"wg-coretransit\" ct state new drop 2&amp;gt;\/dev\/null || true\nnft add rule inet filter input iifname \"br-dmz\" ct state new drop 2&amp;gt;\/dev\/null || true\n\n# 9. MSS clamping\nnft add table inet mangle 2&amp;gt;\/dev\/null || true\nnft add chain inet mangle FORWARD '{ type filter hook forward priority -150; policy accept; }' 2&amp;gt;\/dev\/null || true\nnft add rule inet mangle FORWARD oifname \"wg-coretransit\" tcp flags syn tcp option maxseg size set 1280 2&amp;gt;\/dev\/null || true\nnft add rule inet mangle FORWARD iifname \"wg-coretransit\" tcp flags syn tcp option maxseg size set 1280 2&amp;gt;\/dev\/null || true\n\n# 10. Verify tunnel\nif ping -c 1 -W 5 YOUR_CORETRANSIT_TUNNEL_PEER_IP &amp;gt; \/dev\/null 2&amp;gt;&amp;amp;1; then\n    echo \"SUCCESS: Core Transit tunnel is UP\"\nelse\n    echo \"WARNING: Tunnel may not be established yet\"\nfi\n\necho \"Core Transit startup complete!\"\n<\/code><\/pre>\n\n\n\n<p>Make it executable:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chmod +x \/usr\/local\/bin\/coretransit-startup.sh\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create Systemd Service<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>nano \/etc\/systemd\/system\/coretransit.service\n<\/code><\/pre>\n\n\n\n<p>Content:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Unit]\nDescription=Core Transit WireGuard IP Transit\nAfter=network-online.target\nWants=network-online.target\n\n&#91;Service]\nType=oneshot\nRemainAfterExit=yes\nExecStart=\/usr\/local\/bin\/coretransit-startup.sh\n\n&#91;Install]\nWantedBy=multi-user.target\n<\/code><\/pre>\n\n\n\n<p>Enable the service:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl daemon-reload\nsystemctl enable coretransit.service\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 14: Verification Checklist<\/h2>\n\n\n\n<p>Run through these tests to confirm everything works:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># 1. WireGuard tunnel status\nwg show wg-coretransit\n# Look for \"latest handshake\" timestamp\n\n# 2. Ping tunnel peer\nping -c 3 YOUR_CORETRANSIT_TUNNEL_PEER_IP\n\n# 3. Test outbound IPv4 via public IP\nping -c 3 -I YOUR_GATEWAY_IP 8.8.8.8\n\n# 4. Test outbound IPv6 via public IP\nping6 -c 3 -I YOUR_IPV6_GATEWAY 2001:4860:4860::8888\n\n# 5. Verify public IP\ncurl -4 --interface YOUR_GATEWAY_IP ifconfig.me\n# Should return an IP from your allocated block\n\n# 6. Check policy routing rules\nip rule show | grep YOUR_IPV4_BLOCK\n\n# 7. Check routing table 100\nip route show table 100\n\n# 8. Test from a DMZ host (if available)\n# From a server with IP in your public block:\nping 8.8.8.8\ncurl ifconfig.me\n# Should show public IP from your block\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting Guide<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Tunnel Won&#8217;t Establish (No Handshake)<\/h3>\n\n\n\n<p><strong>Symptoms:<\/strong> <code>wg show<\/code> never shows &#8220;latest handshake&#8221;<\/p>\n\n\n\n<p><strong>Checks:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Is UDP traffic reaching Core Transit?\nnc -zuv CORETRANSIT_ENDPOINT_IP PORT\n\n# Check for firewall blocking outbound UDP\niptables -L -n | grep DROP\nnft list ruleset | grep drop\n<\/code><\/pre>\n\n\n\n<p><strong>Common causes:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Firewall blocking outbound UDP<\/li>\n\n\n\n<li>Wrong endpoint IP\/port<\/li>\n\n\n\n<li>Wrong public key<\/li>\n\n\n\n<li>Core Transit hasn&#8217;t registered your public key yet<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Tunnel Up But No Traffic<\/h3>\n\n\n\n<p><strong>Symptoms:<\/strong> Handshake works, but <code>ping -I YOUR_IP 8.8.8.8<\/code> fails<\/p>\n\n\n\n<p><strong>Checks:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Verify policy routing rule exists\nip rule show\n# Must see: \"from YOUR_BLOCK lookup 100\"\n\n# Verify table 100 has default route\nip route show table 100\n# Must see: \"default dev wg-coretransit\"\n\n# Check for conflicting default routes\nip route show default\n# Should NOT see your public gateway IP here\n<\/code><\/pre>\n\n\n\n<p><strong>Common causes:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Policy routing rules not created<\/li>\n\n\n\n<li>Table 100 missing default route<\/li>\n\n\n\n<li>Another interface stealing the default route<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Slow Speeds \/ Stalling Transfers<\/h3>\n\n\n\n<p><strong>Symptoms:<\/strong> Small pings work, large transfers hang<\/p>\n\n\n\n<p><strong>Solution:<\/strong> MTU problem. See Step 10.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Works Initially, Breaks After Time<\/h3>\n\n\n\n<p><strong>Symptoms:<\/strong> Everything works, then stops after minutes\/hours<\/p>\n\n\n\n<p><strong>Common causes:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>DHCP on management interface adding rogue default route<\/li>\n\n\n\n<li>NAT timeout on ISP (increase PersistentKeepalive)<\/li>\n<\/ul>\n\n\n\n<p><strong>Fix for rogue routes:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Create a cron job to remove bad routes\ncrontab -e\n\n# Add:\n* * * * * ip route del default via YOUR_LAN_GATEWAY dev eth-lan 2&amp;gt;\/dev\/null || true\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Final Network Diagram<\/h2>\n\n\n\n<p>After setup, your traffic flows look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DMZ Host (203.0.113.178)\n         \u2502\n         \u2502 Packet: src=203.0.113.178 dst=8.8.8.8\n         \u2502\n         \u25bc\n    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n    \u2502 br-dmz  \u2502 Gateway receives packet\n    \u2514\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2518\n         \u2502\n         \u2502 Policy routing: \"src 203.0.113.0\/28 \u2192 table 100\"\n         \u2502 Table 100: \"default \u2192 wg-coretransit\"\n         \u2502\n         \u25bc\n  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n  \u2502wg-coretransit\u2502 WireGuard encrypts packet\n  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n         \u2502\n         \u2502 Encrypted WG packet: src=YOUR_WAN_IP dst=CORETRANSIT_ENDPOINT\n         \u2502\n         \u25bc\n    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n    \u2502 eth-wan \u2502 Sent via normal ISP default route\n    \u2514\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2518\n         \u2502\n         \u25bc\n    ISP (CG-NAT)\n         \u2502\n         \u25bc\n    INTERNET\n         \u2502\n         \u25bc\n   CORE TRANSIT\n         \u2502\n         \u2502 Decrypts, routes to internet as 203.0.113.178\n         \u2502\n         \u25bc\n      8.8.8.8\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>That&#8217;s it! You now have real, routable public IP addresses at home, tunneled through Core Transit&#8217;s WireGuard service. Your servers can:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Accept inbound connections from the internet<\/li>\n\n\n\n<li>Host websites, game servers, mail servers, etc.<\/li>\n\n\n\n<li>Use static public IPs that you control<\/li>\n\n\n\n<li>Work through any NAT, including CG-NAT<\/li>\n<\/ul>\n\n\n\n<p>The key pieces are:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>WireGuard tunnel<\/strong> for encrypted transport<\/li>\n\n\n\n<li><strong>Policy routing<\/strong> to direct public IP traffic through the tunnel<\/li>\n\n\n\n<li><strong>Proper MTU<\/strong> to avoid packet fragmentation issues<\/li>\n\n\n\n<li><strong>Firewall<\/strong> to protect your gateway<\/li>\n<\/ol>\n\n\n\n<p>Core Transit has been rock solid for me. The tunnel reconnects automatically, speeds are good, and having real public IPs again feels like getting my internet freedom back.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Quick Reference Card<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Item<\/th><th>Value<\/th><\/tr><\/thead><tbody><tr><td>WireGuard config<\/td><td><code>\/etc\/wireguard\/wg-coretransit.conf<\/code><\/td><\/tr><tr><td>Startup script<\/td><td><code>\/usr\/local\/bin\/coretransit-startup.sh<\/code><\/td><\/tr><tr><td>Systemd service<\/td><td><code>\/etc\/systemd\/system\/coretransit.service<\/code><\/td><\/tr><tr><td>Routing table<\/td><td>100<\/td><\/tr><tr><td>Policy rule priority<\/td><td>5<\/td><\/tr><tr><td>Recommended MTU<\/td><td>1320 (adjust based on testing)<\/td><\/tr><tr><td>MSS clamp value<\/td><td>1280<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Essential Commands<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Tunnel status\nwg show wg-coretransit\n\n# Restart tunnel\nwg-quick down wg-coretransit &amp;amp;&amp;amp; wg-quick up wg-coretransit\n\n# Check policy routing\nip rule show\nip route show table 100\n\n# Test public IP\ncurl -4 --interface YOUR_GATEWAY_IP ifconfig.me\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em>Questions? Issues? Drop a comment below or reach out. Happy tunneling!<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A Complete Guide to why Core Transit&#8217;s WireGuard IP Transit Service is so incredibly awesome for you lab Bypassing CG-NAT with style &#8211; a detailed technical walkthrough The Problem: CG-NAT Hell If you&#8217;re reading this, you probably know the pain. Your ISP puts you behind Carrier-Grade NAT (CG-NAT), which means: I was stuck behind CG-NAT&hellip; <a class=\"read-more\" href=\"https:\/\/carminebufano.com\/index.php\/2025\/12\/26\/how-to-get-real-public-ip-addresses-at-home-lab-wireguard-ip-transit-complete-guide\/\">Read More<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"Complete technical guide to bypassing CG-NAT and getting real public IPv4\/IPv6 addresses at home using Core Transit's WireGuard IP transit service. Step-by-step setup for Linux gateway with policy routing, MTU optimization, and firewall configuration. Perfect for homelabs, self-hosting, and running servers behind carrier-grade NAT.","jetpack_seo_html_title":"Give your homelab real public IPs behind CG-NAT. WireGuard-based IP transit guide with policy routing, firewall setup, and MTU troubleshooting tips.","jetpack_seo_noindex":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[15],"tags":[],"class_list":["post-829","post","type-post","status-publish","format-standard","hentry","category-walkthroughs"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":552,"url":"https:\/\/carminebufano.com\/index.php\/2022\/06\/20\/allow-vmware-workstation-and-hyper-v-to-run-on-the-same-windows-10-workstation\/","url_meta":{"origin":829,"position":0},"title":"Allow VMware Workstation and Hyper-V to run on the same Windows 10 workstation","author":"Carmine Bufano","date":"June 20, 2022","format":false,"excerpt":"So I upgraded my lab workstation recently and it is running Windows 10 Professional. I installed VMware Workstation 16 PRO and it works fine. It is great for a lab, especially when you need to install a entire vSphere environment. This is because VMware Workstation is great at nested virtualization.\u2026","rel":"","context":"In &quot;Walkthroughs&quot;","block_context":{"text":"Walkthroughs","link":"https:\/\/carminebufano.com\/index.php\/category\/walkthroughs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":265,"url":"https:\/\/carminebufano.com\/index.php\/2017\/10\/20\/vmware-nsx\/","url_meta":{"origin":829,"position":1},"title":"VMware NSX","author":"Carmine Bufano","date":"October 20, 2017","format":false,"excerpt":"","rel":"","context":"In &quot;Walkthroughs&quot;","block_context":{"text":"Walkthroughs","link":"https:\/\/carminebufano.com\/index.php\/category\/walkthroughs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":428,"url":"https:\/\/carminebufano.com\/index.php\/2021\/01\/30\/install-virtual-box-on-ubuntu-16-04-on-a-digitalocean-droplet\/","url_meta":{"origin":829,"position":2},"title":"How to Install Oracle Virtual Box on Ubuntu 16.04 on a DigitalOcean Droplet","author":"Carmine Bufano","date":"January 30, 2021","format":false,"excerpt":"I wanted to circumvent my residential FIOS with my one dynamic public ip in my home lab without upgrading to a commercial account with Verizon. Right now I'm enjoying 1Gig up and down. One look at commercial prices and its a small fortune for static ip's and the same residential\u2026","rel":"","context":"In &quot;openvswitch&quot;","block_context":{"text":"openvswitch","link":"https:\/\/carminebufano.com\/index.php\/category\/openvswitch\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":592,"url":"https:\/\/carminebufano.com\/index.php\/2022\/12\/17\/how-to-manually-remove-vmware-nsx-t-components-from-an-esxi-host\/","url_meta":{"origin":829,"position":3},"title":"How to Manually Remove VMware NSX-T Components from an ESXi Host","author":"Carmine Bufano","date":"December 17, 2022","format":false,"excerpt":"A lab is very dynamic by nature. In stark contrast, data center infrastructure is engineered to be as static as possible once it is configured. So naturally when you have a data center lab you it's only a matter of time before you come across weird anomalies just from the\u2026","rel":"","context":"In &quot;Walkthroughs&quot;","block_context":{"text":"Walkthroughs","link":"https:\/\/carminebufano.com\/index.php\/category\/walkthroughs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":611,"url":"https:\/\/carminebufano.com\/index.php\/2023\/06\/26\/the-dynamic-infrastructure-dilemma\/","url_meta":{"origin":829,"position":4},"title":"The Dynamic Infrastructure Dilemma","author":"Carmine Bufano","date":"June 26, 2023","format":false,"excerpt":"I have about 10 servers, several routers, and 5 enterprise and open switches. I want to rewire all of these things in different configurations frequently. I also want to change the hypervisors that run on the 10 servers. i want to do this the easiest way possible. Remotely. If I'm\u2026","rel":"","context":"In &quot;Walkthroughs&quot;","block_context":{"text":"Walkthroughs","link":"https:\/\/carminebufano.com\/index.php\/category\/walkthroughs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":123,"url":"https:\/\/carminebufano.com\/index.php\/2014\/11\/27\/real-world-application-of-gre-encapsulation-between-mixed-hypervisors-on-disjoint-networks-part-1\/","url_meta":{"origin":829,"position":5},"title":"Real World Application of GRE and VXLAN Encapsulation between Mixed Hypervisors on Disjoint Networks PART-1.","author":"Carmine Bufano","date":"November 27, 2014","format":false,"excerpt":"","rel":"","context":"In &quot;CentOS&quot;","block_context":{"text":"CentOS","link":"https:\/\/carminebufano.com\/index.php\/category\/centos\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_shortlink":"https:\/\/wp.me\/p70MUT-dn","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/posts\/829","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/comments?post=829"}],"version-history":[{"count":6,"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/posts\/829\/revisions"}],"predecessor-version":[{"id":837,"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/posts\/829\/revisions\/837"}],"wp:attachment":[{"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/media?parent=829"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/categories?post=829"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/carminebufano.com\/index.php\/wp-json\/wp\/v2\/tags?post=829"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}