<?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: Jesse D</title>
    <description>The latest articles on DEV Community by Jesse D (@jessedye).</description>
    <link>https://dev.to/jessedye</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1512335%2F0fdc00b5-6938-4649-86c3-f2357691afac.jpeg</url>
      <title>DEV Community: Jesse D</title>
      <link>https://dev.to/jessedye</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jessedye"/>
    <language>en</language>
    <item>
      <title>Building a Zero-Trust VPN: How I Wrapped OpenVPN and WireGuard with Modern Authentication</title>
      <dc:creator>Jesse D</dc:creator>
      <pubDate>Sun, 25 Jan 2026 06:02:19 +0000</pubDate>
      <link>https://dev.to/jessedye/building-a-zero-trust-vpn-how-i-wrapped-openvpn-and-wireguard-with-modern-authentication-4cjn</link>
      <guid>https://dev.to/jessedye/building-a-zero-trust-vpn-how-i-wrapped-openvpn-and-wireguard-with-modern-authentication-4cjn</guid>
      <description>&lt;h1&gt;
  
  
  Building a Zero-Trust VPN: How I Wrapped OpenVPN and WireGuard with Modern Authentication
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h7v6fr5ilcj6m75w5ny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h7v6fr5ilcj6m75w5ny.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It started with my homelab.&lt;/p&gt;

&lt;p&gt;I have a modest setup—a few Proxmox nodes, some Kubernetes clusters, network storage, the usual suspects. I wanted secure remote access without exposing everything to the internet. Simple ask, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Search for a Solution
&lt;/h2&gt;

&lt;p&gt;I tried the modern zero-trust solutions first:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailscale&lt;/strong&gt; - Great product, but I wanted self-hosted. Their open-source Headscale works, but felt like I was fighting the architecture. Coordination servers, DERP relays, ACL policies in HuJSON—it's elegant if you buy into their model, but I wanted something closer to traditional VPN semantics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NetBird&lt;/strong&gt; - Promising concept, but the setup was painful. Multiple components to deploy, documentation gaps, and I spent more time debugging connectivity issues than actually using it. The mesh networking model also felt like overkill for my use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firezone&lt;/strong&gt; - Looked good on paper, but the WireGuard-only approach was limiting. Some of my older devices needed OpenVPN. And the complexity of the Elixir/Phoenix stack made me nervous about long-term maintenance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pritunl&lt;/strong&gt; - Closer to what I wanted, but the enterprise licensing model and MongoDB dependency gave me pause. Also, the codebase felt dated.&lt;/p&gt;

&lt;p&gt;Each solution had pieces I liked, but none hit the sweet spot: &lt;strong&gt;simple deployment, SSO integration, both OpenVPN and WireGuard, and actual zero-trust with per-user firewall rules.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So I built my own.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsaeq5f6z2a8f390nmnnb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsaeq5f6z2a8f390nmnnb.jpg" alt=" " width="620" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcojcvilezb9povl4vc4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcojcvilezb9povl4vc4.gif" alt=" " width="688" height="530"&gt;&lt;/a&gt;                                                                                                                                                                                                                                                                                                                      &lt;/p&gt;

&lt;p&gt;A few simple commands. That's it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GateKey&lt;/strong&gt; is a zero-trust VPN solution that wraps OpenVPN and WireGuard to provide software-defined perimeter capabilities—while maintaining 100% compatibility with existing clients.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk through the architecture decisions, the technical challenges, and how you can deploy a zero-trust VPN for your organization or homelab.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with Traditional VPNs
&lt;/h2&gt;

&lt;p&gt;If you've managed a corporate VPN, you know the pain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Certificate Management Hell&lt;/strong&gt; - Generating, distributing, and rotating certificates manually&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Secrets&lt;/strong&gt; - One compromised credential = everyone's compromised&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flat Network Access&lt;/strong&gt; - Once you're in, you can reach everything&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Identity Awareness&lt;/strong&gt; - The VPN doesn't know &lt;em&gt;who&lt;/em&gt; you are, just that you have a valid cert&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual Onboarding&lt;/strong&gt; - New employee? Here's a 10-step guide to configure your VPN client&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Modern identity providers solved authentication years ago. Why are VPNs still using 1990s-era certificate distribution?&lt;/p&gt;




&lt;h2&gt;
  
  
  The Zero-Trust Approach
&lt;/h2&gt;

&lt;p&gt;GateKey flips the model:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Authenticate first, connect second.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User has certificate → User connects → Hope they're authorized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User authenticates via SSO → System generates short-lived cert → User connects → Per-user firewall rules applied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Principles
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identity-First&lt;/strong&gt; - Integrate with your existing IdP (Okta, Azure AD, Google Workspace)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short-Lived Credentials&lt;/strong&gt; - Certificates expire in 24 hours (configurable), no manual rotation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-User Firewall&lt;/strong&gt; - Each user gets individualized network access based on their role&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Standing Privileges&lt;/strong&gt; - No access without active authentication&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's what SSO login looks like in the web UI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5j6dmn9kbg2bi229ieks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5j6dmn9kbg2bi229ieks.png" alt=" " width="700" height="930"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Deep-Dive
&lt;/h2&gt;

&lt;p&gt;Here's how the pieces fit together:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkl3qrwc9kuiilhi67rz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkl3qrwc9kuiilhi67rz.png" alt=" " width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Component Breakdown
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Control Plane&lt;/strong&gt; (Go + React)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles SSO authentication (OIDC/SAML)&lt;/li&gt;
&lt;li&gt;Embedded Certificate Authority for on-demand cert generation&lt;/li&gt;
&lt;li&gt;Policy engine for access rules&lt;/li&gt;
&lt;li&gt;REST API for all operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsuge6tu5xv6grp8ohoqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsuge6tu5xv6grp8ohoqf.png" alt=" " width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gateway Agents&lt;/strong&gt; (Go)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lightweight agents that run alongside OpenVPN/WireGuard&lt;/li&gt;
&lt;li&gt;Communicate with control plane via authenticated API&lt;/li&gt;
&lt;li&gt;Manage nftables firewall rules per-user&lt;/li&gt;
&lt;li&gt;Support both OpenVPN and WireGuard protocols&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wabuor5xf2rnycmvrae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wabuor5xf2rnycmvrae.png" alt=" " width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embedded CA&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No external PKI infrastructure required&lt;/li&gt;
&lt;li&gt;Generates short-lived certificates on-demand&lt;/li&gt;
&lt;li&gt;Supports graceful CA rotation with zero downtime&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Technical Decision: Why Wrap Instead of Reimplement?
&lt;/h2&gt;

&lt;p&gt;I considered building a custom VPN protocol. Then I remembered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;OpenVPN and WireGuard are battle-tested&lt;/strong&gt; - Millions of deployments, extensively audited&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client compatibility&lt;/strong&gt; - Every platform already has OpenVPN/WireGuard clients&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security audits are expensive&lt;/strong&gt; - Why audit a new protocol when proven ones exist?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead, GateKey uses &lt;strong&gt;hook scripts&lt;/strong&gt; to intercept OpenVPN events:&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="c"&gt;# OpenVPN calls these scripts on client events&lt;/span&gt;
client-connect /usr/local/bin/gatekey-connect
client-disconnect /usr/local/bin/gatekey-disconnect
auth-user-pass-verify /usr/local/bin/gatekey-verify via-env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a client connects:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fux0g4209zeqkxcdymfit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fux0g4209zeqkxcdymfit.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OpenVPN triggers &lt;code&gt;gatekey-verify&lt;/code&gt; with the client certificate&lt;/li&gt;
&lt;li&gt;Gateway agent validates the cert against the control plane&lt;/li&gt;
&lt;li&gt;Control plane checks: Is this cert valid? Is the user still authenticated? What access rules apply?&lt;/li&gt;
&lt;li&gt;Gateway receives the user's access rules and configures nftables&lt;/li&gt;
&lt;li&gt;User gets access only to their authorized resources&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The VPN protocol itself is unchanged.&lt;/strong&gt; We're just adding an authentication and authorization layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Per-User Firewall Rules with nftables
&lt;/h2&gt;

&lt;p&gt;This is where zero-trust gets real. Each connected user gets their own firewall rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// internal/firewall/nftables.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NFTablesFirewall&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ApplyUserRules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vpnIP&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;AccessRule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create a chain for this specific user&lt;/span&gt;
    &lt;span class="n"&gt;chainName&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sanitize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;// Add rules for each allowed network&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;nftCmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"nft add rule inet gatekey %s ip saddr %s ip daddr %s accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;chainName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vpnIP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CIDR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nftCmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to add rule: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Default deny for this user&lt;/span&gt;
    &lt;span class="n"&gt;nftCmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"nft add rule inet gatekey %s ip saddr %s drop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;chainName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vpnIP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nftCmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access rules are managed through a clean admin interface:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxphl6whf3hazo9f2kee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxphl6whf3hazo9f2kee.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fh0t4jump85yzjws2jr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fh0t4jump85yzjws2jr.png" alt=" " width="739" height="1346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1r2ib5q4jtqev1g2wdf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1r2ib5q4jtqev1g2wdf6.png" alt=" " width="800" height="990"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When access rules change in the admin UI, gateways poll for updates every 10 seconds and immediately update firewall rules—no client reconnection required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foalpudodzvj1tsylfmoa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foalpudodzvj1tsylfmoa.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Short-Lived Certificates: The Key to Zero Trust
&lt;/h2&gt;

&lt;p&gt;Traditional VPNs issue certificates valid for 1-5 years. If compromised, you're exposed for years.&lt;/p&gt;

&lt;p&gt;GateKey certificates expire in &lt;strong&gt;24 hours by default&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// internal/pki/ca.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ca&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;IssueCertificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commonName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validityHours&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SerialNumber&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;big&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnixNano&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pkix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;CommonName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;commonName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;NotBefore&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;NotAfter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;              &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validityHours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;KeyUsage&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;              &lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;KeyUsageDigitalSignature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ExtKeyUsage&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;           &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExtKeyUsage&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExtKeyUsageClientAuth&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;BasicConstraintsValid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Sign with our CA&lt;/span&gt;
    &lt;span class="n"&gt;certDER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateCertificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ca&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pubKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ca&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CA management interface provides full visibility into certificate lifecycle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7v2u6v60o6ntauovocm7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7v2u6v60o6ntauovocm7.png" alt=" " width="800" height="803"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The client CLI handles renewal automatically:&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="nv"&gt;$ &lt;/span&gt;gatekey connect
&lt;span class="c"&gt;# First time: Opens browser for SSO, downloads config, connects&lt;/span&gt;
&lt;span class="c"&gt;# Subsequent times: Refreshes config if needed, connects immediately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users never see certificate expiration. They just authenticate and connect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dual Protocol Support: OpenVPN + WireGuard
&lt;/h2&gt;

&lt;p&gt;Some environments need OpenVPN (maximum compatibility). Others want WireGuard (performance, mobile). GateKey supports both with the same zero-trust model:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h4z3bfyss4i58o6xt9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h4z3bfyss4i58o6xt9p.png" alt=" " width="800" height="593"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Gateway types in the database&lt;/span&gt;
&lt;span class="na"&gt;gateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-openvpn&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openvpn&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vpn-east.company.com:1194&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-west-wireguard&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wireguard&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vpn-west.company.com:51820&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users can connect to either:&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="nv"&gt;$ &lt;/span&gt;gatekey connect us-east-openvpn    &lt;span class="c"&gt;# Uses OpenVPN&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gatekey connect us-west-wireguard  &lt;span class="c"&gt;# Uses WireGuard&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gatekey status
Active connections:
  us-east-openvpn   connected   10.8.0.5    tun0
  us-west-wireguard connected   10.9.0.12   wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multi-gateway support means users can be connected to multiple VPNs simultaneously—each with their own firewall rules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mesh Networking: Site-to-Site Connectivity
&lt;/h2&gt;

&lt;p&gt;Beyond traditional client-to-site VPN, GateKey supports hub-and-spoke mesh networking for site-to-site connectivity:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8qkiusgr97yxc2d5tic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8qkiusgr97yxc2d5tic.png" alt=" " width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2g244fmet7ao4wmolcc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2g244fmet7ao4wmolcc2.png" alt=" " width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Branch office connectivity&lt;/strong&gt; - Connect remote offices to central resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-cloud networking&lt;/strong&gt; - Link AWS, GCP, and on-prem networks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-trust between sites&lt;/strong&gt; - Same per-identity firewall rules apply&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Network Topology Visualization
&lt;/h2&gt;

&lt;p&gt;Monitor your entire VPN infrastructure in real-time:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjb75bjnbwjeoxqwyzwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjb75bjnbwjeoxqwyzwl.png" alt=" " width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The topology view shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All gateways, hubs, and spokes&lt;/li&gt;
&lt;li&gt;Active VPN sessions&lt;/li&gt;
&lt;li&gt;Connection health and bandwidth&lt;/li&gt;
&lt;li&gt;User attribution for every connection&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Geo-Fencing: IP-Based Access Control
&lt;/h2&gt;

&lt;p&gt;Add another layer of security with geo-fencing rules:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxu2c6dofw7cr7oi6gw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxu2c6dofw7cr7oi6gw5.png" alt=" " width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Whitelist model&lt;/strong&gt; - Only connections from explicitly allowed IP ranges&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hierarchical rules&lt;/strong&gt; - User rules override group rules, which override global rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit mode&lt;/strong&gt; - Log violations without blocking (for testing)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Authentication Integration
&lt;/h2&gt;

&lt;p&gt;GateKey integrates with your existing identity provider:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmwjrscx9way8qoxt1773.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmwjrscx9way8qoxt1773.png" alt=" " width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supported providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Okta&lt;/li&gt;
&lt;li&gt;Azure AD / Entra ID&lt;/li&gt;
&lt;li&gt;Google Workspace&lt;/li&gt;
&lt;li&gt;Keycloak&lt;/li&gt;
&lt;li&gt;Any OIDC or SAML 2.0 compliant provider&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Admin Dashboard
&lt;/h2&gt;

&lt;p&gt;A unified view of your zero-trust VPN infrastructure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z2638fq9y1wx4t5lt7g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z2638fq9y1wx4t5lt7g.png" alt=" " width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The User Experience
&lt;/h2&gt;

&lt;p&gt;For end users, it's three commands:&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="c"&gt;# One-time setup&lt;/span&gt;
gatekey config init &lt;span class="nt"&gt;--server&lt;/span&gt; https://vpn.company.com

&lt;span class="c"&gt;# Daily usage&lt;/span&gt;
gatekey login      &lt;span class="c"&gt;# Opens browser for SSO&lt;/span&gt;
gatekey connect    &lt;span class="c"&gt;# Downloads config, connects&lt;/span&gt;
gatekey disconnect &lt;span class="c"&gt;# When done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the web UI to download a standard &lt;code&gt;.ovpn&lt;/code&gt; file that works with any OpenVPN client.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Kubernetes (Recommended)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add gatekey https://dye-tech.github.io/gatekey-helm-chart
helm &lt;span class="nb"&gt;install &lt;/span&gt;gatekey gatekey/gatekey &lt;span class="nt"&gt;-n&lt;/span&gt; gatekey &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker Compose
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gatekey&lt;/span&gt;

  &lt;span class="na"&gt;gatekey-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dyetech/gatekey-server:latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://gatekey:pass@postgres/gatekey&lt;/span&gt;

  &lt;span class="na"&gt;gatekey-web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dyetech/gatekey-web:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:8080"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Building GateKey taught me several things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't reinvent crypto&lt;/strong&gt; - Wrap proven protocols instead of creating new ones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hook scripts are powerful&lt;/strong&gt; - OpenVPN's plugin system is underutilized&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nftables &amp;gt; iptables&lt;/strong&gt; - Modern, atomic updates, better performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short-lived credentials change everything&lt;/strong&gt; - When certs expire in hours, compromise windows shrink dramatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer experience matters&lt;/strong&gt; - &lt;code&gt;gatekey connect&lt;/code&gt; should just work&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;GateKey is open source under Apache 2.0:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/dye-tech/GateKey" rel="noopener noreferrer"&gt;github.com/dye-tech/GateKey&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://gatekey.net" rel="noopener noreferrer"&gt;gatekey.net&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Hub&lt;/strong&gt;: &lt;a href="https://hub.docker.com/u/dyetech" rel="noopener noreferrer"&gt;hub.docker.com/u/dyetech&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helm Chart&lt;/strong&gt;: &lt;a href="https://github.com/dye-tech/gatekey-helm-chart" rel="noopener noreferrer"&gt;github.com/dye-tech/gatekey-helm-chart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Android Client&lt;/strong&gt;: &lt;a href="https://github.com/dye-tech/gatekey-android-client" rel="noopener noreferrer"&gt;github.com/dye-tech/gatekey-android-client&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're tired of managing VPN certificates manually, or want to add zero-trust to your existing OpenVPN/WireGuard infrastructure, give it a try.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your experience with VPN infrastructure? Have you implemented zero-trust networking? I'd love to hear about your approach in the comments.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>vpn</category>
      <category>security</category>
      <category>zerotrust</category>
      <category>devsecops</category>
    </item>
  </channel>
</rss>
