Add support for WireGuard

Almost full support for WireGuard

admin@server:/> show interface wg0
name                : wg0
type                : wireguard
index               : 10
mtu                 : 1420
operational status  : up
ipv4 addresses      : 10.0.0.1/24 (static)
ipv6 addresses      : fd00::1/64 (static)
peers               : 2

  Peer 1:
    public key        : ROaZyvJc5DzA2XUAAeTj2YlwDsy2w0lr3t+rWj2imAk=
    status            : UP
    endpoint          : 192.168.10.2:51821
    latest handshake  : 2025-12-09T22:51:38+00:00
    transfer tx       : 1412 bytes
    transfer rx       : 1324 bytes

  Peer 2:
    public key        : Om9CPLYdK3l93GauKrq5WXo/gbcD+1CeqFpobRLLkB4=
    status            : UP
    endpoint          : 2001:db8:3c4d:20::2:51822
    latest handshake  : 2025-12-09T22:51:38+00:00
    transfer tx       : 1812 bytes
    transfer rx       : 428 bytes
in-octets           : 1752
out-octets          : 3224

admin@server:/>
This commit is contained in:
Mattias Walström
2026-01-09 11:11:32 +01:00
parent 8e9fd27e24
commit 7e5da21cf6
50 changed files with 6193 additions and 16 deletions
+1
View File
@@ -244,6 +244,7 @@ CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
CONFIG_NETDEVICES=y
CONFIG_BONDING=m
CONFIG_DUMMY=m
CONFIG_WIREGUARD=m
CONFIG_MACVLAN=m
CONFIG_MACVTAP=m
CONFIG_IPVLAN=m
+1
View File
@@ -191,6 +191,7 @@ CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
CONFIG_NETDEVICES=y
CONFIG_BONDING=m
CONFIG_DUMMY=m
CONFIG_WIREGUARD=m
CONFIG_MACVLAN=m
CONFIG_MACVTAP=m
CONFIG_IPVLAN=m
+1
View File
@@ -82,6 +82,7 @@ BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_WHOIS=y
BR2_PACKAGE_WIREGUARD_TOOLS=y
BR2_PACKAGE_BASH_COMPLETION=y
BR2_PACKAGE_SUDO=y
BR2_PACKAGE_GETENT=y
+1
View File
@@ -82,6 +82,7 @@ BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_WHOIS=y
BR2_PACKAGE_WIREGUARD_TOOLS=y
BR2_PACKAGE_BASH_COMPLETION=y
BR2_PACKAGE_SUDO=y
BR2_PACKAGE_GETENT=y
+1
View File
@@ -102,6 +102,7 @@ BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_TRACEROUTE=y
BR2_PACKAGE_ULOGD=y
BR2_PACKAGE_WHOIS=y
BR2_PACKAGE_WIREGUARD_TOOLS=y
BR2_PACKAGE_BASH_COMPLETION=y
BR2_PACKAGE_NEOFETCH=y
BR2_PACKAGE_SUDO=y
+1
View File
@@ -85,6 +85,7 @@ BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_WHOIS=y
BR2_PACKAGE_WIREGUARD_TOOLS=y
BR2_PACKAGE_BASH_COMPLETION=y
BR2_PACKAGE_SUDO=y
BR2_PACKAGE_KMOD_TOOLS=y
+1
View File
@@ -98,6 +98,7 @@ BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_TRACEROUTE=y
BR2_PACKAGE_ULOGD=y
BR2_PACKAGE_WHOIS=y
BR2_PACKAGE_WIREGUARD_TOOLS=y
BR2_PACKAGE_BASH_COMPLETION=y
BR2_PACKAGE_NEOFETCH=y
BR2_PACKAGE_SUDO=y
+1
View File
@@ -80,6 +80,7 @@ BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_WHOIS=y
BR2_PACKAGE_WIREGUARD_TOOLS=y
BR2_PACKAGE_BASH_COMPLETION=y
BR2_PACKAGE_SUDO=y
BR2_PACKAGE_GETENT=y
+2
View File
@@ -19,6 +19,7 @@ All notable changes to the project are documented in this file.
>
> - WiFi Access Point (AP) mode support with multi-SSID capability
> - RIPv2 routing support
> - WireGuard support
### Changes
@@ -47,6 +48,7 @@ All notable changes to the project are documented in this file.
moved to `wifi/station` container. Existing Wi-Fi interfaces will be
removed during upgrade (for the rest of the configuration to apply)
and you need to reconfigure them again. See [wifi.md](wifi.md) for details
- Add support for WireGuard VPN tunnels.
### Fixes
+983
View File
@@ -0,0 +1,983 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 700 500"
version="1.1"
id="svg220"
sodipodi:docname="vpn-hub-spoke.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview222"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.8885714"
inkscape:cx="350.00001"
inkscape:cy="225.56733"
inkscape:window-width="1920"
inkscape:window-height="1014"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg220" />
<defs
id="defs40">
<linearGradient
id="bgGrad"
x1="0%"
y1="0%"
x2="100%"
y2="100%">
<stop
offset="0%"
style="stop-color:#fafbfc;stop-opacity:1"
id="stop2" />
<stop
offset="100%"
style="stop-color:#f3f4f6;stop-opacity:1"
id="stop4" />
</linearGradient>
<linearGradient
id="siteGrad"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#3b82f6;stop-opacity:1"
id="stop7" />
<stop
offset="100%"
style="stop-color:#2563eb;stop-opacity:1"
id="stop9" />
</linearGradient>
<linearGradient
id="routerGrad"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#ef4444;stop-opacity:1"
id="stop12" />
<stop
offset="100%"
style="stop-color:#dc2626;stop-opacity:1"
id="stop14" />
</linearGradient>
<linearGradient
id="hubGrad"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#f59e0b;stop-opacity:1"
id="stop17" />
<stop
offset="100%"
style="stop-color:#d97706;stop-opacity:1"
id="stop19" />
</linearGradient>
<filter
id="shadow"
x="-0.054666667"
y="-0.082"
width="1.1093333"
height="1.184">
<feGaussianBlur
in="SourceAlpha"
stdDeviation="3"
id="feGaussianBlur22" />
<feOffset
dx="0"
dy="2"
result="offsetblur"
id="feOffset24" />
<feComponentTransfer
id="feComponentTransfer28">
<feFuncA
type="linear"
slope="0.2"
id="feFuncA26" />
</feComponentTransfer>
<feMerge
id="feMerge34">
<feMergeNode
id="feMergeNode30" />
<feMergeNode
in="SourceGraphic"
id="feMergeNode32" />
</feMerge>
</filter>
<marker
id="arrow"
markerWidth="8"
markerHeight="8"
refX="7"
refY="4"
orient="auto">
<path
d="M 0,0 V 8 L 8,4 Z"
fill="#10b981"
id="path37" />
</marker>
<radialGradient
id="cloudGrad"
cx="220.99805"
cy="209.29596"
gradientTransform="matrix(1.3416408,0,0,0.74535599,215.52724,137.78745)"
fx="220.99805"
fy="209.29596"
r="51.988579"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
style="stop-color:#ffffff;stop-opacity:1"
id="stop22" />
<stop
offset="70%"
style="stop-color:#dbeafe;stop-opacity:1"
id="stop24" />
<stop
offset="100%"
style="stop-color:#93c5fd;stop-opacity:1"
id="stop26" />
</radialGradient>
<filter
id="shadow-7"
x="-0.061678832"
y="-0.11266667"
width="1.1233577"
height="1.252">
<feGaussianBlur
in="SourceAlpha"
stdDeviation="3"
id="feGaussianBlur29" />
<feOffset
dx="0"
dy="2"
result="offsetblur"
id="feOffset31" />
<feComponentTransfer
id="feComponentTransfer35">
<feFuncA
type="linear"
slope="0.2"
id="feFuncA33" />
</feComponentTransfer>
<feMerge
id="feMerge41">
<feMergeNode
id="feMergeNode37" />
<feMergeNode
in="SourceGraphic"
id="feMergeNode39" />
</feMerge>
</filter>
</defs>
<style
id="style26">
@media (prefers-color-scheme: dark) {
#bgGrad stop:first-child { stop-color: #1f2937; }
#bgGrad stop:last-child { stop-color: #111827; }
#siteGrad stop:first-child { stop-color: #2563eb; }
#siteGrad stop:last-child { stop-color: #1e40af; }
#routerGrad stop:first-child { stop-color: #dc2626; }
#routerGrad stop:last-child { stop-color: #b91c1c; }
#hubGrad stop:first-child { stop-color: #f59e0b; }
#hubGrad stop:last-child { stop-color: #d97706; }
rect[fill=&quot;#fff&quot;], rect[fill=&quot;#ffffff&quot;] { fill: #374151; }
rect[stroke=&quot;#e5e7eb&quot;] { stroke: #4b5563; }
text[fill=&quot;#6b7280&quot;] { fill: #d1d5db; }
circle[fill=&quot;#60a5fa&quot;] { fill: #3b82f6; }
line[stroke=&quot;#94a3b8&quot;] { stroke: #6b7280; }
rect[fill=&quot;#fbbf24&quot;] { fill: #f59e0b; }
rect[fill=&quot;#fca5a5&quot;] { fill: #fca5a5; }
}
</style>
<!-- Physical Internet connections (solid lines) -->
<!-- VPN Tunnels -->
<line
x1="149.87024"
y1="94.852066"
x2="262.66898"
y2="194.88768"
stroke="#10b981"
stroke-width="2.6"
stroke-dasharray="10,6"
marker-end="url(#arrow)"
id="line44" />
<g
transform="translate(155,83)"
id="g52">
<rect
width="8"
height="10"
rx="1.5"
fill="#10b981"
id="rect46"
x="0"
y="0" />
<path
d="m 1.5,0 v -2.5 a 2.5,2.5 0 0 1 5,0 V 0"
fill="none"
stroke="#10b981"
stroke-width="1.2"
id="path48" />
<circle
cx="4"
cy="6"
r="1.2"
fill="#ffffff"
id="circle50" />
</g>
<line
x1="550.13788"
y1="94.850327"
x2="437.03882"
y2="193.65047"
stroke="#10b981"
stroke-width="2.6"
stroke-dasharray="10,6"
marker-end="url(#arrow)"
id="line54" />
<g
transform="translate(537,83)"
id="g62">
<rect
width="8"
height="10"
rx="1.5"
fill="#10b981"
id="rect56"
x="0"
y="0" />
<path
d="m 1.5,0 v -2.5 a 2.5,2.5 0 0 1 5,0 V 0"
fill="none"
stroke="#10b981"
stroke-width="1.2"
id="path58" />
<circle
cx="4"
cy="6"
r="1.2"
fill="#ffffff"
id="circle60" />
</g>
<line
x1="348.10764"
y1="399.8786"
x2="348.10764"
y2="311.25961"
stroke="#10b981"
stroke-width="2.6"
stroke-dasharray="10, 6"
marker-end="url(#arrow)"
id="line64" />
<g
transform="translate(346,383)"
id="g72">
<rect
width="8"
height="10"
rx="1.5"
fill="#10b981"
id="rect66"
x="0"
y="0" />
<path
d="m 1.5,0 v -2.5 a 2.5,2.5 0 0 1 5,0 V 0"
fill="none"
stroke="#10b981"
stroke-width="1.2"
id="path68" />
<circle
cx="4"
cy="6"
r="1.2"
fill="#ffffff"
id="circle70" />
</g>
<!-- Hub (Center) -->
<g
filter="url(#shadow)"
id="g114">
<rect
x="260"
y="195"
width="180"
height="110"
rx="12"
fill="#fff"
stroke="#e5e7eb"
stroke-width="2"
id="rect82" />
<!-- Header -->
<rect
x="260"
y="195"
width="180"
height="38"
rx="12"
fill="url(#siteGrad)"
id="rect84" />
<rect
x="260"
y="220"
width="180"
height="13"
fill="url(#siteGrad)"
id="rect86" />
<text
x="350"
y="220"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="15"
font-weight="700"
fill="#fff"
id="text88">Headquarters</text>
<!-- Hub Router -->
<rect
x="278"
y="243"
width="144"
height="38"
rx="8"
fill="url(#hubGrad)"
id="rect90" />
<rect
x="280"
y="245"
width="140"
height="18"
rx="6"
fill="#fbbf24"
opacity="0.3"
id="rect92" />
<text
x="350"
y="267"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="13"
font-weight="700"
fill="#fff"
id="text94">HUB</text>
<!-- Local Network -->
<line
x1="260"
y1="250"
x2="180"
y2="250"
stroke="#94a3b8"
stroke-width="1.5"
id="line96" />
<line
x1="180"
y1="220"
x2="180"
y2="280"
stroke="#94a3b8"
stroke-width="1.5"
id="line98" />
<circle
cx="172"
cy="225"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle100" />
<line
x1="180"
y1="225"
x2="174"
y2="225"
stroke="#94a3b8"
stroke-width="1"
id="line102" />
<circle
cx="172"
cy="250"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle104" />
<line
x1="180"
y1="250"
x2="174"
y2="250"
stroke="#94a3b8"
stroke-width="1"
id="line106" />
<circle
cx="172"
cy="275"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle108" />
<line
x1="180"
y1="275"
x2="174"
y2="275"
stroke="#94a3b8"
stroke-width="1"
id="line110" />
<text
x="130"
y="252"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="8"
font-weight="600"
fill="#6b7280"
id="text112">192.168.0.0/24</text>
</g>
<!-- Spoke A (Top Left) -->
<g
filter="url(#shadow)"
id="g148">
<rect
x="40"
y="30"
width="150"
height="100"
rx="12"
fill="#fff"
stroke="#e5e7eb"
stroke-width="2"
id="rect116" />
<!-- Header -->
<rect
x="40"
y="30"
width="150"
height="32"
rx="12"
fill="url(#siteGrad)"
id="rect118" />
<rect
x="40"
y="51"
width="150"
height="11"
fill="url(#siteGrad)"
id="rect120" />
<text
x="115"
y="51"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="14"
font-weight="700"
fill="#fff"
id="text122">Branch A</text>
<!-- Router -->
<rect
x="55"
y="70"
width="120"
height="30"
rx="6"
fill="url(#routerGrad)"
id="rect124" />
<rect
x="57"
y="72"
width="116"
height="14"
rx="4"
fill="#fca5a5"
opacity="0.3"
id="rect126" />
<text
x="115"
y="89"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="11"
font-weight="600"
fill="#fff"
id="text128">Spoke</text>
<!-- Local Network -->
<line
x1="115"
y1="100"
x2="115"
y2="107"
stroke="#94a3b8"
stroke-width="1.5"
id="line130" />
<line
x1="90"
y1="107"
x2="140"
y2="107"
stroke="#94a3b8"
stroke-width="1.5"
id="line132" />
<circle
cx="95"
cy="115"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle134" />
<line
x1="95"
y1="107"
x2="95"
y2="113"
stroke="#94a3b8"
stroke-width="1"
id="line136" />
<circle
cx="115"
cy="115"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle138" />
<line
x1="115"
y1="107"
x2="115"
y2="113"
stroke="#94a3b8"
stroke-width="1"
id="line140" />
<circle
cx="135"
cy="115"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle142" />
<line
x1="135"
y1="107"
x2="135"
y2="113"
stroke="#94a3b8"
stroke-width="1"
id="line144" />
<text
x="115"
y="128"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="7"
font-weight="600"
fill="#6b7280"
id="text146">192.168.1.0/24</text>
</g>
<!-- Spoke B (Top Right) -->
<g
filter="url(#shadow)"
id="g182">
<rect
x="510"
y="30"
width="150"
height="100"
rx="12"
fill="#fff"
stroke="#e5e7eb"
stroke-width="2"
id="rect150" />
<!-- Header -->
<rect
x="510"
y="30"
width="150"
height="32"
rx="12"
fill="url(#siteGrad)"
id="rect152" />
<rect
x="510"
y="51"
width="150"
height="11"
fill="url(#siteGrad)"
id="rect154" />
<text
x="585"
y="51"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="14"
font-weight="700"
fill="#fff"
id="text156">Branch B</text>
<!-- Router -->
<rect
x="525"
y="70"
width="120"
height="30"
rx="6"
fill="url(#routerGrad)"
id="rect158" />
<rect
x="527"
y="72"
width="116"
height="14"
rx="4"
fill="#fca5a5"
opacity="0.3"
id="rect160" />
<text
x="585"
y="89"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="11"
font-weight="600"
fill="#fff"
id="text162">Spoke</text>
<!-- Local Network -->
<line
x1="585"
y1="100"
x2="585"
y2="107"
stroke="#94a3b8"
stroke-width="1.5"
id="line164" />
<line
x1="560"
y1="107"
x2="610"
y2="107"
stroke="#94a3b8"
stroke-width="1.5"
id="line166" />
<circle
cx="565"
cy="115"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle168" />
<line
x1="565"
y1="107"
x2="565"
y2="113"
stroke="#94a3b8"
stroke-width="1"
id="line170" />
<circle
cx="585"
cy="115"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle172" />
<line
x1="585"
y1="107"
x2="585"
y2="113"
stroke="#94a3b8"
stroke-width="1"
id="line174" />
<circle
cx="605"
cy="115"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle176" />
<line
x1="605"
y1="107"
x2="605"
y2="113"
stroke="#94a3b8"
stroke-width="1"
id="line178" />
<text
x="585"
y="125"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="7"
font-weight="600"
fill="#6b7280"
id="text180">192.168.2.0/24</text>
</g>
<!-- Spoke C (Bottom) -->
<g
filter="url(#shadow)"
id="g216">
<rect
x="275"
y="370"
width="150"
height="100"
rx="12"
fill="#fff"
stroke="#e5e7eb"
stroke-width="2"
id="rect184" />
<!-- Header -->
<rect
x="275"
y="370"
width="150"
height="32"
rx="12"
fill="url(#siteGrad)"
id="rect186" />
<rect
x="275"
y="391"
width="150"
height="11"
fill="url(#siteGrad)"
id="rect188" />
<text
x="350"
y="391"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="14"
font-weight="700"
fill="#fff"
id="text190">Branch C</text>
<!-- Router -->
<rect
x="290"
y="410"
width="120"
height="30"
rx="6"
fill="url(#routerGrad)"
id="rect192" />
<rect
x="292"
y="412"
width="116"
height="14"
rx="4"
fill="#fca5a5"
opacity="0.3"
id="rect194" />
<text
x="350"
y="429"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="11"
font-weight="600"
fill="#fff"
id="text196">Spoke</text>
<!-- Local Network -->
<line
x1="350"
y1="440"
x2="350"
y2="447"
stroke="#94a3b8"
stroke-width="1.5"
id="line198" />
<line
x1="325"
y1="447"
x2="375"
y2="447"
stroke="#94a3b8"
stroke-width="1.5"
id="line200" />
<circle
cx="330"
cy="455"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle202" />
<line
x1="330"
y1="447"
x2="330"
y2="453"
stroke="#94a3b8"
stroke-width="1"
id="line204" />
<circle
cx="350"
cy="455"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle206" />
<line
x1="350"
y1="447"
x2="350"
y2="453"
stroke="#94a3b8"
stroke-width="1"
id="line208" />
<circle
cx="370"
cy="455"
r="4"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle210" />
<line
x1="370"
y1="447"
x2="370"
y2="453"
stroke="#94a3b8"
stroke-width="1"
id="line212" />
<text
x="350"
y="465"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="7"
font-weight="600"
fill="#6b7280"
id="text214">192.168.3.0/24</text>
</g>
<g
transform="matrix(0.98466939,0,0,1.0000859,498.17175,113.77759)"
id="g97">
<rect
width="10"
height="12"
rx="2"
fill="#10b981"
id="rect91"
x="0"
y="0" />
<path
d="m 2,0 v -3 a 3,3 0 0 1 6,0 v 3"
fill="none"
stroke="#10b981"
stroke-width="1.5"
id="path93" />
<circle
cx="5"
cy="7"
r="1.5"
fill="#ffffff"
id="circle95" />
</g>
<text
x="446.42084"
y="148.79063"
text-anchor="middle"
font-family="system-ui, '-apple-system', sans-serif"
font-size="10.9158px"
font-weight="600"
fill="#059669"
id="text99"
transform="scale(0.99226244,1.0077979)"
style="stroke-width:0.992348">Encrypted VPN</text>
<g
transform="matrix(0.98466939,0,0,1.0000859,193.71492,114.69643)"
id="g97-3">
<rect
width="10"
height="12"
rx="2"
fill="#10b981"
id="rect91-6"
x="0"
y="0" />
<path
d="m 2,0 v -3 a 3,3 0 0 1 6,0 v 3"
fill="none"
stroke="#10b981"
stroke-width="1.5"
id="path93-7" />
<circle
cx="5"
cy="7"
r="1.5"
fill="#ffffff"
id="circle95-5" />
</g>
<text
x="254.16939"
y="144.61209"
text-anchor="middle"
font-family="system-ui, '-apple-system', sans-serif"
font-size="10.9158px"
font-weight="600"
fill="#059669"
id="text99-3"
transform="scale(0.99226244,1.0077979)"
style="stroke-width:0.992348">Encrypted VPN</text>
<g
transform="matrix(0.98466939,0,0,1.0000859,333.47081,353.92815)"
id="g97-5">
<rect
width="10"
height="12"
rx="2"
fill="#10b981"
id="rect91-62"
x="0"
y="0" />
<path
d="m 2,0 v -3 a 3,3 0 0 1 6,0 v 3"
fill="none"
stroke="#10b981"
stroke-width="1.5"
id="path93-9" />
<circle
cx="5"
cy="7"
r="1.5"
fill="#ffffff"
id="circle95-1" />
</g>
<text
x="393.52994"
y="352.21753"
text-anchor="middle"
font-family="system-ui, '-apple-system', sans-serif"
font-size="10.9158px"
font-weight="600"
fill="#059669"
id="text99-2"
transform="scale(0.99226244,1.0077979)"
style="stroke-width:0.992348">Encrypted VPN</text>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

+689
View File
@@ -0,0 +1,689 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 700 350"
version="1.1"
id="svg171"
sodipodi:docname="vpn-roadwarrior.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview173"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.8885714"
inkscape:cx="350.00001"
inkscape:cy="174.73525"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg171" />
<defs
id="defs47">
<linearGradient
id="bgGrad"
x1="0%"
y1="0%"
x2="100%"
y2="100%">
<stop
offset="0%"
style="stop-color:#fafbfc;stop-opacity:1"
id="stop2" />
<stop
offset="100%"
style="stop-color:#f3f4f6;stop-opacity:1"
id="stop4" />
</linearGradient>
<linearGradient
id="corpGrad"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#3b82f6;stop-opacity:1"
id="stop7" />
<stop
offset="100%"
style="stop-color:#2563eb;stop-opacity:1"
id="stop9" />
</linearGradient>
<linearGradient
id="routerGrad"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#ef4444;stop-opacity:1"
id="stop12" />
<stop
offset="100%"
style="stop-color:#dc2626;stop-opacity:1"
id="stop14" />
</linearGradient>
<linearGradient
id="deviceGrad"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#8b5cf6;stop-opacity:1"
id="stop17" />
<stop
offset="100%"
style="stop-color:#7c3aed;stop-opacity:1"
id="stop19" />
</linearGradient>
<radialGradient
id="cloudGrad"
cx="220.99805"
cy="209.29596"
gradientTransform="scale(1.3416408,0.74535599)"
fx="220.99805"
fy="209.29596"
r="51.98858"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
style="stop-color:#ffffff;stop-opacity:1"
id="stop22" />
<stop
offset="70%"
style="stop-color:#dbeafe;stop-opacity:1"
id="stop24" />
<stop
offset="100%"
style="stop-color:#93c5fd;stop-opacity:1"
id="stop26" />
</radialGradient>
<filter
id="shadow"
x="-0.164"
y="-0.11699244"
width="1.328"
height="1.2563258">
<feGaussianBlur
in="SourceAlpha"
stdDeviation="3"
id="feGaussianBlur29" />
<feOffset
dx="0"
dy="2"
result="offsetblur"
id="feOffset31" />
<feComponentTransfer
id="feComponentTransfer35">
<feFuncA
type="linear"
slope="0.2"
id="feFuncA33" />
</feComponentTransfer>
<feMerge
id="feMerge41">
<feMergeNode
id="feMergeNode37" />
<feMergeNode
in="SourceGraphic"
id="feMergeNode39" />
</feMerge>
</filter>
<marker
id="arrow"
markerWidth="8"
markerHeight="8"
refX="7"
refY="4"
orient="auto">
<path
d="M 0,0 V 8 L 8,4 Z"
fill="#10b981"
id="path44" />
</marker>
</defs>
<style
id="style30">
@media (prefers-color-scheme: dark) {
#bgGrad stop:first-child { stop-color: #1f2937; }
#bgGrad stop:last-child { stop-color: #111827; }
#corpGrad stop:first-child { stop-color: #2563eb; }
#corpGrad stop:last-child { stop-color: #1e40af; }
#routerGrad stop:first-child { stop-color: #dc2626; }
#routerGrad stop:last-child { stop-color: #b91c1c; }
#deviceGrad stop:first-child { stop-color: #4c1d95; }
#deviceGrad stop:last-child { stop-color: #3b0764; }
#cloudGrad stop:first-child { stop-color: #374151; }
#cloudGrad stop:nth-child(2) { stop-color: #1e3a8a; }
#cloudGrad stop:last-child { stop-color: #1e40af; }
rect[fill=&quot;#fff&quot;], rect[fill=&quot;#ffffff&quot;] { fill: #374151; }
rect[stroke=&quot;#e5e7eb&quot;] { stroke: #4b5563; }
text[fill=&quot;#6b7280&quot;] { fill: #d1d5db; }
text[fill=&quot;#1e40af&quot;] { fill: #60a5fa; }
ellipse[fill=&quot;#ffffff&quot;] { fill: #4b5563; }
circle[fill=&quot;#60a5fa&quot;] { fill: #3b82f6; }
line[stroke=&quot;#94a3b8&quot;] { stroke: #6b7280; }
rect[fill=&quot;#1e40af&quot;] { fill: #1e3a8a; }
rect[fill=&quot;#c4b5fd&quot;] { fill: #8b5cf6; }
rect[fill=&quot;#ede9fe&quot;] { fill: #2e1065; }
text[fill=&quot;#5b21b6&quot;] { fill: #9333ea; }
circle[stroke=&quot;#6d28d9&quot;] { stroke: #4c1d95; }
rect[stroke=&quot;#6d28d9&quot;] { stroke: #4c1d95; }
text[fill=&quot;#ef4444&quot;] { fill: #fca5a5; }
}
</style>
<!-- Internet Cloud -->
<!-- Physical Internet connections (solid lines) -->
<line
x1="110"
y1="65"
x2="230"
y2="160"
stroke="#6b7280"
stroke-width="2"
id="inet-laptop" />
<text
x="137.55841"
y="109.12807"
text-anchor="middle"
font-family="system-ui, '-apple-system', sans-serif"
font-size="9px"
font-weight="500"
fill="#ef4444"
id="text-inet-laptop">Unsecure</text>
<line
x1="79.384186"
y1="185.51099"
x2="229.7012"
y2="183.32214"
stroke="#6b7280"
stroke-width="2"
id="inet-phone" />
<text
x="145"
y="194"
text-anchor="middle"
font-family="system-ui, '-apple-system', sans-serif"
font-size="9px"
font-weight="500"
fill="#ef4444"
id="text-inet-phone">Unsecure</text>
<line
x1="105"
y1="292"
x2="230"
y2="190"
stroke="#6b7280"
stroke-width="2"
id="inet-tablet" />
<text
x="143.19562"
y="243.69884"
text-anchor="middle"
font-family="system-ui, '-apple-system', sans-serif"
font-size="9px"
font-weight="500"
fill="#ef4444"
id="text-inet-tablet">Unsecure</text>
<line
x1="513.08228"
y1="155.4146"
x2="344.54852"
y2="156.5854"
stroke="#6b7280"
stroke-width="1.98644"
id="inet-corporate" />
<text
x="430"
y="150"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="9"
font-weight="500"
fill="#ef4444"
id="text-inet-corporate">Unsecure</text>
<!-- VPN Tunnels: Three lines from clients that merge into one line to server -->
<g
transform="matrix(0.98466939,0,0,1.0000859,119.62684,52.989786)"
id="g73">
<rect
width="8"
height="10"
rx="1.5"
fill="#10b981"
id="rect67"
x="0"
y="0" />
<path
d="m 1.5,0 v -2.5 a 2.5,2.5 0 0 1 5,0 V 0"
fill="none"
stroke="#10b981"
stroke-width="1.2"
id="path69" />
<circle
cx="4"
cy="6"
r="1.2"
fill="#ffffff"
id="circle71" />
</g>
<g
transform="matrix(0.98466939,0,0,1.0000859,87.933449,161.99967)"
id="g81">
<rect
width="8"
height="10"
rx="1.5"
fill="#10b981"
id="rect75"
x="0"
y="0" />
<path
d="m 1.5,0 v -2.5 a 2.5,2.5 0 0 1 5,0 V 0"
fill="none"
stroke="#10b981"
stroke-width="1.2"
id="path77" />
<circle
cx="4"
cy="6"
r="1.2"
fill="#ffffff"
id="circle79" />
</g>
<g
transform="matrix(0.98466939,0,0,1.0000859,113.62684,307.00955)"
id="g89">
<rect
width="8"
height="10"
rx="1.5"
fill="#10b981"
id="rect83"
x="0"
y="0" />
<path
d="m 1.5,0 v -2.5 a 2.5,2.5 0 0 1 5,0 V 0"
fill="none"
stroke="#10b981"
stroke-width="1.2"
id="path85" />
<circle
cx="4"
cy="6"
r="1.2"
fill="#ffffff"
id="circle87" />
</g>
<g
transform="matrix(0.98466939,0,0,1.0000859,457.18096,186.99924)"
id="g97">
<rect
width="10"
height="12"
rx="2"
fill="#10b981"
id="rect91"
x="0"
y="0" />
<path
d="m 2,0 v -3 a 3,3 0 0 1 6,0 v 3"
fill="none"
stroke="#10b981"
stroke-width="1.5"
id="path93" />
<circle
cx="5"
cy="7"
r="1.5"
fill="#ffffff"
id="circle95" />
</g>
<text
x="413.23505"
y="186.04326"
text-anchor="middle"
font-family="system-ui, '-apple-system', sans-serif"
font-size="10.9158px"
font-weight="600"
fill="#059669"
id="text99"
transform="scale(0.99226244,1.0077979)"
style="stroke-width:0.992348">Encrypted VPN</text>
<!-- Laptop Device -->
<g
filter="url(#shadow)"
id="g115">
<rect
x="20"
y="30"
width="90"
height="60"
rx="6"
fill="url(#deviceGrad)"
stroke="#6d28d9"
stroke-width="2"
id="rect103" />
<rect
x="26"
y="36"
width="78"
height="46"
rx="3"
fill="#ede9fe"
id="rect105" />
<rect
x="49"
y="82"
width="32"
height="4"
rx="2"
fill="url(#deviceGrad)"
id="rect107" />
<rect
x="35"
y="86"
width="60"
height="3"
rx="1.5"
fill="url(#deviceGrad)"
id="rect109" />
<text
x="65"
y="63"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="11"
font-weight="600"
fill="#5b21b6"
id="text111">Laptop</text>
<text
x="65"
y="100"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="9"
font-weight="500"
fill="#6b7280"
id="text113">Remote Worker</text>
</g>
<!-- Mobile Phone Device -->
<g
filter="url(#shadow)"
id="g127">
<rect
x="30"
y="145"
width="50"
height="80"
rx="6"
fill="url(#deviceGrad)"
stroke="#6d28d9"
stroke-width="2"
id="rect117" />
<rect
x="36"
y="153"
width="38"
height="56"
rx="3"
fill="#ede9fe"
id="rect119" />
<circle
cx="55"
cy="218"
r="4"
fill="#ede9fe"
stroke="#6d28d9"
stroke-width="1.5"
id="circle121" />
<text
x="55"
y="188"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="10"
font-weight="600"
fill="#5b21b6"
id="text123">Mobile</text>
<text
x="55"
y="240"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="9"
font-weight="500"
fill="#6b7280"
id="text125">On-the-go</text>
</g>
<!-- Tablet Device -->
<g
filter="url(#shadow)"
id="g139">
<rect
x="25"
y="265"
width="80"
height="55"
rx="5"
fill="url(#deviceGrad)"
stroke="#6d28d9"
stroke-width="2"
id="rect129" />
<rect
x="31"
y="271"
width="68"
height="40"
rx="3"
fill="#ede9fe"
id="rect131" />
<circle
cx="65"
cy="313"
r="3.5"
fill="#ede9fe"
stroke="#6d28d9"
stroke-width="1.5"
id="circle133" />
<text
x="65"
y="295"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="10"
font-weight="600"
fill="#5b21b6"
id="text135">Tablet</text>
<text
x="65"
y="335"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="9"
font-weight="500"
fill="#6b7280"
id="text137">Traveling</text>
</g>
<!-- Corporate Network -->
<g
filter="url(#shadow)"
id="g167">
<rect
x="510"
y="80"
width="170"
height="190"
rx="12"
fill="#fff"
stroke="#e5e7eb"
stroke-width="2"
id="rect141" />
<!-- Header -->
<rect
x="510"
y="80"
width="170"
height="45"
rx="12"
fill="url(#corpGrad)"
id="rect143" />
<rect
x="510"
y="105"
width="170"
height="20"
fill="url(#corpGrad)"
id="rect145" />
<text
x="595"
y="108"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="16"
font-weight="700"
fill="#fff"
id="text147">Corporate</text>
<rect
x="545"
y="115"
width="100"
height="18"
rx="9"
fill="#1e40af"
id="rect149" />
<text
x="595"
y="127"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="11"
font-weight="600"
fill="#fff"
id="text151">192.168.0.0/24</text>
<!-- VPN Gateway -->
<rect
x="530"
y="140"
width="130"
height="42"
rx="8"
fill="url(#routerGrad)"
id="rect153" />
<rect
x="532"
y="142"
width="126"
height="20"
rx="6"
fill="#fca5a5"
opacity="0.3"
id="rect155" />
<text
x="595"
y="165"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="13"
font-weight="700"
fill="#fff"
id="text157">VPN Gateway</text>
<!-- Resources -->
<rect
x="530"
y="195"
width="130"
height="60"
rx="8"
fill="#dbeafe"
stroke="#3b82f6"
stroke-width="1.5"
id="rect159" />
<text
x="595"
y="218"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="12"
font-weight="600"
fill="#1e40af"
id="text161">Servers</text>
<text
x="595"
y="232"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="12"
font-weight="600"
fill="#1e40af"
id="text163">Files</text>
<text
x="595"
y="246"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="12"
font-weight="600"
fill="#1e40af"
id="text165">Applications</text>
</g>
<path
d="m 230,175 c 0,-13 8,-23 20,-23 2,-9 10,-15 20,-15 7,0 13,3 17,8 5,-7 13,-11 23,-11 16,0 30,11 32,26 13,1 23,12 23,25 0,13 -11,24 -25,24 h -88 c -13,0 -24,-11 -24,-24 0,-7 3,-13 7,-17 z"
fill="url(#cloudGrad)"
stroke="#3b82f6"
stroke-width="2.5"
stroke-linejoin="round"
id="path51"
style="fill:url(#cloudGrad);filter:url(#shadow)" />
<line
x1="114.70349"
y1="64.990814"
x2="316.56073"
y2="175.00027"
stroke="#10b981"
stroke-width="2.6"
stroke-dasharray="10,6"
id="line59" />
<line
x1="83.621422"
y1="175.41223"
x2="316.59158"
y2="175.58884"
stroke="#10b981"
stroke-width="2.6"
stroke-dasharray="10, 6"
id="line61" />
<line
x1="108.29816"
y1="302.53445"
x2="316.5495"
y2="175.04326"
stroke="#10b981"
stroke-width="2.6"
stroke-dasharray="10, 6"
id="line63" />
<line
x1="316.56073"
y1="175.00027"
x2="503.64789"
y2="175.00027"
stroke="#10b981"
stroke-width="2.6"
stroke-dasharray="12,8"
marker-end="url(#arrow)"
id="line65" />
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

+595
View File
@@ -0,0 +1,595 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 800 300"
version="1.1"
id="svg178"
sodipodi:docname="vpn-site-to-site.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview180"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.6525"
inkscape:cx="400"
inkscape:cy="150.07564"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg178" />
<defs
id="defs42">
<linearGradient
id="bgGrad"
x1="0%"
y1="0%"
x2="100%"
y2="100%">
<stop
offset="0%"
style="stop-color:#fafbfc;stop-opacity:1"
id="stop2" />
<stop
offset="100%"
style="stop-color:#f3f4f6;stop-opacity:1"
id="stop4" />
</linearGradient>
<linearGradient
id="siteGrad"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#3b82f6;stop-opacity:1"
id="stop7" />
<stop
offset="100%"
style="stop-color:#2563eb;stop-opacity:1"
id="stop9" />
</linearGradient>
<linearGradient
id="routerGrad"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#ef4444;stop-opacity:1"
id="stop12" />
<stop
offset="100%"
style="stop-color:#dc2626;stop-opacity:1"
id="stop14" />
</linearGradient>
<radialGradient
id="cloudGrad"
cx="288.08009"
cy="175.75494"
gradientTransform="matrix(1.3416408,0,0,0.74535599,-11.528571,2.8295566)"
fx="288.08009"
fy="175.75494"
r="51.98858"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
style="stop-color:#ffffff;stop-opacity:1"
id="stop17" />
<stop
offset="70%"
style="stop-color:#dbeafe;stop-opacity:1"
id="stop19" />
<stop
offset="100%"
style="stop-color:#93c5fd;stop-opacity:1"
id="stop21" />
</radialGradient>
<filter
id="shadow"
x="-0.061678832"
y="-0.11266667"
width="1.1233577"
height="1.252">
<feGaussianBlur
in="SourceAlpha"
stdDeviation="3"
id="feGaussianBlur24" />
<feOffset
dx="0"
dy="2"
result="offsetblur"
id="feOffset26" />
<feComponentTransfer
id="feComponentTransfer30">
<feFuncA
type="linear"
slope="0.2"
id="feFuncA28" />
</feComponentTransfer>
<feMerge
id="feMerge36">
<feMergeNode
id="feMergeNode32" />
<feMergeNode
in="SourceGraphic"
id="feMergeNode34" />
</feMerge>
</filter>
<marker
id="arrow"
markerWidth="8"
markerHeight="8"
refX="7"
refY="4"
orient="auto">
<path
d="M 0,0 V 8 L 8,4 Z"
fill="#10b981"
id="path39" />
</marker>
</defs>
<style
id="style27">
@media (prefers-color-scheme: dark) {
#bgGrad stop:first-child { stop-color: #1f2937; }
#bgGrad stop:last-child { stop-color: #111827; }
#siteGrad stop:first-child { stop-color: #2563eb; }
#siteGrad stop:last-child { stop-color: #1e40af; }
#routerGrad stop:first-child { stop-color: #dc2626; }
#routerGrad stop:last-child { stop-color: #b91c1c; }
#cloudGrad stop:first-child { stop-color: #374151; }
#cloudGrad stop:nth-child(2) { stop-color: #1e3a8a; }
#cloudGrad stop:last-child { stop-color: #1e40af; }
rect[fill=&quot;#fff&quot;], rect[fill=&quot;#ffffff&quot;] { fill: #374151; }
rect[stroke=&quot;#e5e7eb&quot;] { stroke: #4b5563; }
text[fill=&quot;#6b7280&quot;] { fill: #d1d5db; }
text[fill=&quot;#1e40af&quot;] { fill: #60a5fa; }
ellipse[fill=&quot;#ffffff&quot;] { fill: #4b5563; }
circle[fill=&quot;#60a5fa&quot;] { fill: #3b82f6; }
line[stroke=&quot;#94a3b8&quot;] { stroke: #6b7280; }
rect[fill=&quot;#1e40af&quot;] { fill: #1e3a8a; }
text[fill=&quot;#ef4444&quot;] { fill: #fca5a5; }
}
</style>
<!-- Internet Cloud -->
<!-- Physical Internet connections (solid lines) -->
<line
x1="169.96941"
y1="150.66559"
x2="308.03061"
y2="148.83441"
stroke="#6b7280"
stroke-width="2"
id="inet-left" />
<text
x="240"
y="145"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="10"
font-weight="500"
fill="#ef4444"
id="text-inet-left">Unsecure</text>
<line
x1="630.02637"
y1="143.69931"
x2="437.97366"
y2="143.80069"
stroke="#6b7280"
stroke-width="2"
id="inet-right" />
<text
x="535"
y="138"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="10"
font-weight="500"
fill="#ef4444"
id="text-inet-right">Unsecure</text>
<!-- VPN Tunnel through cloud -->
<g
transform="translate(191.99738,184.62118)"
id="g62">
<rect
width="10"
height="12"
rx="2"
fill="#10b981"
id="rect56"
x="0"
y="0" />
<path
d="m 2,0 v -3 a 3,3 0 0 1 6,0 v 3"
fill="none"
stroke="#10b981"
stroke-width="1.5"
id="path58" />
<circle
cx="5"
cy="7"
r="1.5"
fill="#ffffff"
id="circle60" />
</g>
<g
transform="translate(564.75904,184.3387)"
id="g70">
<rect
width="10"
height="12"
rx="2"
fill="#10b981"
id="rect64"
x="0"
y="0" />
<path
d="m 2,0 v -3 a 3,3 0 0 1 6,0 v 3"
fill="none"
stroke="#10b981"
stroke-width="1.5"
id="path66" />
<circle
cx="5"
cy="7"
r="1.5"
fill="#ffffff"
id="circle68" />
</g>
<text
x="513.77582"
y="161.37065"
text-anchor="middle"
font-family="system-ui, '-apple-system', sans-serif"
font-size="12px"
font-weight="600"
fill="#059669"
id="text72">Encrypted VPN Tunnel</text>
<!-- Site A (Left) -->
<g
filter="url(#shadow)"
id="g124">
<rect
x="20"
y="60"
width="170"
height="180"
rx="12"
fill="#fff"
stroke="#e5e7eb"
stroke-width="2"
id="rect76" />
<!-- Header -->
<rect
x="20"
y="60"
width="170"
height="45"
rx="12"
fill="url(#siteGrad)"
id="rect78" />
<rect
x="20"
y="85"
width="170"
height="20"
fill="url(#siteGrad)"
id="rect80" />
<text
x="105"
y="88"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="16"
font-weight="700"
fill="#fff"
id="text82">Main Office</text>
<rect
x="55"
y="95"
width="100"
height="18"
rx="9"
fill="#1e40af"
id="rect84" />
<text
x="105"
y="107"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="11"
font-weight="600"
fill="#fff"
id="text86">192.168.1.0/24</text>
<!-- Router -->
<rect
x="40"
y="125"
width="130"
height="45"
rx="8"
fill="url(#routerGrad)"
id="rect88" />
<rect
x="42"
y="127"
width="126"
height="20"
rx="6"
fill="#fca5a5"
opacity="0.3"
id="rect90" />
<text
x="105"
y="152"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="13"
font-weight="700"
fill="#fff"
id="text92">VPN Gateway</text>
<!-- Local Network -->
<line
x1="105"
y1="170"
x2="105"
y2="195"
stroke="#94a3b8"
stroke-width="1.5"
id="line94" />
<line
x1="65"
y1="195"
x2="145"
y2="195"
stroke="#94a3b8"
stroke-width="1.5"
id="line96" />
<circle
cx="70"
cy="204"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle98" />
<line
x1="70"
y1="195"
x2="70"
y2="202"
stroke="#94a3b8"
stroke-width="1"
id="line100" />
<circle
cx="105"
cy="204"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle102" />
<line
x1="105"
y1="195"
x2="105"
y2="202"
stroke="#94a3b8"
stroke-width="1"
id="line104" />
<circle
cx="140"
cy="204"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle106" />
<line
x1="140"
y1="195"
x2="140"
y2="202"
stroke="#94a3b8"
stroke-width="1"
id="line108" />
<text
x="105"
y="218"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="8"
font-weight="600"
fill="#6b7280"
id="text122">192.168.1.0/24</text>
</g>
<!-- Site B (Right) -->
<g
filter="url(#shadow)"
id="g174">
<rect
x="610"
y="60"
width="170"
height="180"
rx="12"
fill="#fff"
stroke="#e5e7eb"
stroke-width="2"
id="rect126" />
<!-- Header -->
<rect
x="610"
y="60"
width="170"
height="45"
rx="12"
fill="url(#siteGrad)"
id="rect128" />
<rect
x="610"
y="85"
width="170"
height="20"
fill="url(#siteGrad)"
id="rect130" />
<text
x="695"
y="88"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="16"
font-weight="700"
fill="#fff"
id="text132">Branch Office</text>
<rect
x="645"
y="95"
width="100"
height="18"
rx="9"
fill="#1e40af"
id="rect134" />
<text
x="695"
y="107"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="11"
font-weight="600"
fill="#fff"
id="text136">192.168.2.0/24</text>
<!-- Router -->
<rect
x="630"
y="125"
width="130"
height="45"
rx="8"
fill="url(#routerGrad)"
id="rect138" />
<rect
x="632"
y="127"
width="126"
height="20"
rx="6"
fill="#fca5a5"
opacity="0.3"
id="rect140" />
<text
x="695"
y="152"
text-anchor="middle"
font-family="system-ui, -apple-system, sans-serif"
font-size="13"
font-weight="700"
fill="#fff"
id="text142">VPN Gateway</text>
<!-- Local Network -->
<line
x1="695"
y1="170"
x2="695"
y2="195"
stroke="#94a3b8"
stroke-width="1.5"
id="line144" />
<line
x1="655"
y1="195"
x2="735"
y2="195"
stroke="#94a3b8"
stroke-width="1.5"
id="line146" />
<circle
cx="660"
cy="204"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle148" />
<line
x1="660"
y1="195"
x2="660"
y2="202"
stroke="#94a3b8"
stroke-width="1"
id="line150" />
<circle
cx="695"
cy="204"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle152" />
<line
x1="695"
y1="195"
x2="695"
y2="202"
stroke="#94a3b8"
stroke-width="1"
id="line154" />
<circle
cx="730"
cy="204"
r="5"
fill="#60a5fa"
stroke="#2563eb"
stroke-width="1"
id="circle156" />
<line
x1="730"
y1="195"
x2="730"
y2="202"
stroke="#94a3b8"
stroke-width="1"
id="line158" />
<text
x="695"
y="218"
text-anchor="middle"
font-family="'SF Mono', 'Monaco', monospace"
font-size="8"
font-weight="600"
fill="#6b7280"
id="text172">192.168.2.0/24</text>
</g>
<path
d="m 308.47143,152.82956 c 0,-13 8,-23 20,-23 2,-9 10,-15 20,-15 7,0 13,3 17,8 5,-7 13,-11 23,-11 16,0 30,11 32,26 13,1 23,12 23,25 0,13 -11,24 -25,24 h -88 c -13,0 -24,-11 -24,-24 0,-7 3,-13 7,-17 z"
fill="url(#cloudGrad)"
stroke="#3b82f6"
stroke-width="2.5"
stroke-linejoin="round"
id="path46"
style="fill:url(#cloudGrad);filter:url(#shadow)" />
<line
x1="188.49466"
y1="166.37616"
x2="604.93829"
y2="166.37616"
stroke="#10b981"
stroke-width="2.6"
stroke-dasharray="12, 8"
marker-end="url(#arrow)"
id="line54" />
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

+785
View File
@@ -0,0 +1,785 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 900 400"
version="1.1"
id="svg180"
sodipodi:docname="wireguard-allowed-ips.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview182"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.3653333"
inkscape:cx="297.36329"
inkscape:cy="375.00001"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg180" />
<defs
id="defs57">
<!-- Premium Gradients -->
<linearGradient
id="bgGradient"
x1="0"
y1="0"
x2="821.58384"
y2="821.58384"
gradientTransform="matrix(1.0954451,0,0,0.46357119,-0.66009153,-0.49920384)"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
style="stop-color:#f8f9fa;stop-opacity:1"
id="stop2" />
<stop
offset="100%"
style="stop-color:#e9ecef;stop-opacity:1"
id="stop4" />
</linearGradient>
<linearGradient
id="peerGradient"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#748ffc;stop-opacity:1"
id="stop7" />
<stop
offset="100%"
style="stop-color:#5c7cfa;stop-opacity:1"
id="stop9" />
</linearGradient>
<linearGradient
id="networkGradient"
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<stop
offset="0%"
style="stop-color:#51cf66;stop-opacity:1"
id="stop12" />
<stop
offset="100%"
style="stop-color:#40c057;stop-opacity:1"
id="stop14" />
</linearGradient>
<linearGradient
id="tunnelGradient"
gradientTransform="scale(9.797959,0.10206207)"
x1="21.433035"
y1="2767.9234"
x2="70.42283"
y2="2767.9234"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
style="stop-color:#5c7cfa;stop-opacity:0.8"
id="stop17" />
<stop
offset="50%"
style="stop-color:#4c6ef5;stop-opacity:1"
id="stop19" />
<stop
offset="100%"
style="stop-color:#5c7cfa;stop-opacity:0.8"
id="stop21" />
</linearGradient>
<!-- Premium Filters -->
<filter
id="cardShadow"
x="-0.075714286"
y="-0.35333333"
width="1.1514286"
height="1.84">
<feGaussianBlur
in="SourceAlpha"
stdDeviation="4"
id="feGaussianBlur24" />
<feOffset
dx="0"
dy="4"
result="offsetblur"
id="feOffset26" />
<feComponentTransfer
id="feComponentTransfer30">
<feFuncA
type="linear"
slope="0.15"
id="feFuncA28" />
</feComponentTransfer>
<feMerge
id="feMerge36">
<feMergeNode
id="feMergeNode32" />
<feMergeNode
in="SourceGraphic"
id="feMergeNode34" />
</feMerge>
</filter>
<filter
id="glow"
x="-0.015"
y="-inf"
width="1.03"
height="inf">
<feGaussianBlur
stdDeviation="3"
result="coloredBlur"
id="feGaussianBlur39" />
<feMerge
id="feMerge45">
<feMergeNode
in="coloredBlur"
id="feMergeNode41" />
<feMergeNode
in="SourceGraphic"
id="feMergeNode43" />
</feMerge>
</filter>
<marker
id="arrowRight"
markerWidth="12"
markerHeight="12"
refX="9"
refY="6"
orient="auto"
markerUnits="strokeWidth">
<path
d="M 2,2 L 10,6 L 2,10 Z"
fill="#4c6ef5"
id="path48" />
</marker>
<marker
id="arrowLeft"
markerWidth="12"
markerHeight="12"
refX="3"
refY="6"
orient="auto"
markerUnits="strokeWidth">
<path
d="M 10,2 L 2,6 L 10,10 Z"
fill="#4c6ef5"
id="path51" />
</marker>
<marker
id="keyArrow"
markerWidth="10"
markerHeight="10"
refX="8"
refY="5"
orient="auto"
markerUnits="strokeWidth">
<path
d="M 1,1 L 9,5 L 1,9 Z"
fill="#ff6b6b"
id="path54" />
</marker>
<marker
id="arrowRight-3"
markerWidth="12"
markerHeight="12"
refX="9"
refY="6"
orient="auto"
markerUnits="strokeWidth">
<path
d="m 2,2 8,4 -8,4 z"
fill="#4c6ef5"
id="path48-6" />
</marker>
</defs>
<style>
@media (prefers-color-scheme: dark) {
/* Gradients */
#bgGradient stop:first-child { stop-color: #1f2937; }
#bgGradient stop:last-child { stop-color: #111827; }
#peerGradient stop:first-child { stop-color: #3b82f6; }
#peerGradient stop:last-child { stop-color: #2563eb; }
#networkGradient stop:first-child { stop-color: #10b981; }
#networkGradient stop:last-child { stop-color: #059669; }
#tunnelGradient stop:first-child { stop-color: #3b82f6; stop-opacity: 0.8; }
#tunnelGradient stop:nth-child(2) { stop-color: #2563eb; stop-opacity: 1; }
#tunnelGradient stop:last-child { stop-color: #3b82f6; stop-opacity: 0.8; }
/* White backgrounds to dark */
rect[fill="white"] { fill: #374151; }
/* Borders */
rect[stroke="#dee2e6"] { stroke: #4b5563; }
rect[stroke="#4263eb"] { stroke: #3b82f6; }
rect[stroke="#4dabf7"] { stroke: #60a5fa; }
rect[stroke="#fa5252"] { stroke: #ef4444; }
rect[stroke="#fab005"] { stroke: #f59e0b; }
/* Background colors */
rect[fill="#e7f5ff"] { fill: #1e3a8a; }
rect[fill="#fff5f5"] { fill: #7f1d1d; }
rect[fill="#fff3bf"] { fill: #78350f; }
rect[fill="#ffffff"] { fill: #374151; }
/* Text colors */
text[fill="#364fc7"] { fill: #60a5fa; }
text[fill="#2b8a3e"] { fill: #10b981; }
text[fill="#868e96"] { fill: #9ca3af; }
text[fill="#1971c2"] { fill: #60a5fa; }
text[fill="#c92a2a"] { fill: #f87171; }
text[fill="#2f9e44"] { fill: #34d399; }
text[fill="#495057"] { fill: #d1d5db; }
text[fill="#e67700"] { fill: #fbbf24; }
text[fill="#fa5252"] { fill: #f87171; }
text[fill="#4263eb"] { fill: #60a5fa; }
/* Arrows and strokes */
line[stroke="#4c6ef5"] { stroke: #60a5fa; }
path[stroke="#ff6b6b"] { stroke: #f87171; }
#arrowRight path { fill: #60a5fa; }
#arrowLeft path { fill: #60a5fa; }
#arrowRight-3 path { fill: #60a5fa; }
#keyArrow path { fill: #f87171; }
}
</style>
<line
id="traffic-arrow-right-5"
x1="690.22913"
y1="168.51242"
x2="584.96887"
y2="264.0126"
stroke="#4c6ef5"
stroke-width="2.5"
stroke-dasharray="6, 4"
marker-end="url(#arrowRight-3)"
style="display:inline;marker-end:url(#arrowRight-3);fill:none"
inkscape:label="traffic-arrow-right-5" />
<!-- Title -->
<text
id="title"
x="450"
y="30"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="22"
font-weight="800"
fill="#364fc7">Understanding WireGuard: Keys &amp; allowed-ips</text>
<!-- Peer A (Left Side) -->
<g
id="peer-a">
<!-- Network behind Peer A -->
<g
id="network-a"
filter="url(#cardShadow)">
<rect
id="network-a-bg"
x="30"
y="80"
width="180"
height="90"
rx="12"
fill="white"
stroke="#dee2e6"
stroke-width="2" />
<rect
id="network-a-header-bg"
x="30"
y="80"
width="180"
height="30"
rx="12"
fill="url(#networkGradient)" />
<rect
id="network-a-header-extension"
x="30"
y="98"
width="180"
height="12"
fill="url(#networkGradient)" />
<text
id="network-a-title"
x="120"
y="98"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="13"
font-weight="700"
fill="white">Network A</text>
<text
id="network-a-cidr"
x="120"
y="130"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="16"
font-weight="600"
fill="#2b8a3e">192.168.1.0/24</text>
<text
id="network-a-devices"
x="120"
y="155"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="10"
fill="#868e96">Devices: .1, .2, .3...</text>
</g>
<!-- WireGuard Peer A -->
<g
id="wg-peer-a"
filter="url(#cardShadow)">
<rect
id="wg-peer-a-bg"
x="30"
y="200"
width="180"
height="170"
rx="12"
fill="white"
stroke="#4263eb"
stroke-width="3" />
<rect
id="wg-peer-a-header-bg"
x="30"
y="200"
width="180"
height="35"
rx="12"
fill="url(#peerGradient)" />
<rect
id="wg-peer-a-header-extension"
x="30"
y="221"
width="180"
height="14"
fill="url(#peerGradient)" />
<text
id="wg-peer-a-title"
x="120"
y="221"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="15"
font-weight="700"
fill="white">WireGuard Peer A</text>
<!-- Interface IP -->
<rect
id="wg-peer-a-interface-bg"
x="50"
y="245"
width="140"
height="22"
rx="6"
fill="#e7f5ff"
stroke="#4dabf7"
stroke-width="1" />
<text
id="wg-peer-a-interface-text"
x="120"
y="260"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="11"
font-weight="600"
fill="#1971c2">wg0: 10.0.0.1/24</text>
<!-- Own Keys -->
<rect
id="wg-peer-a-keys-bg"
x="40"
y="275"
width="160"
height="30"
rx="6"
fill="#fff5f5"
stroke="#fa5252"
stroke-width="1.5" />
<text
id="wg-peer-a-private-key"
x="120"
y="288"
text-anchor="middle"
font-family="Consolas, Monaco, 'Courier New', monospace"
font-size="9px"
font-weight="600"
fill="#c92a2a">PrivateKey: aaa...=</text>
<text
id="wg-peer-a-public-key"
x="120"
y="300"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="9"
font-weight="600"
fill="#2f9e44">PublicKey: AAA...=</text>
<!-- Peer B Config -->
<text
id="wg-peer-a-peer-config-label"
x="120"
y="320"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="10"
font-weight="700"
fill="#495057">[Peer] # Peer B</text>
<rect
id="wg-peer-a-peer-config-bg"
x="40"
y="325"
width="160"
height="38"
rx="6"
fill="#fff3bf"
stroke="#fab005"
stroke-width="1.5" />
<text
id="wg-peer-a-peer-pubkey"
x="120"
y="338"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="9"
font-weight="700"
fill="#e67700">PublicKey = BBB...=</text>
<text
id="wg-peer-a-peer-allowed-ips-1"
x="120"
y="350"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="9"
font-weight="700"
fill="#e67700">allowed-ips = 10.0.0.2/32,</text>
<text
id="wg-peer-a-peer-allowed-ips-2"
x="120"
y="360"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="9"
font-weight="700"
fill="#e67700"> 192.168.2.0/24</text>
</g>
</g>
<!-- Peer B (Right Side) -->
<g
id="peer-b">
<!-- Network behind Peer B -->
<g
id="network-b"
filter="url(#cardShadow)">
<rect
id="network-b-bg"
x="690"
y="80"
width="180"
height="90"
rx="12"
fill="white"
stroke="#dee2e6"
stroke-width="2" />
<rect
id="network-b-header-bg"
x="690"
y="80"
width="180"
height="30"
rx="12"
fill="url(#networkGradient)" />
<rect
id="network-b-header-extension"
x="690"
y="98"
width="180"
height="12"
fill="url(#networkGradient)" />
<text
id="network-b-title"
x="780"
y="98"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="13"
font-weight="700"
fill="white">Network B</text>
<text
id="network-b-cidr"
x="780"
y="130"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="16"
font-weight="600"
fill="#2b8a3e">192.168.2.0/24</text>
<text
id="network-b-devices"
x="780"
y="155"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="10"
fill="#868e96">Devices: .1, .2, .3...</text>
</g>
<!-- WireGuard Peer B -->
<g
id="wg-peer-b"
filter="url(#cardShadow)">
<rect
id="wg-peer-b-bg"
x="690"
y="200"
width="180"
height="170"
rx="12"
fill="white"
stroke="#4263eb"
stroke-width="3" />
<rect
id="wg-peer-b-header-bg"
x="690"
y="200"
width="180"
height="35"
rx="12"
fill="url(#peerGradient)" />
<rect
id="wg-peer-b-header-extension"
x="690"
y="221"
width="180"
height="14"
fill="url(#peerGradient)" />
<text
id="wg-peer-b-title"
x="780"
y="221"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="15"
font-weight="700"
fill="white">WireGuard Peer B</text>
<!-- Interface IP -->
<rect
id="wg-peer-b-interface-bg"
x="710"
y="245"
width="140"
height="22"
rx="6"
fill="#e7f5ff"
stroke="#4dabf7"
stroke-width="1" />
<text
id="wg-peer-b-interface-text"
x="780"
y="260"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="11"
font-weight="600"
fill="#1971c2">wg0: 10.0.0.2/24</text>
<!-- Own Keys -->
<rect
id="wg-peer-b-keys-bg"
x="700"
y="275"
width="160"
height="30"
rx="6"
fill="#fff5f5"
stroke="#fa5252"
stroke-width="1.5" />
<text
id="wg-peer-b-private-key"
x="780"
y="288"
text-anchor="middle"
font-family="Consolas, Monaco, 'Courier New', monospace"
font-size="9px"
font-weight="600"
fill="#c92a2a">PrivateKey: bbb...=</text>
<text
id="wg-peer-b-public-key"
x="780"
y="300"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="9"
font-weight="600"
fill="#2f9e44">PublicKey: BBB...=</text>
<!-- Peer A Config -->
<text
id="wg-peer-b-peer-config-label"
x="780"
y="320"
text-anchor="middle"
font-family="system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="10"
font-weight="700"
fill="#495057">[Peer] # Peer A</text>
<rect
id="wg-peer-b-peer-config-bg"
x="700"
y="325"
width="160"
height="38"
rx="6"
fill="#fff3bf"
stroke="#fab005"
stroke-width="1.5" />
<text
id="wg-peer-b-peer-pubkey"
x="780"
y="338"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="9"
font-weight="700"
fill="#e67700">PublicKey = AAA...=</text>
<text
id="wg-peer-b-peer-allowed-ips-1"
x="780"
y="350"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="9"
font-weight="700"
fill="#e67700">allowed-ips = 10.0.0.1/32,</text>
<text
id="wg-peer-b-peer-allowed-ips-2"
x="780"
y="360"
text-anchor="middle"
font-family="'Consolas', 'Monaco', 'Courier New', monospace"
font-size="9"
font-weight="700"
fill="#e67700"> 192.168.1.0/24</text>
</g>
</g>
<!-- Key Exchange Arrows -->
<path
id="key-arrow-a-to-b"
d="m 178.05495,298.67711 q 309.54082,10.30088 544.04144,39.14333"
stroke="#ff6b6b"
stroke-width="2.45742"
stroke-dasharray="6, 4"
fill="none"
marker-end="url(#keyArrow)" />
<text
id="key-label-a"
x="362.67459"
y="320.16849"
text-anchor="middle"
font-family="system-ui, '-apple-system', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="10px"
font-weight="600"
fill="#fa5252">A's public key (AAA...=)</text>
<path
id="key-arrow-b-to-a"
d="M 729.1812,299.9818 Q 415.736,360.0266 178.27753,338.01017"
stroke="#ff6b6b"
stroke-width="2.4374"
stroke-dasharray="6, 4"
fill="none"
marker-end="url(#keyArrow)" />
<text
id="key-label-b"
x="375.15118"
y="360.33688"
text-anchor="middle"
font-family="system-ui, '-apple-system', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="10px"
font-weight="600"
fill="#fa5252">B's public key (BBB...=)</text>
<!-- WireGuard Tunnel -->
<g
id="tunnel"
transform="translate(0.0587404,0.39164733)">
<line
id="tunnel-glow"
x1="210"
y1="285"
x2="690"
y2="285"
stroke="#4c6ef5"
stroke-width="10"
opacity="0.2"
filter="url(#glow)" />
<line
id="tunnel-line"
x1="210"
y1="285"
x2="690"
y2="285"
stroke="url(#tunnelGradient)"
stroke-width="5"
style="stroke:url(#tunnelGradient)" />
<!-- Tunnel label -->
<rect
id="tunnel-label-bg"
x="380"
y="270"
width="140"
height="30"
rx="8"
fill="#ffffff"
stroke="#4263eb"
stroke-width="2"
filter="url(#cardShadow)" />
<text
id="tunnel-label-text"
x="450"
y="290"
text-anchor="middle"
font-family="system-ui, '-apple-system', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="12px"
font-weight="700"
fill="#4263eb">WireGuard Tunnel</text>
</g>
<!-- Explanation Box -->
<!-- Traffic Flow Example -->
<g
id="traffic-left"
transform="matrix(1.0191745,0,0,1.0648706,-3.9974789,-10.979528)">
<line
id="traffic-arrow-left"
x1="210"
y1="170"
x2="320"
y2="260"
stroke="#4c6ef5"
stroke-width="2.5"
stroke-dasharray="6, 4"
marker-end="url(#arrowRight)" />
<text
id="traffic-label-left"
x="290"
y="215"
font-family="system-ui, '-apple-system', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="9px"
font-weight="600"
fill="#4263eb">to 192.168.2.x</text>
</g>
<text
id="traffic-label-right"
x="583.14978"
y="201.57195"
font-family="system-ui, '-apple-system', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
font-size="9px"
font-weight="600"
fill="#4263eb">to 192.168.1.x</text>
<path
style="fill:none"
d="m 585.61735,261.45164 c -1.59298,2.54412 -1.30788,10.31284 2.71904,6.28562"
id="path1111" />
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

+437
View File
@@ -0,0 +1,437 @@
# WireGuard VPN
> [!NOTE]
> For a general introduction to VPN concepts and deployment models, see
> [VPN Configuration](vpn.md).
WireGuard is a modern, high-performance VPN protocol that uses state-of-the-art
cryptography. It is significantly simpler and faster than traditional VPN
solutions like IPsec or OpenVPN, while maintaining strong security guarantees.
Key features of WireGuard:
- **Simple Configuration:** Minimal settings required compared to IPsec
- **High Performance:** Runs in kernel space with efficient cryptography
- **Strong Cryptography:** Uses Curve25519, ChaCha20, Poly1305, and BLAKE2
- **Roaming Support:** Seamlessly handles endpoint IP address changes
- **Dual-Stack:** Supports IPv4 and IPv6 for both tunnel endpoints and traffic
> [!IMPORTANT]
> When issuing `leave` to activate your changes, remember to also save
> your settings, `copy running-config startup-config`. See the [CLI
> Introduction](cli/introduction.md) for a background.
## Key Management
WireGuard uses public-key cryptography similar to SSH. Each WireGuard interface
requires a private key, and each peer is identified by its public key.
**Generate a WireGuard key pair using the `wg` command:**
```bash
admin@example:~$ wg genkey | tee privatekey | wg pubkey > publickey
admin@example:~$ cat privatekey
aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=
admin@example:~$ cat publickey
bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=
```
This generates a private key, saves it to `privatekey`, derives the public key,
and saves it to `publickey`.
**Import the private key into the keystore:**
```
admin@example:/> configure
admin@example:/config/> edit keystore asymmetric-key wg-site-a
admin@example:/config/keystore/asymmetric-key/wg-site-a/> set public-key-format x25519-public-key-format
admin@example:/config/keystore/asymmetric-key/wg-site-a/> set private-key-format x25519-private-key-format
admin@example:/config/keystore/asymmetric-key/wg-site-a/> set public-key bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=
admin@example:/config/keystore/asymmetric-key/wg-site-a/> set private-key aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=
admin@example:/config/keystore/asymmetric-key/wg-site-a/> leave
admin@example:/>
```
**Import peer public keys into the truststore:**
```
admin@example:/> configure
admin@example:/config/> edit truststore public-key-bag wg-peers public-key peer-b
admin@example:/config/truststore/…/peer-b/> set public-key-format x25519-public-key-format
admin@example:/config/truststore/…/peer-b/> set public-key PEER_PUBLIC_KEY_HERE
admin@example:/config/truststore/…/peer-b/> leave
admin@example:/>
```
> [!IMPORTANT]
> Keep private keys secure! Never share your private key. Only exchange
> public keys with peers. Delete the `privatekey` file after importing it
> into the keystore.
## Point-to-Point Configuration
> [!TIP]
> If you name your WireGuard interface `wgN`, where `N` is a number, the
> CLI infers the interface type automatically.
A basic WireGuard tunnel between two sites:
**Site A configuration:**
```
admin@siteA:/> configure
admin@siteA:/config/> edit interface wg0
admin@siteA:/config/interface/wg0/> set wireguard listen-port 51820
admin@siteA:/config/interface/wg0/> set wireguard private-key wg-site-a
admin@siteA:/config/interface/wg0/> set ipv4 address 10.0.0.1 prefix-length 24
admin@siteA:/config/interface/wg0/> edit wireguard peer wg-peers peer-b
admin@siteA:/config/interface/…/wg-peers/peer/peer-b/> set endpoint 203.0.113.2
admin@siteA:/config/interface/…/wg-peers/peer/peer-b/> set endpoint-port 51820
admin@siteA:/config/interface/…/wg-peers/peer/peer-b/> set allowed-ips 10.0.0.2/32
admin@siteA:/config/interface/…/wg-peers/peer/peer-b/> set persistent-keepalive 25
admin@siteA:/config/interface/…/wg-peers/peer/peer-b/> leave
admin@siteA:/>
```
**Site B configuration:**
```
admin@siteB:/> configure
admin@siteB:/config/> edit interface wg0
admin@siteB:/config/interface/wg0/> set wireguard listen-port 51820
admin@siteB:/config/interface/wg0/> set wireguard private-key wg-site-b
admin@siteB:/config/interface/wg0/> set ipv4 address 10.0.0.2 prefix-length 24
admin@siteB:/config/interface/wg0/> edit wireguard peer wg-peers peer-a
admin@siteB:/config/interface/…/wg-peers/peer/peer-a/> set endpoint 203.0.113.1
admin@siteB:/config/interface/…/wg-peers/peer/peer-a/> set endpoint-port 51820
admin@siteB:/config/interface/…/wg-peers/peer/peer-a/> set allowed-ips 10.0.0.1/32
admin@siteB:/config/interface/…/wg-peers/peer/peer-a/> set persistent-keepalive 25
admin@siteB:/config/interface/…/wg-peers/peer/peer-a/> leave
admin@siteB:/>
```
This creates an encrypted tunnel with Site A at 10.0.0.1 and Site B at 10.0.0.2.
## Understanding Allowed IPs
The `allowed-ips` setting in WireGuard serves two critical purposes:
1. **Ingress Filtering:** Only packets with source IPs in the allowed list
are accepted from the peer
2. **Cryptokey Routing:** Determines which peer receives outbound packets
for a given destination
Think of `allowed-ips` as a combination of firewall rules and routing table.
![WireGuard Keys and Allowed IPs](img/wireguard-allowed-ips.svg)
*Figure: WireGuard key exchange and allowed-ips configuration between two peers*
For a simple point-to-point tunnel, you typically allow only the peer's
tunnel IP address (e.g., `10.0.0.2/32`). For site-to-site VPNs connecting
entire networks, include the remote network prefixes:
```
admin@example:/config/interface/…/wg-peers/peer/peer-a/> set allowed-ips 10.0.0.2/32
admin@example:/config/interface/…/wg-peers/peer/peer-a/> set allowed-ips 192.168.2.0/24
```
This allows traffic to/from the peer at 10.0.0.2 and routes traffic destined
for 192.168.2.0/24 through this peer.
> [!NOTE]
> When routing traffic to networks behind WireGuard peers, you also need
> to configure static routes pointing to the WireGuard interface. See
> [Static Routes](networking.md#static-routes) for more information.
## Peer Configuration and Key Bags
WireGuard peer configuration supports a two-level hierarchy that allows
efficient management of multiple peers with shared settings.
**Public Key Bags** group related peers together (e.g., all mobile clients,
all branch offices) and allow you to configure default settings that apply
to all peers in the bag. Individual peers can then override these defaults
when needed.
Settings that support bag-level defaults and per-peer overrides:
- `endpoint` - Remote peer's IP address
- `endpoint-port` - Remote peer's UDP port (defaults to 51820 at bag level)
- `persistent-keepalive` - Keepalive interval in seconds
- `preshared-key` - Optional pre-shared key for additional quantum resistance
- `allowed-ips` - IP addresses allowed to/from this peer
> [!IMPORTANT]
> **Key Bag Configuration Rules:**
> - **Single key in bag:** You can use bag-level settings (endpoint, allowed-ips, etc.)
> without specifying individual peer configurations. All settings apply to that one peer.
> - **Multiple keys in bag:** You MUST provide individual `peer` configuration for
> each key in the bag. Settings like `endpoint` and `allowed-ips` must be unique
> per peer and cannot be shared at the bag level when multiple peers exist.
>
> This prevents configuration errors where multiple peers would incorrectly share
> the same endpoint address or allowed-ips ranges.
**Example with bag-level defaults:**
```
admin@example:/> configure
admin@example:/config/> edit interface wg0
admin@example:/config/interface/wg0/> set wireguard listen-port 51820
admin@example:/config/interface/wg0/> set wireguard private-key wg-key
admin@example:/config/interface/wg0/> set ipv4 address 10.0.0.1 prefix-length 24
# Configure defaults for all peers in the 'branch-offices' bag
admin@example:/config/interface/wg0/> edit wireguard peers branch-offices
admin@example:/config/interface/…/wireguard/peers/branch-offices/> set endpoint-port 51820
admin@example:/config/interface/…/wireguard/peers/branch-offices/> set persistent-keepalive 25
admin@example:/config/interface/…/wireguard/peers/branch-offices/> end
# Configure peer-specific settings (inherits endpoint-port and keepalive from bag)
admin@example:/config/interface/…/wireguard/peers/branch-offices/> edit peer office-east
admin@example:/config/interface/…/branch-offices/peer/office-east/> set endpoint 203.0.113.10
admin@example:/config/interface/…/branch-offices/peer/office-east/> set allowed-ips 10.0.0.10/32
admin@example:/config/interface/…/branch-offices/peer/office-east/> set allowed-ips 192.168.10.0/24
admin@example:/config/interface/…/branch-offices/peer/office-east/> end
# Another peer with an override for persistent-keepalive
admin@example:/config/interface/…/wireguard/peers/branch-offices/> edit peer office-west
admin@example:/config/interface/…/branch-offices/peer/office-west/> set endpoint 203.0.113.20
admin@example:/config/interface/…/branch-offices/peer/office-west/> set allowed-ips 10.0.0.20/32
admin@example:/config/interface/…/branch-offices/peer/office-west/> set allowed-ips 192.168.20.0/24
admin@example:/config/interface/…/branch-offices/peer/office-west/> set persistent-keepalive 10
admin@example:/config/interface/…/branch-offices/peer/office-west/> leave
admin@example:/>
```
In this example:
- Both peers inherit `endpoint-port 51820` and `persistent-keepalive 25` from the bag
- `office-west` overrides the keepalive to 10 seconds while `office-east` uses the default 25
- Each peer has its own `endpoint` and `allowed-ips` configuration
This approach simplifies management when you have many peers with similar
configurations - set the common defaults once at the bag level, then only
specify per-peer differences.
## Site-to-Site VPN
![Site-to-Site VPN Topology](img/vpn-site-to-site.svg)
*Figure: Site-to-Site VPN connecting two office networks*
A site-to-site VPN connects entire networks across locations, creating a unified
private network over the internet. This allows devices in one location to
seamlessly access resources in another as if they were on the same local network.
This is the point-to-point configuration shown earlier, extended with routing
to allow access to networks behind each peer. Configure the WireGuard tunnel
as shown in [Point-to-Point Configuration](#point-to-point-configuration),
then add the remote network to `allowed-ips` and configure static routes.
## Road Warrior VPN
![Road Warrior VPN Topology](img/vpn-roadwarrior.svg)
*Figure: Mobile clients connecting to corporate network*
For mobile clients or peers without fixed IPs, omit the `endpoint` setting.
WireGuard learns the peer's endpoint from authenticated incoming packets:
```
admin@hub:/> configure
admin@hub:/config/> edit interface wg0 wireguard peers wg-peers peer mobile-client
admin@hub:/config/interface/…/wg-peers/peer/mobile-client/> set allowed-ips 10.0.0.10/32
admin@hub:/config/interface/…/wg-peers/peer/mobile-client/> leave
admin@hub:/>
```
The mobile client configures the hub's endpoint normally. The hub learns
and tracks the mobile client's changing IP address automatically.
## Hub-and-Spoke Topology
![Hub-and-Spoke VPN Topology](img/vpn-hub-spoke.svg)
*Figure: Hub-and-Spoke topology with central hub routing traffic between spokes*
WireGuard excels at hub-and-spoke (star) topologies where multiple remote
sites connect to a central hub.
**Hub configuration:**
```
admin@hub:/> configure
admin@hub:/config/> edit interface wg0
admin@hub:/config/interface/wg0/> set ipv4 address 10.0.0.1 prefix-length 24
admin@hub:/config/interface/wg0/> set wireguard listen-port 51820
admin@hub:/config/interface/wg0/> set wireguard private-key wg-hub
admin@hub:/config/interface/wg0/> edit wireguard peers wg-peers
# Spoke 1
admin@hub:/config/interface/…/wireguard/peers/wg-peers/> edit peer spoke1
admin@hub:/config/interface/…/wg-peers/peer/spoke1/> set allowed-ips 10.0.0.2/32
admin@hub:/config/interface/…/wg-peers/peer/spoke1/> set allowed-ips 192.168.1.0/24
admin@hub:/config/interface/…/wg-peers/peer/spoke1/> end
# Spoke 2
admin@hub:/config/interface/…/wireguard/peers/wg-peers/> edit peer spoke2
admin@hub:/config/interface/…/wg-peers/peer/spoke2/> set allowed-ips 10.0.0.3/32
admin@hub:/config/interface/…/wg-peers/peer/spoke2/> set allowed-ips 192.168.2.0/24
admin@hub:/config/interface/…/wg-peers/peer/spoke2/> leave
# Add routes for spoke networks
admin@hub:/> configure
admin@hub:/config/> edit routing control-plane-protocol static name default
admin@hub:/config/routing/…/static/name/default/> set ipv4 route 192.168.1.0/24 wg0
admin@hub:/config/routing/…/static/name/default/> set ipv4 route 192.168.2.0/24 wg0
admin@hub:/config/routing/…/static/name/default/> leave
admin@hub:/>
```
**Spoke 1 configuration:**
```
admin@spoke1:/> configure
admin@spoke1:/config/> edit interface wg0
admin@spoke1:/config/interface/wg0/> set wireguard listen-port 51820
admin@spoke1:/config/interface/wg0/> set wireguard private-key wg-spoke1
admin@spoke1:/config/interface/wg0/> set ipv4 address 10.0.0.2 prefix-length 24
admin@spoke1:/config/interface/wg0/> edit wireguard peers wg-peers peer hub
admin@spoke1:/config/interface/…/wg-peers/peer/hub/> set endpoint 203.0.113.1
admin@spoke1:/config/interface/…/wg-peers/peer/hub/> set endpoint-port 51820
admin@spoke1:/config/interface/…/wg-peers/peer/hub/> set allowed-ips 10.0.0.1/32
admin@spoke1:/config/interface/…/wg-peers/peer/hub/> set allowed-ips 10.0.0.3/32
admin@spoke1:/config/interface/…/wg-peers/peer/hub/> set allowed-ips 192.168.0.0/24
admin@spoke1:/config/interface/…/wg-peers/peer/hub/> set allowed-ips 192.168.2.0/24
admin@spoke1:/config/interface/…/wg-peers/peer/hub/> set persistent-keepalive 25
admin@spoke1:/config/interface/…/wg-peers/peer/hub/> leave
admin@spoke1:/> configure
admin@spoke1:/config/> edit routing control-plane-protocol static name default
admin@spoke1:/config/routing/…/static/name/default/> set ipv4 route 192.168.0.0/24 wg0
admin@spoke1:/config/routing/…/static/name/default/> set ipv4 route 192.168.2.0/24 wg0
admin@spoke1:/config/routing/…/static/name/default/> leave
admin@spoke1:/>
```
This configuration allows Spoke 1 to reach both the hub network (192.168.0.0/24)
and Spoke 2's network (192.168.2.0/24) via the hub, enabling spoke-to-spoke
communication through the central hub.
## Persistent Keepalive
The `persistent-keepalive` setting sends periodic packets to keep the tunnel
active through NAT devices and firewalls:
```
admin@example:/config/interface/…/wg-peers/peer/hub/> set persistent-keepalive 25
```
This is particularly important when:
- The peer is behind NAT
- Intermediate firewalls have connection timeouts
- You need the tunnel to remain ready for bidirectional traffic
A value of 25 seconds is recommended for most scenarios. Omit this setting
for peers with public static IPs that initiate connections.
> [!NOTE]
> Only the peer behind NAT needs `persistent-keepalive` configured. The
> peer with a public IP learns the NAT endpoint from incoming packets.
## IPv6 Endpoints
WireGuard fully supports IPv6 for tunnel endpoints:
```
admin@example:/> configure
admin@example:/config/> edit interface wg0
admin@example:/config/interface/wg0/> set wireguard listen-port 51820
admin@example:/config/interface/wg0/> set wireguard private-key wg-key
admin@example:/config/interface/wg0/> set ipv4 address 10.0.0.1 prefix-length 24
admin@example:/config/interface/wg0/> set ipv6 address fd00::1 prefix-length 64
admin@example:/config/interface/wg0/> edit wireguard peers wg-peers peer remote
admin@example:/config/interface/…/wg-peers/peer/remote/> set endpoint 2001:db8::2
admin@example:/config/interface/…/wg-peers/peer/remote/> set endpoint-port 51820
admin@example:/config/interface/…/wg-peers/peer/remote/> set allowed-ips 10.0.0.2/32
admin@example:/config/interface/…/wg-peers/peer/remote/> set allowed-ips fd00::2/128
admin@example:/config/interface/…/wg-peers/peer/remote/> leave
admin@example:/>
```
WireGuard can carry both IPv4 and IPv6 traffic regardless of whether the
tunnel endpoints use IPv4 or IPv6.
## Monitoring WireGuard Status
Check WireGuard interface status and peer connections:
```
admin@example:/> show interfaces
wg0 wireguard UP 2 peers (1 up)
ipv4 10.0.0.1/24 (static)
ipv6 fd00::1/64 (static)
admin@example:/> show interfaces wg0
name : wg0
type : wireguard
index : 12
operational status : up
peers : 2
Peer 1:
status : UP
endpoint : 203.0.113.2:51820
latest handshake : 2025-12-09T10:23:45+0000
transfer tx : 125648 bytes
transfer rx : 98432 bytes
Peer 2:
status : DOWN
endpoint : 203.0.113.3:51820
latest handshake : 2025-12-09T09:15:22+0000
transfer tx : 45120 bytes
transfer rx : 32768 bytes
```
The connection status shows `UP` if a handshake occurred within the last 3
minutes, indicating an active tunnel. The `latest handshake` timestamp shows
when the peers last successfully authenticated and exchanged keys.
## Post-Quantum Security (Preshared Keys)
WireGuard supports optional preshared keys (PSK) that add an extra layer of
symmetric encryption alongside Curve25519. This provides defense-in-depth
against future quantum computers that might break elliptic curve cryptography.
PSKs protect your data from "harvest now, decrypt later" attacks - adversaries
recording traffic today would still need the PSK even if they break Curve25519
later. However, peer authentication still relies on Curve25519, so PSKs don't
provide complete post-quantum security.
**Generate a preshared key using `wg genpsk`:**
```bash
admin@example:~$ wg genpsk > preshared.key
admin@example:~$ cat preshared.key
cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=
```
**Import the preshared key into the keystore:**
```
admin@example:/> configure
admin@example:/config/> edit keystore symmetric-key wg-psk
admin@example:/config/keystore/symmetric-key/wg-psk/> set key-format wireguard-symmetric-key-format
admin@example:/config/keystore/symmetric-key/wg-psk/> set key cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=
admin@example:/config/keystore/symmetric-key/wg-psk/> end
admin@example:/config/interface/wg0/> edit wireguard peers wg-peers peer remote
admin@example:/config/interface/…/wg-peers/peer/remote/> set preshared-key wg-psk
admin@example:/config/interface/…/wg-peers/peer/remote/> leave
admin@example:/>
```
The preshared key must be securely shared between both peers and configured
on both sides.
> [!IMPORTANT]
> Preshared keys must be kept secret and exchanged through a secure channel,
> just like passwords. Delete the `preshared.key` file after importing it
> into both peer keystores.
+83
View File
@@ -0,0 +1,83 @@
# VPN Configuration
A Virtual Private Network (VPN) creates encrypted tunnels over public networks,
enabling secure communication between remote locations or users. Unlike plain
tunnels (GRE, VXLAN) that only provide encapsulation, VPNs add authentication
and encryption to protect data confidentiality and integrity.
## Configuring VPN
For detailed configuration instructions and examples, see:
- **[WireGuard VPN](vpn-wireguard.md)** - Complete guide to configuring
WireGuard tunnels, including site-to-site, road warrior, and hub-and-spoke
topologies.
## Understanding VPN Tunnels
VPN tunnels establish secure connections across untrusted networks by:
- **Authentication:** Verifying the identity of tunnel endpoints using
cryptographic keys or certificates
- **Encryption:** Protecting data confidentiality with strong ciphers
- **Integrity:** Detecting tampering through message authentication codes
This makes VPNs essential for connecting sites over the internet, enabling
remote access for mobile users, and securing traffic in untrusted environments.
### VPN Deployment Models
VPNs are typically deployed in one of several models:
**Site-to-Site VPN**
![Site-to-Site VPN Topology](img/vpn-site-to-site.svg)
*Figure: Site-to-Site VPN connecting two office networks*
Connects entire networks across locations, creating a unified private network
over the internet. Routers or firewalls at each site maintain persistent
tunnels, allowing seamless access between locations.
- Use case: Connecting branch offices to headquarters
- Characteristics: Always-on, connects networks not individual devices
- Example: Main office (192.168.1.0/24) ↔ Branch office (192.168.2.0/24)
**Remote Access VPN (Road Warrior)**
![Road Warrior VPN Topology](img/vpn-roadwarrior.svg)
*Figure: Mobile clients connecting to corporate network*
Enables individual users to securely access a private network from remote
locations. Clients initiate connections as needed from dynamic IP addresses.
- Use case: Remote employees accessing corporate resources
- Characteristics: On-demand, handles dynamic endpoints and roaming
- Example: Mobile laptop ↔ Corporate network
**Hub-and-Spoke VPN**
![Hub-and-Spoke VPN Topology](img/vpn-hub-spoke.svg)
*Figure: Hub-and-Spoke topology with central hub routing traffic between spokes*
A central hub connects to multiple remote sites (spokes), routing traffic
between them. Spokes don't connect directly to each other but communicate
through the hub.
- Use case: Central office connecting multiple remote locations
- Characteristics: Centralized control, simplified management
- Example: HQ ↔ (Branch A, Branch B, Branch C)
### VPN Protocol Comparison
Different VPN protocols offer varying trade-offs between security, performance,
and complexity:
| Protocol | Complexity | Performance | Use Case |
|------------|------------|-------------|---------------------------------|
| WireGuard | Simple | Very High | Modern deployments, all models |
| IPsec | Complex | High | Legacy systems, compliance reqs |
| OpenVPN | Moderate | Moderate | Maximum compatibility |
Infix supports WireGuard as its primary VPN solution, offering the best
balance of simplicity, security, and performance for modern networks.
+1
View File
@@ -33,6 +33,7 @@ nav:
- Quality of Service: qos.md
- RMON Counters: eth-counters.md
- Tunneling (L2/L3): tunnels.md
- VPN Tunnels: vpn.md
- Wireless LAN (WiFi): wifi.md
- Services:
- Device Discovery: discovery.md
+1
View File
@@ -38,6 +38,7 @@ confd_plugin_la_SOURCES = \
if-gre.c \
if-vxlan.c \
if-wifi.c \
if-wireguard.c \
keystore.c \
system.c \
ntp.c \
+161
View File
@@ -0,0 +1,161 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <srx/lyx.h>
#include "interfaces.h"
#define WIREGUARD_CONFIG "/run/wireguard-%s.conf"
/* Helper to get a peer setting with override logic:
* 1. Check peer-specific override
* 2. Fall back to key-bag level default
*/
static const char *get_peer_setting(struct lyd_node *peer_override, struct lyd_node *bag_peer,
const char *setting_name)
{
const char *value = NULL;
if (peer_override)
value = lydx_get_cattr(peer_override, setting_name);
if (!value)
value = lydx_get_cattr(bag_peer, setting_name);
return value;
}
static void write_allowed_ips(FILE *wg_fp, struct lyd_node *peer_override, struct lyd_node *bag_peer)
{
struct lyd_node *settings_node, *allowed_ip, *check_ip;
int first = 1;
/* Determine which node has the allowed-ips to use:
* If peer override exists and has any allowed-ips, use those.
* Otherwise use bag-level allowed-ips.
*/
settings_node = bag_peer; /* Default to bag level */
if (peer_override && lyd_child(peer_override)) {
LYX_LIST_FOR_EACH(lyd_child(peer_override), check_ip, "allowed-ips") {
settings_node = peer_override; /* Override has IPs, use them */
break;
}
}
LYX_LIST_FOR_EACH(lyd_child(settings_node), allowed_ip, "allowed-ips") {
const char *ip_prefix = lyd_get_value(allowed_ip);
if (ip_prefix) {
fprintf(wg_fp, "%s%s", first ? "AllowedIPs = " : ", ", ip_prefix);
first = 0;
}
}
if (!first)
fprintf(wg_fp, "\n");
}
static void write_peer(FILE *wg_fp, struct lyd_node *cif, struct lyd_node *bag_peer,
struct lyd_node *peer_override, const char *public_key_data)
{
const char *preshared_key_ref, *preshared_key_data;
const char *endpoint, *endpoint_port, *keepalive;
struct lyd_node *psk_node;
fprintf(wg_fp, "\n[Peer]\n");
fprintf(wg_fp, "PublicKey = %s\n", public_key_data);
preshared_key_ref = get_peer_setting(peer_override, bag_peer, "preshared-key");
endpoint = get_peer_setting(peer_override, bag_peer, "endpoint");
endpoint_port = get_peer_setting(peer_override, bag_peer, "endpoint-port");
keepalive = get_peer_setting(peer_override, bag_peer, "persistent-keepalive");
if (preshared_key_ref) {
psk_node = lydx_get_xpathf(cif, "../../keystore/symmetric-keys/symmetric-key[name='%s']",
preshared_key_ref);
if (psk_node) {
preshared_key_data = lydx_get_cattr(psk_node, "cleartext-symmetric-key");
if (preshared_key_data)
fprintf(wg_fp, "PresharedKey = %s\n", preshared_key_data);
}
}
if (endpoint) {
if (!endpoint_port)
endpoint_port = "51820"; /* Default port */
fprintf(wg_fp, "Endpoint = %s:%s\n", endpoint, endpoint_port);
}
write_allowed_ips(wg_fp, peer_override, bag_peer);
if (keepalive)
fprintf(wg_fp, "PersistentKeepalive = %s\n", keepalive);
}
int wireguard_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, struct dagger *net)
{
const char *ifname, *listen_port, *private_key_ref, *private_key_data;
struct lyd_node *wg, *key_node, *bag_peer, *pub_key_bag;
FILE *wg_fp = NULL;
FILE *wg_sh = NULL;
mode_t old_umask;
ifname = lydx_get_cattr(cif, "name");
wg = lydx_get_child(cif, "wireguard");
if (!wg)
return -EINVAL;
listen_port = lydx_get_cattr(wg, "listen-port");
private_key_ref = lydx_get_cattr(wg, "private-key");
key_node = lydx_get_xpathf(cif, "../../keystore/asymmetric-keys/asymmetric-key[name='%s']",
private_key_ref);
private_key_data = lydx_get_cattr(key_node, "cleartext-private-key");
fprintf(ip, "link add dev %s type wireguard\n", ifname);
/* Set umask to create config file with limited permissions (0600) */
old_umask = umask(0177);
wg_fp = fopenf("w", WIREGUARD_CONFIG, ifname);
umask(old_umask);
if (!wg_fp)
return -errno;
fprintf(wg_fp, "[Interface]\n");
fprintf(wg_fp, "PrivateKey = %s\n", private_key_data);
fprintf(wg_fp, "ListenPort = %s\n", listen_port);
LYX_LIST_FOR_EACH(lyd_child(wg), bag_peer, "peers") {
const char *public_key_bag_ref = lydx_get_cattr(bag_peer, "public-key-bag");
pub_key_bag = lydx_get_xpathf(cif, "../../truststore/public-key-bags/public-key-bag[name='%s']",
public_key_bag_ref);
if (pub_key_bag) {
struct lyd_node *pub_key, *peer_override;
const char *public_key_name, *public_key_data;
LYX_LIST_FOR_EACH(lyd_child(pub_key_bag), pub_key, "public-key") {
public_key_name = lydx_get_cattr(pub_key, "name");
public_key_data = lydx_get_cattr(pub_key, "public-key");
if (!public_key_data)
continue;
/* Check if there's a peer-specific override for this key */
peer_override = lydx_get_xpathf(bag_peer, "peer[public-key='%s']", public_key_name);
write_peer(wg_fp, cif, bag_peer, peer_override, public_key_data);
}
}
}
fclose(wg_fp);
/* Create activation script */
wg_sh = dagger_fopen_net_init(net, ifname, NETDAG_INIT_POST, "enable-wireguard.sh");
fprintf(wg_sh, "wg setconf %s ", ifname);
fprintf(wg_sh, WIREGUARD_CONFIG, ifname);
fprintf(wg_sh, "\n");
fclose(wg_sh);
return 0;
}
+9
View File
@@ -108,6 +108,8 @@ static int ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
inferred.data.string_val = "infix-if-type:gretap";
else if (!fnmatch("vxlan+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:vxlan";
else if (!fnmatch("wg+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:wireguard";
free(ifname);
@@ -419,6 +421,8 @@ static int netdag_gen_afspec_add(sr_session_ctx_t *session, struct dagger *net,
return vxlan_gen(NULL, cif, ip);
case IFT_WIFI:
return wifi_add_iface(cif, net);
case IFT_WIREGUARD:
return wireguard_gen(NULL, cif, ip, net);
case IFT_ETH:
return netdag_gen_ethtool(net, cif, dif);
case IFT_LO:
@@ -457,6 +461,7 @@ static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net,
case IFT_GRETAP:
case IFT_VETH:
case IFT_VXLAN:
case IFT_WIREGUARD:
case IFT_LO:
return 0;
@@ -494,6 +499,8 @@ static bool netdag_must_del(struct lyd_node *dif, struct lyd_node *cif)
return lydx_get_descendant(lyd_child(dif), "veth", NULL);
case IFT_VXLAN:
return lydx_get_descendant(lyd_child(dif), "vxlan", NULL);
case IFT_WIREGUARD:
return lydx_get_descendant(lyd_child(dif), "wireguard", NULL);
case IFT_UNKNOWN:
ERR_IFACE(cif, -EINVAL, "unsupported interface type \"%s\"",
lydx_get_cattr(cif, "type"));
@@ -579,6 +586,7 @@ static int netdag_gen_iface_del(struct dagger *net, struct lyd_node *dif,
case IFT_LAG:
case IFT_VLAN:
case IFT_VXLAN:
case IFT_WIREGUARD:
case IFT_UNKNOWN:
link_gen_del(dif, ip);
break;
@@ -744,6 +752,7 @@ static int netdag_init_iface(struct lyd_node *cif)
case IFT_GRETAP:
case IFT_LO:
case IFT_VXLAN:
case IFT_WIREGUARD:
case IFT_UNKNOWN:
break;
}
+4
View File
@@ -33,6 +33,7 @@
_map(IFT_VLAN, "infix-if-type:vlan") \
_map(IFT_VXLAN, "infix-if-type:vxlan") \
_map(IFT_WIFI, "infix-if-type:wifi") \
_map(IFT_WIREGUARD,"infix-if-type:wireguard") \
/* */
enum iftype {
@@ -152,4 +153,7 @@ int ifchange_cand_infer_dhcp(sr_session_ctx_t *session, const char *path);
/* if-vxlan.c */
int vxlan_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
/* infix-if-wireguard */
int wireguard_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, struct dagger *net);
#endif /* CONFD_INTERFACES_H_ */
+3 -3
View File
@@ -30,7 +30,7 @@ MODULES=(
"infix-hardware@2025-12-04.yang"
"ieee802-dot1q-types@2022-10-29.yang"
"infix-ip@2025-11-02.yang"
"infix-if-type@2025-02-12.yang"
"infix-if-type@2026-01-07.yang"
"infix-routing@2025-12-02.yang"
"ieee802-dot1ab-lldp@2022-03-15.yang"
"infix-lldp@2025-05-05.yang"
@@ -49,8 +49,8 @@ MODULES=(
"infix-factory-default@2023-06-28.yang"
"infix-interfaces@2025-11-06.yang -e vlan-filtering"
"ietf-crypto-types -e cleartext-symmetric-keys"
"infix-crypto-types@2025-06-17.yang"
"infix-crypto-types@2025-11-09.yang"
"ietf-keystore -e symmetric-keys"
"infix-keystore@2025-12-10.yang"
"infix-ntp@2025-12-03.yang"
"infix-keystore@2025-12-17.yang"
)
@@ -6,6 +6,9 @@ module infix-crypto-types {
prefix ct;
}
revision 2025-11-09 {
description "Add Wireguard public/private key and sha";
}
revision 2025-06-17 {
description "Add Wi-Fi secret support.";
}
@@ -28,6 +31,7 @@ module infix-crypto-types {
base public-key-format;
base ct:ssh-public-key-format;
}
identity symmetric-key-format {
description
"Base for symmetric key format";
@@ -38,4 +42,29 @@ module infix-crypto-types {
description
"WiFi secret key";
}
identity x25519-public-key-format {
base public-key-format;
base ct:public-key-format;
description
"X25519 (Curve25519) public key format for Diffie-Hellman key exchange.
This is the format used by WireGuard.";
}
identity x25519-private-key-format {
base private-key-format;
base ct:private-key-format;
description
"X25519 (Curve25519) private key format for Diffie-Hellman key exchange.
This is the format used by WireGuard.";
}
identity wireguard-symmetric-key-format {
base ct:symmetric-key-format;
base symmetric-key-format;
description
"WireGuard pre-shared key format.
32-byte base64-encoded key used as an optional additional layer
of symmetric encryption for post-quantum resistance.";
}
}
+8
View File
@@ -11,6 +11,11 @@ module infix-if-type {
contact "kernelkit@googlegroups.com";
description "Infix extensions to IANA interfaces types";
revision 2026-01-07 {
description "Add interface type wifi and wireguard";
reference "internal";
}
revision 2025-02-12 {
description "Remove interface type etherlike.";
reference "internal";
@@ -81,6 +86,9 @@ module infix-if-type {
identity vxlan {
base infix-interface-type;
}
identity wireguard {
base infix-interface-type;
}
identity lag {
base infix-interface-type;
base ianaift:ieee8023adLag;
@@ -0,0 +1,309 @@
submodule infix-if-wireguard {
yang-version 1.1;
belongs-to infix-interfaces {
prefix infix-if;
}
import ietf-interfaces {
prefix if;
}
import ietf-inet-types {
prefix inet;
}
import infix-crypto-types {
prefix ixct;
}
import ietf-keystore {
prefix ks;
}
import infix-keystore {
prefix infix-ks;
}
import ietf-truststore {
prefix ts;
}
import infix-if-type {
prefix infixift;
}
import ietf-yang-types {
prefix yang;
}
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "WireGuard VPN tunnel interface";
revision 2025-11-09 {
description "Initial revision";
reference "internal";
}
typedef port {
type inet:port-number;
description
"WireGuard UDP port. Valid range: 0..65535.";
}
typedef keepalive-interval {
type uint16 {
range "1..65535";
}
units "seconds";
description
"Persistent keepalive interval in seconds.
A keepalive packet will be sent to the peer endpoint at this
interval if no traffic has been exchanged. This is useful for
traversing NAT and firewalls that may close the UDP connection
after a period of inactivity.
Recommended value is 25 seconds for peers behind NAT.";
}
grouping peer-settings {
description
"Common WireGuard peer configuration settings.
These settings can be applied at the key-bag level (affecting all
keys in the bag) or at individual peer level (overriding key-bag
settings for specific keys).";
leaf preshared-key {
type ks:central-symmetric-key-ref;
description
"Optional preshared key for additional layer of symmetric encryption.
This provides post-quantum resistance as an attacker would need
to break both the Curve25519 key exchange and this symmetric key.";
must "derived-from-or-self(deref(.)/../ks:key-format, 'ixct:wireguard-symmetric-key-format')" {
error-message "Preshared key must be in wireguard-symmetric-key-format";
}
}
leaf endpoint {
type inet:host;
description
"Peer endpoint address (IP address or DNS hostname).
If not specified, this peer can only be a responder and cannot
initiate connections. WireGuard will learn the endpoint from
incoming authenticated packets.";
}
leaf endpoint-port {
type port;
description "Peer endpoint UDP port";
}
leaf-list allowed-ips {
type inet:ip-prefix;
description
"List of IP address ranges (in CIDR notation) that are allowed
to be used as source addresses inside the tunnel from this peer.
This also controls which destination addresses will be routed
to this peer. For example:
- '10.0.0.2/32' allows only this single IP
- '10.0.0.0/24' allows the entire subnet
- '0.0.0.0/0, ::/0' routes all traffic through this peer
WireGuard uses this as a cryptographic routing table.
IMPORTANT: Allowed-IPs must be unique across all peers. If
multiple peers have overlapping allowed-ips, only the last
peer will be used for routing. When configuring at the
key-bag level, only use allowed-ips if all peers genuinely
need identical routing (rare). For typical hub-and-spoke
scenarios, use per-peer configuration with unique allowed-ips
for each client (e.g., 10.0.0.2/32, 10.0.0.3/32, etc.).";
}
leaf persistent-keepalive {
type keepalive-interval;
description
"Interval in seconds to send keepalive packets to this peer.
If not specified (or set to 0), keepalive is disabled.
Use this when the peer is behind NAT or a firewall that may
close the UDP connection after inactivity.";
}
}
augment "/if:interfaces/if:interface" {
when "derived-from-or-self(if:type, 'infixift:wireguard')" {
description "Only shown for if:type infixift:wireguard";
}
container wireguard {
description "WireGuard VPN configuration";
leaf listen-port {
type port;
default 51820;
description "Local UDP port to listen on for incoming WireGuard traffic";
}
leaf private-key {
type ks:central-asymmetric-key-ref;
mandatory true;
description "Reference to WireGuard private key (X25519/Curve25519 format)";
must "not(deref(.)/ks:public-key-format) or "
+ "(derived-from-or-self(deref(.)/ks:public-key-format, 'ixct:x25519-public-key-format') and "
+ "derived-from-or-self(deref(.)/ks:private-key-format, 'ixct:x25519-private-key-format'))" {
error-message "Private key must be in WireGuard (X25519/Curve25519) format";
}
}
list peers {
key "public-key-bag";
description
"Peers from a public key bag with common settings.
If the bag contains a single key, bag-level settings (endpoint,
allowed-ips, etc.) apply to that peer. If the bag contains multiple
keys, individual peer configuration is required for each key - you
must provide a 'peer' entry for each public key in the bag with
unique settings like endpoint and allowed-ips.";
must "count(../../../../ts:truststore/ts:public-key-bags/ts:public-key-bag[ts:name=current()/public-key-bag]/ts:public-key) = 1 or "
+ "count(./peer) = count(../../../../ts:truststore/ts:public-key-bags/ts:public-key-bag[ts:name=current()/public-key-bag]/ts:public-key)" {
error-message "When a key-bag contains multiple keys, individual peer "
+ "configuration is required for each key. Either use a "
+ "key-bag with a single key for shared settings, or provide "
+ "a 'peer' entry for each key in the bag with unique settings.";
}
leaf public-key-bag {
type ts:central-public-key-bag-ref;
must "not(deref(.)/ts:public-key-format) or "
+ "(derived-from-or-self(deref(.)/ts:public-key-format, 'ixct:x25519-public-key-format'))" {
error-message "Public key must be in WireGuard (X25519/Curve25519) format";
}
description "Reference to public key bag containing peer public keys";
}
uses peer-settings {
refine endpoint-port {
default 51820;
}
}
list peer {
key "public-key";
description
"Individual peer with override settings.
Settings specified here override the key-bag level settings
for this specific peer.";
leaf public-key {
type leafref {
path "../../../../../../ts:truststore/ts:public-key-bags/" +
"ts:public-key-bag[ts:name=current()/../../public-key-bag]/" +
"ts:public-key/ts:name";
}
description "Name of the peer's public key within the referenced key-bag";
}
uses peer-settings;
}
}
container peer-status {
config false;
description "Operational state for WireGuard peers";
list peer {
key "public-key";
description "Per-peer operational statistics and status";
leaf public-key {
type string {
pattern '[A-Za-z0-9+/]{43}=';
}
description
"WireGuard public key of the peer in base64 encoding.
This is the actual key value, not a keystore reference.";
}
leaf latest-handshake {
type yang:date-and-time;
description
"Timestamp of the most recent successful handshake with this peer.
If no handshake has occurred yet, this leaf will not be present.
A successful handshake indicates that the peer is authenticated
and a secure session has been established.";
}
leaf endpoint-address {
type inet:ip-address;
description
"The actual IP address from which packets were last received
from this peer.
This may differ from the configured endpoint if:
- The peer is roaming (changed IP address)
- The configured endpoint is a DNS hostname
- No endpoint was configured (learned from incoming packets)
If no packets have been received, this leaf will not be present.";
}
leaf endpoint-port {
type port;
description
"The actual UDP port from which packets were last received
from this peer.
If no packets have been received, this leaf will not be present.";
}
container transfer {
description "Data transfer statistics for this peer";
leaf tx-bytes {
type yang:counter64;
units "bytes";
description
"Total number of bytes transmitted (sent) to this peer.
This counts encrypted payload bytes sent through the tunnel.";
}
leaf rx-bytes {
type yang:counter64;
units "bytes";
description
"Total number of bytes received from this peer.
This counts decrypted payload bytes received through the tunnel.";
}
}
leaf connection-status {
type enumeration {
enum down {
description
"No handshake has occurred, or the last handshake is too old.
The peer is not considered connected.";
}
enum up {
description
"A recent handshake has occurred and the connection is active.
Typically means a handshake within the last 2-3 minutes.";
}
}
description
"Current connection status with this peer.
This is derived from the latest-handshake timestamp and indicates
whether the tunnel is currently operational.";
}
}
}
}
}
}
@@ -0,0 +1 @@
infix-if-wireguard.yang
@@ -34,6 +34,7 @@ module infix-interfaces {
include infix-if-gre;
include infix-if-vxlan;
include infix-if-wifi;
include infix-if-wireguard;
organization "KernelKit";
contact "kernelkit@googlegroups.com";
+14 -10
View File
@@ -12,6 +12,9 @@ module infix-keystore {
prefix infix-ct;
}
revision 2025-12-17 {
description "Add WireGuard support";
}
revision 2025-12-10 {
description "Adapt to changes in final version of ietf-keystore";
}
@@ -36,18 +39,10 @@ module infix-keystore {
}
}
deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-format" {
deviate not-supported;
}
augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key" {
leaf key-format {
deviate replace {
type identityref {
base infix-ct:symmetric-key-format;
}
description
"Identifies the symmetric key's format
Valid symmetric key formats are:
wifi-preshared-key-format - WiFi preshared key";
}
}
deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type/ks:cleartext-symmetric-key" {
@@ -61,7 +56,16 @@ module infix-keystore {
"(string-length(.) >= 8 and string-length(.) <= 63)" {
error-message "WiFi pre-shared key must be 8-63 characters long";
}
description "WiFi pre-shared key: 8-63 printable ASCII characters";
must "../infix-ks:key-format != 'infix-ct:wireguard-symmetric-key-format' or " +
"string-length(.) = 44" {
error-message "WireGuard pre-shared key must be 44 characters (32-byte base64-encoded)";
}
description
"Cleartext symmetric key value.
Format depends on key-format:
- WiFi pre-shared key: 8-63 printable ASCII characters
- WireGuard pre-shared key: 32-byte base64-encoded key (44 chars with padding)";
}
}
+71 -3
View File
@@ -1114,6 +1114,7 @@ class Iface:
self.gre = self.data.get('infix-interfaces:gre')
self.vxlan = self.data.get('infix-interfaces:vxlan')
self.wifi = self.data.get('infix-interfaces:wifi')
self.wireguard = self.data.get('infix-interfaces:wireguard')
if self.data.get('infix-interfaces:vlan'):
self.lower_if = self.data.get('infix-interfaces:vlan', None).get('lower-layer-if',None)
@@ -1148,6 +1149,9 @@ class Iface:
def is_gretap(self):
return self.data['type'] == "infix-if-type:gretap"
def is_wireguard(self):
return self.data['type'] == "infix-if-type:wireguard"
def oper(self, detail=False):
"""Remap in brief overview to fit column widths."""
if not detail and self.oper_status == "lower-layer-down":
@@ -1219,6 +1223,23 @@ class Iface:
row = self._pr_proto_common("vxlan", True, pipe);
print(row)
def pr_proto_wireguard(self, pipe=''):
row = self._pr_proto_common("wireguard", False, pipe)
if self.wireguard:
peer_status = self.wireguard.get('peer-status', {})
peers = peer_status.get('peer', [])
total_peers = len(peers)
up_peers = sum(1 for p in peers if p.get('connection-status') == 'up')
if total_peers > 0:
row += f"{total_peers} peer"
if total_peers != 1:
row += "s"
row += f" ({up_peers} up)"
print(row)
def pr_proto_loopack(self, pipe=''):
row = self._pr_proto_common("loopback", False, pipe);
print(row)
@@ -1471,6 +1492,12 @@ class Iface:
self.pr_proto_ipv4()
self.pr_proto_ipv6()
def pr_wireguard(self):
self.pr_name(pipe="")
self.pr_proto_wireguard()
self.pr_proto_ipv4()
self.pr_proto_ipv6()
def pr_wifi(self):
self.pr_name(pipe="")
self.pr_proto_wifi()
@@ -1598,9 +1625,6 @@ class Iface:
print(f"{'in-octets':<{20}}: {self.in_octets}")
print(f"{'out-octets':<{20}}: {self.out_octets}")
frame = get_json_data([], self.data,'ieee802-ethernet-interface:ethernet',
'statistics', 'frame')
if self.wifi:
# Detect mode: AP has "stations", Station has "rssi" or "scan-results"
ap = self.wifi.get('access-point')
@@ -1635,6 +1659,46 @@ class Iface:
print(f"{'remote address':<{20}}: {self.vxlan['remote']}")
print(f"{'VxLAN id':<{20}}: {self.vxlan['vni']}")
if self.wireguard:
peer_status = self.wireguard.get('peer-status', {})
peers = peer_status.get('peer', [])
if peers:
print(f"{'peers':<{20}}: {len(peers)}")
for idx, peer in enumerate(peers, 1):
print(f"\n Peer {idx}:")
# Public key (always 44 chars: 43 + '=')
if pubkey := peer.get('public-key'):
print(f" {'public key':<{18}}: {pubkey}")
# Connection status with color
status = peer.get('connection-status', 'unknown')
if status == 'up':
status_str = Decore.green(status.upper())
else:
status_str = Decore.red(status.upper())
print(f" {'status':<{18}}: {status_str}")
# Endpoint information
if endpoint := peer.get('endpoint-address'):
port = peer.get('endpoint-port', '')
endpoint_str = f"{endpoint}:{port}" if port else endpoint
print(f" {'endpoint':<{18}}: {endpoint_str}")
# Latest handshake
if handshake := peer.get('latest-handshake'):
print(f" {'latest handshake':<{18}}: {handshake}")
# Transfer statistics
if transfer := peer.get('transfer'):
tx = transfer.get('tx-bytes', '0')
rx = transfer.get('rx-bytes', '0')
print(f" {'transfer tx':<{18}}: {tx} bytes")
print(f" {'transfer rx':<{18}}: {rx} bytes")
frame = get_json_data([], self.data,'ieee802-ethernet-interface:ethernet',
'statistics', 'frame')
if frame:
print("")
for key, val in frame.items():
@@ -1799,6 +1863,10 @@ def pr_interface_list(json):
iface.pr_vxlan()
continue
if iface.is_wireguard():
iface.pr_wireguard()
continue
if iface.is_wifi():
iface.pr_wifi()
continue
@@ -9,6 +9,7 @@ from . import tun
from . import veth
from . import vlan
from . import wifi
from . import wireguard
def statistics(iplink):
@@ -27,6 +28,7 @@ def statistics(iplink):
def iplink2yang_type(iplink):
ifname=iplink["ifname"]
match iplink["link_type"]:
case "loopback":
return "infix-if-type:loopback"
@@ -36,6 +38,8 @@ def iplink2yang_type(iplink):
data = HOST.run(tuple(f"ls /sys/class/net/{ifname}/wireless/".split()), default="no")
if data != "no":
return "infix-if-type:wifi"
case "none":
pass # WireGuard interfaces is for some reason link_type none
case _:
return "infix-if-type:other"
@@ -54,6 +58,8 @@ def iplink2yang_type(iplink):
return "infix-if-type:veth"
case "vlan":
return "infix-if-type:vlan"
case "wireguard":
return "infix-if-type:wireguard"
return "infix-if-type:ethernet"
@@ -141,6 +147,9 @@ def interface(iplink, ipaddr):
case "infix-if-type:wifi":
if w := wifi.wifi(iplink["ifname"]):
interface["infix-interfaces:wifi"] = w
case "infix-if-type:wireguard":
if wg := wireguard.wireguard(iplink):
interface["infix-interfaces:wireguard"] = wg
match iplink2yang_lower(iplink):
case "infix-interfaces:bridge-port":
@@ -0,0 +1,127 @@
import subprocess
import json
from datetime import datetime, timezone
from ..host import HOST
def _parse_wg_show(ifname):
"""Parse `wg show <ifname> dump` output into structured data"""
try:
result = HOST.run(("wg", "show", ifname, "dump"), default="")
if not result:
return None
lines = result.strip().split('\n')
if len(lines) < 2: # Need at least interface line + one peer
return None
peers = []
# Skip first line (interface info), process peer lines
for line in lines[1:]:
parts = line.split('\t')
if len(parts) < 8:
continue
public_key, preshared_key, endpoint, allowed_ips, \
latest_handshake, rx_bytes, tx_bytes, persistent_keepalive = parts
peer = {
"public_key": public_key,
"endpoint": endpoint if endpoint != "(none)" else None,
"allowed_ips": allowed_ips.split(',') if allowed_ips else [],
"latest_handshake": int(latest_handshake) if latest_handshake != "0" else None,
"rx_bytes": int(rx_bytes),
"tx_bytes": int(tx_bytes),
}
peers.append(peer)
return peers
except Exception:
return None
def _format_timestamp(epoch_seconds):
"""Convert Unix timestamp to YANG date-and-time format"""
if not epoch_seconds:
return None
dt = datetime.fromtimestamp(epoch_seconds, tz=timezone.utc)
# YANG date-and-time requires timezone with colon: +00:00 not +0000
timestamp = dt.strftime("%Y-%m-%dT%H:%M:%S%z")
# Insert colon in timezone offset: +0000 -> +00:00
return timestamp[:-2] + ':' + timestamp[-2:]
def _parse_endpoint(endpoint_str):
"""Parse endpoint string like '192.168.1.1:51820' or '[2001:db8::1]:51820'"""
if not endpoint_str or endpoint_str == "(none)":
return None, None
# Handle IPv6 with brackets
if endpoint_str.startswith('['):
addr_end = endpoint_str.find(']')
if addr_end == -1:
return None, None
addr = endpoint_str[1:addr_end]
port_part = endpoint_str[addr_end+1:]
port = int(port_part.lstrip(':')) if ':' in port_part else None
return addr, port
# Handle IPv4
parts = endpoint_str.rsplit(':', 1)
if len(parts) == 2:
return parts[0], int(parts[1])
return parts[0], None
def _connection_status(latest_handshake_epoch):
"""Determine connection status based on handshake time"""
if not latest_handshake_epoch:
return "down"
# Consider connection up if handshake within last 3 minutes
age = datetime.now(timezone.utc).timestamp() - latest_handshake_epoch
return "up" if age < 180 else "down"
def wireguard(iplink):
"""Get WireGuard operational state data"""
ifname = iplink.get("ifname")
if not ifname:
return None
peers_data = _parse_wg_show(ifname)
if not peers_data:
return None
peers = []
for peer_data in peers_data:
peer = {
"public-key": peer_data["public_key"]
}
# Connection status (always include)
if peer_data["latest_handshake"]:
peer["latest-handshake"] = _format_timestamp(peer_data["latest_handshake"])
peer["connection-status"] = _connection_status(peer_data["latest_handshake"])
else:
peer["connection-status"] = "down"
# Parse endpoint
if peer_data["endpoint"]:
addr, port = _parse_endpoint(peer_data["endpoint"])
if addr:
peer["endpoint-address"] = addr
if port:
peer["endpoint-port"] = port
# Transfer statistics
if peer_data["tx_bytes"] or peer_data["rx_bytes"]:
peer["transfer"] = {
"tx-bytes": str(peer_data["tx_bytes"]),
"rx-bytes": str(peer_data["rx_bytes"]),
}
peers.append(peer)
return {"peer-status": {"peer": peers}} if peers else None
+9
View File
@@ -7,3 +7,12 @@
- name: Tunnel TTL verification
suite: tunnel_ttl/test.yaml
- name: WireGuard Point-to-Point
case: wireguard_p2p/test.py
- name: WireGuard multipoint
case: wireguard_multipoint/test.py
- name: WireGuard Roadwarrior
case: wireguard_roadwarrior/test.py
@@ -0,0 +1 @@
test.adoc
@@ -0,0 +1,57 @@
=== WireGuard multipoint
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/interfaces/wireguard_multipoint]
==== Description
Set up a WireGuard hub-and-spoke topology with one server (hub) and two
clients (spokes). The server acts as a central point through which clients
can communicate. Host namespaces are connected behind the server and client1
to test routing through the WireGuard mesh.
This test verifies:
- WireGuard hub-and-spoke topology with multiple peers
- Mixed IPv4/IPv6 tunnel endpoints (client1 uses IPv4, client2 uses IPv6)
- Dual-stack WireGuard tunnels carrying both IPv4 and IPv6 traffic
- Advanced key management with preshared keys for post-quantum resistance
- Persistent keepalive configuration for NAT traversal
- Different listen ports on server and clients
- Multiple allowed-ips per peer for routing multiple subnets
- Static routes for subnet reachability through WireGuard
- Security boundaries enforced by allowed-ips (client2 isolated from server subnet)
- IPv4 and IPv6 connectivity through the encrypted tunnel mesh
- Proper routing between all nodes in the WireGuard network
WireGuard hub-and-spoke:
....
server:wg0 (10.0.0.1, fd00:0::1)
|
+----------------+----------------+
| |
client1:wg0 client2:wg0
(10.0.0.2, fd00:0::2) (10.0.0.3, fd00:0::3)
via IPv4 endpoint via IPv6 endpoint
192.168.10.x 2001:db8:3c4d:20::x
....
Security boundaries:
- host:data1 can reach all WireGuard IPs (10.0.0.1, .2, .3 and fd00:0::1, ::2, ::3)
- host:data2 can reach server and client1 WireGuard IPs, but NOT client2 (blocked by allowed-ips)
==== Topology
image::topology.svg[WireGuard multipoint topology, align=center, scaledwidth=75%]
==== Sequence
. Set up topology and attach to target DUTs
. Configure server, client1 and client2
. Verify IPv4 connectivity with ping 10.0.0.1, 10.0.0.2 and 10.0.0.3 from host:data1
. Verify IPv4 connectivity with ping 10.0.0.1 and 10.0.0.2 from host:data2
. Verify host:data2 can not ping 10.0.0.3
. Verify IPv6 connectivity with ping fd00:0::1, fd00:0::2 and fd00:0::3 from host:data1
. Verify IPv6 connectivity with ping fd00:0:1 and fd00:0:2 from host:data2
. Verify host:data2 can not ping fd00:0::3
+512
View File
@@ -0,0 +1,512 @@
#!/usr/bin/env python3
r"""
Advanced WireGuard multipoint hub-and-spoke tunnel test
Set up a WireGuard hub-and-spoke topology with one server (hub) and two
clients (spokes). The server acts as a central point through which clients
can communicate. Host namespaces are connected behind the server and client1
to test routing through the WireGuard mesh.
This test verifies:
- WireGuard hub-and-spoke topology with multiple peers
- Mixed IPv4/IPv6 tunnel endpoints (client1 uses IPv4, client2 uses IPv6)
- Dual-stack WireGuard tunnels carrying both IPv4 and IPv6 traffic
- Advanced key management with preshared keys for post-quantum resistance
- Persistent keepalive configuration for NAT traversal
- Different listen ports on server and clients
- Multiple allowed-ips per peer for routing multiple subnets
- Static routes for subnet reachability through WireGuard
- Security boundaries enforced by allowed-ips (client2 isolated from server subnet)
- IPv4 and IPv6 connectivity through the encrypted tunnel mesh
- Proper routing between all nodes in the WireGuard network
WireGuard hub-and-spoke:
....
server:wg0 (10.0.0.1, fd00:0::1)
|
+----------------+----------------+
| |
client1:wg0 client2:wg0
(10.0.0.2, fd00:0::2) (10.0.0.3, fd00:0::3)
via IPv4 endpoint via IPv6 endpoint
192.168.10.x 2001:db8:3c4d:20::x
....
Security boundaries:
- host:data1 can reach all WireGuard IPs (10.0.0.1, .2, .3 and fd00:0::1, ::2, ::3)
- host:data2 can reach server and client1 WireGuard IPs, but NOT client2 (blocked by allowed-ips)
"""
import infamy
import infamy.util as util
# Server keys
server_private_key = "uIUL4AnD5QaVrwHDPHJzQ7sIQ+Q3zDdflnvfd59qa28="
server_public_key = "qGVmu5UbNtMuZs2t9wFoOoHlvgmV+A1SyQacVb/bEV0="
# Client1 keys
client1_private_key = "kNmkNlSkSh9+Va2tmFv9Va8TBCZlTBF0fKAGJf8vomo="
client1_public_key = "ROaZyvJc5DzA2XUAAeTj2YlwDsy2w0lr3t+rWj2imAk="
# Client2 keys
client2_private_key = "OPT7v/l5zICEmFIrO0U+YwA+w07l8Xo2Dp38hjGOHGY="
client2_public_key = "Om9CPLYdK3l93GauKrq5WXo/gbcD+1CeqFpobRLLkB4="
# Preshared keys (256-bit symmetric keys, base64-encoded)
psk_client1 = "zYr83O4Ykj9i1gN+/aaosJxQxCzvXv1EYOj0MX9H2K4="
psk_client2 = "A4Gf6KCp+CL+tH2TUd9cyARpBZAH8e+9QXiPJ0t+4So="
def configure_server(dut):
dut.put_config_dicts({
"ietf-keystore": {
"keystore": {
"asymmetric-keys": {
"asymmetric-key": [{
"name": "server-wg-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": server_public_key,
"private-key-format": "infix-crypto-types:x25519-private-key-format",
"cleartext-private-key": server_private_key
}]
},
"symmetric-keys": {
"symmetric-key": [{
"name": "psk-client1",
"infix-keystore:symmetric-key": psk_client1,
"key-format": "infix-crypto-types:wireguard-symmetric-key-format"
}, {
"name": "psk-client2",
"infix-keystore:symmetric-key": psk_client2,
"key-format": "infix-crypto-types:wireguard-symmetric-key-format"
}]
}
}
},
"ietf-truststore": {
"truststore": {
"public-key-bags": {
"public-key-bag": [{
"name": "wireguard-clients",
"public-key": [{
"name": "client1-wg-pubkey",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": client1_public_key
}, {
"name": "client2-wg-pubkey",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": client2_public_key
}]
}]
}
}
},
"ietf-interfaces": {
"interfaces": {
"interface": [{
"name": server["link1"],
"ipv4": {
"address": [{
"ip": "192.168.10.1",
"prefix-length": 24
}]
}
}, {
"name": server["link2"],
"ipv6": {
"address": [{
"ip": "2001:db8:3c4d:20::1",
"prefix-length": 64
}]
}
}, {
"name": server["data"],
"ipv4": {
"address": [{
"ip": "192.168.0.2",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "2001:db8:3c4d:02::2",
"prefix-length": 64
}],
"forwarding": True
}
},
{
"name": "wg0",
"type": "infix-if-type:wireguard",
"ipv4": {
"address": [{
"ip": "10.0.0.1",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "fd00:0::1",
"prefix-length": 64
}],
"forwarding": True
},
"wireguard": {
"listen-port": 51820,
"private-key": "server-wg-key",
"peers": [{
"public-key-bag": "wireguard-clients",
"persistent-keepalive": 3,
"peer": [{
"public-key": "client1-wg-pubkey",
"preshared-key": "psk-client1",
"endpoint": "192.168.10.2",
"endpoint-port": 51821,
"allowed-ips": ["10.0.0.2/32", "192.168.1.0/24", "fd00:0::2/128", "2001:db8:3c4d:01::/64"]
}, {
"public-key": "client2-wg-pubkey",
"preshared-key": "psk-client2",
"endpoint": "2001:db8:3c4d:20::2",
"endpoint-port": 51822,
"allowed-ips": ["10.0.0.3/32", "fd00:0::3/128"],
"persistent-keepalive": 5
}]
}]
}
}]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "192.168.1.0/24",
"next-hop": {
"next-hop-address": "10.0.0.2"
}
}]
},
"ipv6": {
"route": [{
"destination-prefix": "2001:db8:3c4d:01::/64",
"next-hop": {
"next-hop-address": "fd00:0::2"
}
}]
}
}
}]
}
}
}
})
def configure_client1(dut):
dut.put_config_dicts({
"ietf-keystore": {
"keystore": {
"asymmetric-keys": {
"asymmetric-key": [{
"name": "client1-wg-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": client1_public_key,
"private-key-format": "infix-crypto-types:x25519-private-key-format",
"cleartext-private-key": client1_private_key
}]
},
"symmetric-keys": {
"symmetric-key": [{
"name": "psk-server",
"infix-keystore:symmetric-key": psk_client1,
"key-format": "infix-crypto-types:wireguard-symmetric-key-format"
}]
}
}
},
"ietf-truststore": {
"truststore": {
"public-key-bags": {
"public-key-bag": [{
"name": "wireguard-server",
"public-key": [{
"name": "server-wg-pubkey",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": server_public_key
}]
}]
}
}
},
"ietf-interfaces": {
"interfaces": {
"interface": [{
"name": client1["link"],
"ipv4": {
"address": [{
"ip": "192.168.10.2",
"prefix-length": 24
}]
}
}, {
"name": client1["data"],
"ipv4": {
"address": [{
"ip": "192.168.1.2",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "2001:db8:3c4d:01::2",
"prefix-length": 64
}],
"forwarding": True
}
}, {
"name": "wg0",
"type": "infix-if-type:wireguard",
"ipv4": {
"address": [{
"ip": "10.0.0.2",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "fd00:0::2",
"prefix-length": 64
}],
"forwarding": True
},
"wireguard": {
"listen-port": 51821,
"private-key": "client1-wg-key",
"peers": [{
"public-key-bag": "wireguard-server",
"preshared-key": "psk-server",
"endpoint": "192.168.10.1",
"endpoint-port": 51820,
"allowed-ips": ["10.0.0.1/32", "10.0.0.3/32", "192.168.0.0/24", "fd00:0::1/128", "fd00:0::3/128", "2001:db8:3c4d:02::/64"],
"persistent-keepalive": 3
}]
}
}]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "10.0.0.0/24",
"next-hop": {
"next-hop-address": "10.0.0.1"
}
}, {
"destination-prefix": "192.168.0.0/16",
"next-hop": {
"next-hop-address": "10.0.0.1"
}
}]
},
"ipv6": {
"route": [{
"destination-prefix": "fd00::/64",
"next-hop": {
"next-hop-address": "fd00::1"
}
}, {
"destination-prefix": "2001:db8:3c4d:02::/64",
"next-hop": {
"next-hop-address": "fd00::1"
}
}]
}
}
}]
}
}
}
})
def configure_client2(dut):
dut.put_config_dicts({
"ietf-keystore": {
"keystore": {
"asymmetric-keys": {
"asymmetric-key": [{
"name": "client2-wg-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": client2_public_key,
"private-key-format": "infix-crypto-types:x25519-private-key-format",
"cleartext-private-key": client2_private_key
}]
},
"symmetric-keys": {
"symmetric-key": [{
"name": "psk-server",
"infix-keystore:symmetric-key": psk_client2,
"key-format": "infix-crypto-types:wireguard-symmetric-key-format"
}]
}
}
},
"ietf-truststore": {
"truststore": {
"public-key-bags": {
"public-key-bag": [{
"name": "wireguard-server",
"public-key": [{
"name": "server-wg-pubkey",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": server_public_key
}]
}]
}
}
},
"ietf-interfaces": {
"interfaces": {
"interface": [{
"name": client2["link"],
"ipv6": {
"address": [{
"ip": "2001:db8:3c4d:20::2",
"prefix-length": 64
}]
}
}, {
"name": "wg0",
"type": "infix-if-type:wireguard",
"ipv4": {
"address": [{
"ip": "10.0.0.3",
"prefix-length": 24
}]
},
"ipv6": {
"address": [{
"ip": "fd00:0::3",
"prefix-length": 64
}]
},
"wireguard": {
"listen-port": 51822,
"private-key": "client2-wg-key",
"peers": [{
"public-key-bag": "wireguard-server",
"preshared-key": "psk-server",
"endpoint": "2001:db8:3c4d:20::1",
"endpoint-port": 51820,
"allowed-ips": ["10.0.0.1/32", "10.0.0.2/32", "192.168.1.0/24", "fd00:0::1/128", "fd00:0::2/128", "2001:db8:3c4d:01::/64"],
"persistent-keepalive": 5
}]
}
}]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "10.0.0.0/24",
"next-hop": {
"next-hop-address": "10.0.0.1"
}
}, {
"destination-prefix": "192.168.0.0/16",
"next-hop": {
"next-hop-address": "10.0.0.1"
}
}]
},
"ipv6": {
"route": [{
"destination-prefix": "fd00::/64",
"next-hop": {
"next-hop-address": "fd00::1"
}
}, {
"destination-prefix": "2001:db8:3c4d:01::/64",
"next-hop": {
"next-hop-address": "fd00::1"
}
}]
}
}
}]
}
}
}
})
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env()
server = env.attach("server", "mgmt")
client1 = env.attach("client1", "mgmt")
client2 = env.attach("client2", "mgmt")
_, hclient1 = env.ltop.xlate("host", "data1")
_, hserver = env.ltop.xlate("host", "data2")
with test.step("Configure server, client1 and client2"):
util.parallel(configure_server(server), configure_client1(client1), configure_client2(client2))
with infamy.IsolatedMacVlan(hserver) as nsserver, infamy.IsolatedMacVlan(hclient1) as nsclient1:
nsserver.addip("192.168.0.1")
nsserver.addroute("default", "192.168.0.2");
nsclient1.addip("192.168.1.1")
nsclient1.addroute("default", "192.168.1.2");
nsserver.addip("2001:db8:3c4d:02::100", prefix_length=64, proto="ipv6")
nsserver.addroute("default", "2001:db8:3c4d:02::2", proto="ipv6")
nsclient1.addip("2001:db8:3c4d:01::100", prefix_length=64, proto="ipv6")
nsclient1.addroute("default", "2001:db8:3c4d:01::2", proto="ipv6")
with test.step("Verify IPv4 connectivity with ping 10.0.0.1, 10.0.0.2 and 10.0.0.3 from host:data1"):
util.parallel(nsclient1.must_reach("10.0.0.1"),
nsclient1.must_reach("10.0.0.2"),
nsclient1.must_reach("10.0.0.3"))
with test.step("Verify IPv4 connectivity with ping 10.0.0.1 and 10.0.0.2 from host:data2"):
util.parallel(nsserver.must_reach("10.0.0.1"),
nsserver.must_reach("10.0.0.2"))
with test.step("Verify host:data2 can not ping 10.0.0.3"):
nsserver.must_not_reach("10.0.0.3") # Not in allowed IPs
with test.step("Verify IPv6 connectivity with ping fd00:0::1, fd00:0::2 and fd00:0::3 from host:data1"):
util.parallel(nsclient1.must_reach("fd00:0::1"),
nsclient1.must_reach("fd00:0::2"),
nsclient1.must_reach("fd00:0::3"))
with test.step("Verify IPv6 connectivity with ping fd00:0:1 and fd00:0:2 from host:data2"):
util.parallel(nsserver.must_reach("fd00:0::1"),
nsserver.must_reach("fd00:0::2"))
with test.step("Verify host:data2 can not ping fd00:0::3"):
nsserver.must_not_reach("fd00:0::3") # Not in allowed IPs
test.succeed()
@@ -0,0 +1,43 @@
graph "wireguard-multipoint" {
layout="neato";
overlap="false";
esep="+40";
node [shape=record, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
host [
label=" { <data1> data1 } | { <mgmt2> mgmt2 } | { <mgmt1> mgmt1 } | host | { <data2> data2 } | { <mgmt3> mgmt3 }"
pos="6,0!",
requires="controller",
];
server [
label=" { <link1> link1 } | { server } | { <mgmt> mgmt } | { <data> data } | { <link2> link2 }"
pos="6, -6!",
requires="infix",
];
client1 [
label="{ { <data> data } | { <mgmt> mgmt } | { client1 } | <link> link } ",
pos="0, -6!",
requires="infix",
];
client2 [
label="{ { <data> data } | { <mgmt> mgmt } | { client2 } | <link> link } ",
pos="12,-6!",
requires="infix",
];
host:mgmt1 -- server:mgmt [requires="mgmt", color="lightgray"]
host:mgmt2 -- client1:mgmt [requires="mgmt", color="lightgray"]
host:mgmt3 -- client2:mgmt [requires="mgmt", color="lightgray"]
host:data1 -- client1:data [headlabel=".1", label="192.168.1.0/24" taillabel=".2&nbsp;", labeldistance=1, fontcolor="black", color="black"]
host:data2 -- server:data [headlabel=".1", label="192.168.0.0/24" taillabel=".2&nbsp;", labeldistance=1, fontcolor="black", color="black"]
server:link1 -- client1:link [headlabel=".1", label="192.168.10.0/24", taillabel=".10&nbsp;", labeldistance=1, fontcolor="black", color="black"]
server:link2 -- client2:link [headlabel=".2\n\n", label="192.168.20.0/24", taillabel="\n.20", labeldistance=1, fontcolor="black", color="black"]
}
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: wireguard&#45;multipoint Pages: 1 -->
<svg width="638pt" height="351pt"
viewBox="0.00 0.00 638.06 351.03" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 347.03)">
<title>wireguard&#45;multipoint</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-347.03 634.06,-347.03 634.06,4 -4,4"/>
<!-- host -->
<g id="node1" class="node">
<title>host</title>
<polygon fill="none" stroke="black" points="145.03,-306.53 145.03,-342.53 485.03,-342.53 485.03,-306.53 145.03,-306.53"/>
<text text-anchor="middle" x="174.03" y="-320.83" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
<polyline fill="none" stroke="black" points="203.03,-306.53 203.03,-342.53 "/>
<text text-anchor="middle" x="232.03" y="-320.83" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
<polyline fill="none" stroke="black" points="261.03,-306.53 261.03,-342.53 "/>
<text text-anchor="middle" x="290.03" y="-320.83" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
<polyline fill="none" stroke="black" points="319.03,-306.53 319.03,-342.53 "/>
<text text-anchor="middle" x="344.03" y="-320.83" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
<polyline fill="none" stroke="black" points="369.03,-306.53 369.03,-342.53 "/>
<text text-anchor="middle" x="398.03" y="-320.83" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
<polyline fill="none" stroke="black" points="427.03,-306.53 427.03,-342.53 "/>
<text text-anchor="middle" x="456.03" y="-320.83" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt3</text>
</g>
<!-- server -->
<g id="node2" class="node">
<title>server</title>
<polygon fill="none" stroke="black" points="174.03,-28.5 174.03,-64.5 456.03,-64.5 456.03,-28.5 174.03,-28.5"/>
<text text-anchor="middle" x="203.03" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">link1</text>
<polyline fill="none" stroke="black" points="232.03,-28.5 232.03,-64.5 "/>
<text text-anchor="middle" x="265.03" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">server</text>
<polyline fill="none" stroke="black" points="298.03,-28.5 298.03,-64.5 "/>
<text text-anchor="middle" x="323.03" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="348.03,-28.5 348.03,-64.5 "/>
<text text-anchor="middle" x="373.03" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="398.03,-28.5 398.03,-64.5 "/>
<text text-anchor="middle" x="427.03" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">link2</text>
</g>
<!-- host&#45;&#45;server -->
<g id="edge1" class="edge">
<title>host:mgmt1&#45;&#45;server:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M290.03,-306.53C290.03,-306.53 323.03,-64.5 323.03,-64.5"/>
</g>
<!-- host&#45;&#45;server -->
<g id="edge5" class="edge">
<title>host:data2&#45;&#45;server:data</title>
<path fill="none" stroke="black" stroke-width="2" d="M398.03,-306.53C398.03,-306.53 373.03,-64.5 373.03,-64.5"/>
<text text-anchor="middle" x="331.03" y="-189.31" font-family="DejaVu Serif, Book" font-size="14.00">192.168.0.0/24</text>
<text text-anchor="middle" x="378.16" y="-69.38" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
<text text-anchor="middle" x="392.89" y="-294.25" font-family="DejaVu Serif, Book" font-size="14.00">.2 </text>
</g>
<!-- client1 -->
<g id="node3" class="node">
<title>client1</title>
<polygon fill="none" stroke="black" points="0,-0.5 0,-92.5 74,-92.5 74,-0.5 0,-0.5"/>
<text text-anchor="middle" x="37" y="-77.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="0,-69.5 74,-69.5 "/>
<text text-anchor="middle" x="37" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="0,-46.5 74,-46.5 "/>
<text text-anchor="middle" x="37" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">client1</text>
<polyline fill="none" stroke="black" points="0,-23.5 74,-23.5 "/>
<text text-anchor="middle" x="37" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
</g>
<!-- host&#45;&#45;client1 -->
<g id="edge2" class="edge">
<title>host:mgmt2&#45;&#45;client1:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M232.03,-306.53C232.03,-306.53 74,-58.5 74,-58.5"/>
</g>
<!-- host&#45;&#45;client1 -->
<g id="edge4" class="edge">
<title>host:data1&#45;&#45;client1:data</title>
<path fill="none" stroke="black" stroke-width="2" d="M174.03,-306.53C174.03,-306.53 74,-81.5 74,-81.5"/>
<text text-anchor="middle" x="69.51" y="-197.81" font-family="DejaVu Serif, Book" font-size="14.00">192.168.1.0/24</text>
<text text-anchor="middle" x="81.54" y="-84.37" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
<text text-anchor="middle" x="166.48" y="-296.26" font-family="DejaVu Serif, Book" font-size="14.00">.2 </text>
</g>
<!-- client2 -->
<g id="node4" class="node">
<title>client2</title>
<polygon fill="none" stroke="black" points="556.06,-0.5 556.06,-92.5 630.06,-92.5 630.06,-0.5 556.06,-0.5"/>
<text text-anchor="middle" x="593.06" y="-77.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="556.06,-69.5 630.06,-69.5 "/>
<text text-anchor="middle" x="593.06" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="556.06,-46.5 630.06,-46.5 "/>
<text text-anchor="middle" x="593.06" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">client2</text>
<polyline fill="none" stroke="black" points="556.06,-23.5 630.06,-23.5 "/>
<text text-anchor="middle" x="593.06" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
</g>
<!-- host&#45;&#45;client2 -->
<g id="edge3" class="edge">
<title>host:mgmt3&#45;&#45;client2:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M456.03,-306.53C456.03,-306.53 556.06,-58.5 556.06,-58.5"/>
</g>
<!-- server&#45;&#45;client1 -->
<g id="edge6" class="edge">
<title>server:link1&#45;&#45;client1:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M174.03,-46.5C174.03,-46.5 74,-11.5 74,-11.5"/>
<text text-anchor="middle" x="153.51" y="-17.8" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.0/24</text>
<text text-anchor="middle" x="83.95" y="-6.8" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
<text text-anchor="middle" x="164.08" y="-43.8" font-family="DejaVu Serif, Book" font-size="14.00">.10 </text>
</g>
<!-- server&#45;&#45;client2 -->
<g id="edge7" class="edge">
<title>server:link2&#45;&#45;client2:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M456.03,-46.5C456.03,-46.5 556.06,-11.5 556.06,-11.5"/>
<text text-anchor="middle" x="447.04" y="-17.8" font-family="DejaVu Serif, Book" font-size="14.00">192.168.20.0/24</text>
<text text-anchor="middle" x="548.9" y="-22.78" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
<text text-anchor="middle" x="463.19" y="-27.82" font-family="DejaVu Serif, Book" font-size="14.00">.20</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

+1
View File
@@ -0,0 +1 @@
test.adoc
@@ -0,0 +1,45 @@
=== WireGuard Point-to-Point
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/interfaces/wireguard_p2p]
==== Description
Set up a WireGuard tunnel between two DUTs with a host connected to the first
DUT. Enable IP forwarding on first DUT's interface to host and the WireGuard
tunnel interface to the second DUT. On host, add route to IP network of second
DUT and verify connectivity with the second DUT through the WireGuard tunnel.
This test verifies:
- WireGuard tunnel establishment between two peers
- Key management via ietf-keystore with X25519 keypairs
- IPv4 and IPv6 connectivity through the encrypted tunnel
- Proper routing through the WireGuard tunnel
Topology:
....
192.168.50.0/24
host:data ---- left:data left:link ---- right:link
192.168.10.2/24 192.168.10.1 192.168.50.1 192.168.50.2
\\ /
\\ /
\\ WireGuard
\\ Tunnel /
\\ /
left:wg0 right:wg0
10.0.0.1/32 10.0.0.2/32
....
==== Topology
image::topology.svg[WireGuard Point-to-Point topology, align=center, scaledwidth=75%]
==== Sequence
. Set up topology and attach to target DUTs
. Configure WireGuard tunnel on DUTs
. Verify IPv4 connectivity with ping 10.0.0.2 from host:data
. Verify IPv6 connectivity with ping fd00::2 from host:data
+260
View File
@@ -0,0 +1,260 @@
#!/usr/bin/env python3
r"""
Basic WireGuard point-to-point tunnel test
Set up a WireGuard tunnel between two DUTs with a host connected to the first
DUT. Enable IP forwarding on first DUT's interface to host and the WireGuard
tunnel interface to the second DUT. On host, add route to IP network of second
DUT and verify connectivity with the second DUT through the WireGuard tunnel.
This test verifies:
- WireGuard tunnel establishment between two peers
- Key management via ietf-keystore with X25519 keypairs
- IPv4 and IPv6 connectivity through the encrypted tunnel
- Proper routing through the WireGuard tunnel
Topology:
....
192.168.50.0/24
host:data ---- left:data left:link ---- right:link
192.168.10.2/24 192.168.10.1 192.168.50.1 192.168.50.2
\\ /
\\ /
\\ WireGuard
\\ Tunnel /
\\ /
left:wg0 right:wg0
10.0.0.1/32 10.0.0.2/32
....
"""
import infamy
import infamy.util as util
left_private_key = "EJPoi0BnccsfjEhKk0IWwNzJKXZKgS6XaKt+InYITVA="
left_public_key = "xWVOEFUZZ5VI6t1fhZeISNyw7Ma/bY8INzIoaSSLlz8="
right_private_key = "UEaX13FTGhiIrnnKRd20KWh/vG6zqRIMSTzOP3hNs2s="
right_public_key = "2pytpunN+e3V9e5asMXP+UqKoerFm08KWzcFYoWP41k="
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env()
left = env.attach("left", "mgmt")
right = env.attach("right", "mgmt")
with test.step("Configure WireGuard tunnel on DUTs"):
util.parallel(left.put_config_dicts({
"ietf-keystore": {
"keystore": {
"asymmetric-keys": {
"asymmetric-key": [{
"name": "left-wg-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": left_public_key,
"private-key-format": "infix-crypto-types:x25519-private-key-format",
"cleartext-private-key": left_private_key
}]
}
}
},
"ietf-truststore": {
"truststore": {
"public-key-bags": {
"public-key-bag": [{
"name": "wireguard-peers",
"public-key": [{
"name": "right-wg-pubkey",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": right_public_key
}]
}]
}
}
},
"ietf-interfaces": {
"interfaces": {
"interface": [{
"name": left["link"],
"ipv4": {
"address": [{
"ip": "192.168.50.1",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "2001:db8:3c4d:50::1",
"prefix-length": 64
}],
"forwarding": True
}
}, {
"name": left["data"],
"ipv4": {
"address": [{
"ip": "192.168.10.1",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "2001:db8:3c4d:10::1",
"prefix-length": 64
}],
"forwarding": True
}
}, {
"name": "wg0",
"type": "infix-if-type:wireguard",
"ipv4": {
"address": [{
"ip": "10.0.0.1",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "fd00::1",
"prefix-length": 64
}],
"forwarding": True
},
"wireguard": {
"listen-port": 51820,
"private-key": "left-wg-key",
"peers": [{
"public-key-bag": "wireguard-peers",
"endpoint": "192.168.50.2",
"endpoint-port": 51820,
"allowed-ips": ["10.0.0.0/24", "fd00::/64"]
}]
}
}]
}
}
}),
right.put_config_dicts({
"ietf-keystore": {
"keystore": {
"asymmetric-keys": {
"asymmetric-key": [{
"name": "right-wg-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": right_public_key,
"private-key-format": "infix-crypto-types:x25519-private-key-format",
"cleartext-private-key": right_private_key
}]
}
}
},
"ietf-truststore": {
"truststore": {
"public-key-bags": {
"public-key-bag": [{
"name": "wireguard-peers",
"public-key": [{
"name": "left-wg-pubkey",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": left_public_key
}]
}]
}
}
},
"ietf-interfaces": {
"interfaces": {
"interface": [{
"name": right["link"],
"ipv4": {
"address": [{
"ip": "192.168.50.2",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "2001:db8:3c4d:50::2",
"prefix-length": 64
}]
}
}, {
"name": "wg0",
"type": "infix-if-type:wireguard",
"ipv4": {
"address": [{
"ip": "10.0.0.2",
"prefix-length": 24
}],
"forwarding": True
},
"ipv6": {
"address": [{
"ip": "fd00::2",
"prefix-length": 64
}]
},
"wireguard": {
"listen-port": 51820,
"private-key": "right-wg-key",
"peers": [{
"public-key-bag": "wireguard-peers",
"endpoint": "192.168.50.1",
"endpoint-port": 51820,
"allowed-ips": ["10.0.0.0/24", "fd00::/64", "2001:db8:3c4d:10::/64", "192.168.10.0/24"]
}]
}
}]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "192.168.10.0/24",
"next-hop": {
"next-hop-address": "10.0.0.1"
}
}]
},
"ipv6": {
"route": [{
"destination-prefix": "2001:db8:3c4d:10::/64",
"next-hop": {
"next-hop-address": "fd00::1"
}
}]
}
}
}]
}
}
}
}))
_, hport = env.ltop.xlate("host", "data")
with test.step("Verify IPv4 connectivity with ping 10.0.0.2 from host:data"):
with infamy.IsolatedMacVlan(hport) as ns0:
ns0.addip("192.168.10.2")
ns0.addroute("10.0.0.0/24", "192.168.10.1")
ns0.must_reach("10.0.0.2")
with test.step("Verify IPv6 connectivity with ping fd00::2 from host:data"):
with infamy.IsolatedMacVlan(hport) as ns0:
ns0.addip("2001:db8:3c4d:10::2", prefix_length=64, proto="ipv6")
ns0.addroute("fd00::/64", "2001:db8:3c4d:10::1", proto="ipv6")
ns0.must_reach("fd00::2")
test.succeed()
@@ -0,0 +1,34 @@
graph "wireguard-basic" {
layout="neato";
overlap="false";
esep="+40";
node [shape=record, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
host [
label="host | { <mgmt1> mgmt1 } | { <data> data } | { <mgmt2> mgmt2 }"
pos="3,0!",
requires="controller",
];
left [
label="{ left } | { <mgmt> mgmt } | { <data> data } | { <link> link }",
pos="0, -3!",
requires="infix",
];
right [
label="{ <link> link } | { <mgmt> mgmt } | { right }",
pos="8,-3!",
requires="infix",
];
host:mgmt1 -- left:mgmt [requires="mgmt", color="lightgray"]
host:data -- left:data [headlabel=".1", label="192.168.10.0/24" taillabel=".2&nbsp;", labeldistance=1, fontcolor="black", color="black"]
host:mgmt2 -- right:mgmt [requires="mgmt", color="lightgray"]
left:link -- right:link [headlabel=".1\n\n", label="192.168.50.0/24", taillabel="\n.2", labeldistance=1, fontcolor="black", color="black"]
}
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: wireguard&#45;basic Pages: 1 -->
<svg width="552pt" height="183pt"
viewBox="0.00 0.00 552.37 183.24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 179.24)">
<title>wireguard&#45;basic</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-179.24 548.37,-179.24 548.37,4 -4,4"/>
<!-- host -->
<g id="node1" class="node">
<title>host</title>
<polygon fill="none" stroke="black" points="129.01,-138.74 129.01,-174.74 345.01,-174.74 345.01,-138.74 129.01,-138.74"/>
<text text-anchor="middle" x="154.01" y="-153.04" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
<polyline fill="none" stroke="black" points="179.01,-138.74 179.01,-174.74 "/>
<text text-anchor="middle" x="208.01" y="-153.04" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
<polyline fill="none" stroke="black" points="237.01,-138.74 237.01,-174.74 "/>
<text text-anchor="middle" x="262.01" y="-153.04" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="287.01,-138.74 287.01,-174.74 "/>
<text text-anchor="middle" x="316.01" y="-153.04" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
</g>
<!-- left -->
<g id="node2" class="node">
<title>left</title>
<polygon fill="none" stroke="black" points="0,-1.73 0,-37.73 200,-37.73 200,-1.73 0,-1.73"/>
<text text-anchor="middle" x="25" y="-16.03" font-family="DejaVu Sans Mono, Book" font-size="14.00">left</text>
<polyline fill="none" stroke="black" points="50,-1.73 50,-37.73 "/>
<text text-anchor="middle" x="75" y="-16.03" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="100,-1.73 100,-37.73 "/>
<text text-anchor="middle" x="125" y="-16.03" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="150,-1.73 150,-37.73 "/>
<text text-anchor="middle" x="175" y="-16.03" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
</g>
<!-- host&#45;&#45;left -->
<g id="edge1" class="edge">
<title>host:mgmt1&#45;&#45;left:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M208.01,-138.74C208.01,-138.74 75,-37.73 75,-37.73"/>
</g>
<!-- host&#45;&#45;left -->
<g id="edge2" class="edge">
<title>host:data&#45;&#45;left:data</title>
<path fill="none" stroke="black" stroke-width="2" d="M262.01,-138.74C262.01,-138.74 125,-37.73 125,-37.73"/>
<text text-anchor="middle" x="134.51" y="-92.03" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.0/24</text>
<text text-anchor="middle" x="134.8" y="-36" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
<text text-anchor="middle" x="252.21" y="-133.06" font-family="DejaVu Serif, Book" font-size="14.00">.2 </text>
</g>
<!-- right -->
<g id="node3" class="node">
<title>right</title>
<polygon fill="none" stroke="black" points="386.37,-1.73 386.37,-37.73 544.37,-37.73 544.37,-1.73 386.37,-1.73"/>
<text text-anchor="middle" x="411.37" y="-16.03" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="436.37,-1.73 436.37,-37.73 "/>
<text text-anchor="middle" x="461.37" y="-16.03" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="486.37,-1.73 486.37,-37.73 "/>
<text text-anchor="middle" x="515.37" y="-16.03" font-family="DejaVu Sans Mono, Book" font-size="14.00">right</text>
</g>
<!-- host&#45;&#45;right -->
<g id="edge3" class="edge">
<title>host:mgmt2&#45;&#45;right:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M345.01,-156.74C345.01,-156.74 461.37,-37.73 461.37,-37.73"/>
</g>
<!-- left&#45;&#45;right -->
<g id="edge4" class="edge">
<title>left:link&#45;&#45;right:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M200,-19.73C200,-19.73 386.37,-19.73 386.37,-19.73"/>
<text text-anchor="middle" x="293.18" y="-8.53" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.0/24</text>
<text text-anchor="middle" x="377.31" y="-28.25" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
<text text-anchor="middle" x="209.06" y="-3.8" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -0,0 +1 @@
test.adoc
@@ -0,0 +1,56 @@
=== WireGuard Roadwarrior
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/interfaces/wireguard_roadwarrior]
==== Description
This test demonstrates a realistic roadwarrior scenario where clients
connect through an internet router to reach a VPN server.
Set up a WireGuard server with 2 roadwarrior clients connecting through
a router that simulates the internet. Each client is on a different subnet
behind the router, and has a private network that should be accessible
through the VPN tunnel.
This test verifies:
- WireGuard tunnel establishment through an intermediate router
- Roadwarrior clients on different subnets
- Access to server's private network through the VPN tunnel
- Routing between client networks and server network via WireGuard
Topology:
....
Server ---- Router ---- Client1
(VPN) (Internet) \---- Client2
WAN: 192.168.100.0/24
Server: 192.168.100.1
Router: 192.168.100.2
Client1 link: 192.168.50.0/24 (Router LAN1)
Client2 link: 192.168.51.0/24 (Router LAN2)
WireGuard tunnel: 10.0.0.0/24
Server: 10.0.0.1/24
Client1: 10.0.0.2/24
Client2: 10.0.0.3/24
Backend networks:
Server data: 192.168.0.0/24
Client1 data: 192.168.1.0/24
Client2 data: 192.168.2.0/24
....
==== Topology
image::topology.svg[WireGuard Roadwarrior topology, align=center, scaledwidth=75%]
==== Sequence
. Set up topology and attach to target DUTs
. Configure DUTs
. Check on the server that both clients is connected
. Verify IPv4 connectivity with ping 192.168.0.2 from host:data1 and host:data2
+359
View File
@@ -0,0 +1,359 @@
#!/usr/bin/env python3
r"""
WireGuard roadwarrior test with router simulating internet
This test demonstrates a realistic roadwarrior scenario where clients
connect through an internet router to reach a VPN server.
Set up a WireGuard server with 2 roadwarrior clients connecting through
a router that simulates the internet. Each client is on a different subnet
behind the router, and has a private network that should be accessible
through the VPN tunnel.
This test verifies:
- WireGuard tunnel establishment through an intermediate router
- Roadwarrior clients on different subnets
- Access to server's private network through the VPN tunnel
- Routing between client networks and server network via WireGuard
Topology:
....
Server ---- Router ---- Client1
(VPN) (Internet) \---- Client2
WAN: 192.168.100.0/24
Server: 192.168.100.1
Router: 192.168.100.2
Client1 link: 192.168.50.0/24 (Router LAN1)
Client2 link: 192.168.51.0/24 (Router LAN2)
WireGuard tunnel: 10.0.0.0/24
Server: 10.0.0.1/24
Client1: 10.0.0.2/24
Client2: 10.0.0.3/24
Backend networks:
Server data: 192.168.0.0/24
Client1 data: 192.168.1.0/24
Client2 data: 192.168.2.0/24
....
"""
import infamy
import infamy.util as util
import infamy.iface as iface
import infamy.wireguard as wg
def configure_server(server, server_public_key, server_private_key, client1_public_key, client2_public_key):
"""Configure WireGuard server with key-bag level settings for all clients"""
server.put_config_dicts({
"ietf-keystore": {
"keystore": {
"asymmetric-keys": {
"asymmetric-key": [{
"name": "server-wg-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": server_public_key,
"private-key-format": "infix-crypto-types:x25519-private-key-format",
"cleartext-private-key": server_private_key
}]
}
}
},
"ietf-truststore": {
"truststore": {
"public-key-bags": {
"public-key-bag": [{
"name": "roadwarriors",
"public-key": [{
"name": "client1-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": client1_public_key,
}, {
"name": "client2-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": client2_public_key,
}]
}]
}
}
},
"ietf-interfaces": {
"interfaces": {
"interface": [{
"name": server["wan"],
"ipv4": {
"address": [{
"ip": "192.168.100.1",
"prefix-length": 24
}],
"forwarding": True
}
}, {
"name": server["data"],
"ipv4": {
"address": [{
"ip": "192.168.0.1",
"prefix-length": 24
}],
"forwarding": True
}
},
{
"name": "wg0",
"type": "infix-if-type:wireguard",
"ipv4": {
"address": [{
"ip": "10.0.0.1",
"prefix-length": 24
}],
"forwarding": True
},
"wireguard": {
"private-key": "server-wg-key",
"peers": [{
"public-key-bag": "roadwarriors",
"persistent-keepalive": 3,
"peer": [{
"public-key": "client1-key",
"allowed-ips": ["10.0.0.2/32", "192.168.1.0/24"]
}, {
"public-key": "client2-key",
"allowed-ips": ["10.0.0.3/32", "192.168.2.0/24"]
}]
}]
}
}]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "192.168.1.0/24",
"next-hop": {
"next-hop-address": "10.0.0.2"
}
}, {
"destination-prefix": "192.168.2.0/24",
"next-hop": {
"next-hop-address": "10.0.0.3"
}
}, {
"destination-prefix": "192.168.50.0/24",
"next-hop": {
"next-hop-address": "192.168.100.2"
}
}, {
"destination-prefix": "192.168.51.0/24",
"next-hop": {
"next-hop-address": "192.168.100.2"
}
}]
}
}
}]
}
}
}
})
def configure_router(router):
"""Configure router to forward traffic between server and clients"""
router.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [{
"name": router["wan"],
"ipv4": {
"address": [{
"ip": "192.168.100.2",
"prefix-length": 24
}],
"forwarding": True
}
}, {
"name": router["lan1"],
"ipv4": {
"address": [{
"ip": "192.168.50.1",
"prefix-length": 24
}],
"forwarding": True
}
}, {
"name": router["lan2"],
"ipv4": {
"address": [{
"ip": "192.168.51.1",
"prefix-length": 24
}],
"forwarding": True
}
}]
}
}
})
def configure_client(client, client_num, private_key, public_key, server_pubkey):
"""Configure a WireGuard roadwarrior client"""
# Client1 is on 192.168.50.0/24, Client2 is on 192.168.51.0/24
link_subnet = 49 + client_num
client.put_config_dicts({
"ietf-keystore": {
"keystore": {
"asymmetric-keys": {
"asymmetric-key": [{
"name": f"client{client_num}-wg-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": public_key,
"private-key-format": "infix-crypto-types:x25519-private-key-format",
"cleartext-private-key": private_key
}]
}
}
},
"ietf-truststore": {
"truststore": {
"public-key-bags": {
"public-key-bag": [{
"name": "server",
"public-key": [{
"name": "server-key",
"public-key-format": "infix-crypto-types:x25519-public-key-format",
"public-key": server_pubkey
}]
}]
}
}
},
"ietf-interfaces": {
"interfaces": {
"interface": [{
"name": client["link"],
"ipv4": {
"address": [{
"ip": f"192.168.{link_subnet}.2",
"prefix-length": 24
}]
}
}, {
"name": client["data"],
"ipv4": {
"address": [{
"ip": f"192.168.{client_num}.1",
"prefix-length": 24
}],
"forwarding": True
}
}, {
"name": "wg0",
"type": "infix-if-type:wireguard",
"ipv4": {
"address": [{
"ip": f"10.0.0.{client_num + 1}",
"prefix-length": 24
}],
"forwarding": True
},
"wireguard": {
"private-key": f"client{client_num}-wg-key",
"peers": [{
"public-key-bag": "server",
"endpoint": "192.168.100.1",
"endpoint-port": 51820,
"allowed-ips": ["10.0.0.0/24", "192.168.0.0/24"],
"persistent-keepalive": 3
}]
}
}]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "192.168.0.0/24",
"next-hop": {
"next-hop-address": "10.0.0.1"
}
}, {
"destination-prefix": "0.0.0.0/0",
"next-hop": {
"next-hop-address": f"192.168.{link_subnet}.1"
}
}]
}
}
}]
}
}
}
})
server_private_key = "uIUL4AnD5QaVrwHDPHJzQ7sIQ+Q3zDdflnvfd59qa28="
server_public_key = "qGVmu5UbNtMuZs2t9wFoOoHlvgmV+A1SyQacVb/bEV0="
client1_private_key = "kNmkNlSkSh9+Va2tmFv9Va8TBCZlTBF0fKAGJf8vomo="
client1_public_key = "ROaZyvJc5DzA2XUAAeTj2YlwDsy2w0lr3t+rWj2imAk="
client2_private_key = "OPT7v/l5zICEmFIrO0U+YwA+w07l8Xo2Dp38hjGOHGY="
client2_public_key = "Om9CPLYdK3l93GauKrq5WXo/gbcD+1CeqFpobRLLkB4="
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env()
server = env.attach("server", "mgmt")
router = env.attach("router", "mgmt")
client1 = env.attach("client1", "mgmt")
client2 = env.attach("client2", "mgmt")
_, hport_server = env.ltop.xlate("host", "data")
_, hport_client1 = env.ltop.xlate("host", "data1")
_, hport_client2 = env.ltop.xlate("host", "data2")
with test.step("Configure DUTs"):
util.parallel(
lambda: configure_server(server, server_public_key, server_private_key, client1_public_key, client2_public_key),
lambda: configure_router(router),
lambda: configure_client(client1, 1, client1_private_key, client1_public_key, server_public_key),
lambda: configure_client(client2, 2, client2_private_key, client2_public_key, server_public_key)
)
with test.step("Check on the server that both clients is connected"):
util.parallel(lambda: util.until(lambda: wg.is_peer_up(server, "wg0", client1_public_key)),
lambda: util.until(lambda: wg.is_peer_up(server, "wg0", client2_public_key)))
with infamy.IsolatedMacVlan(hport_server) as ns_server, \
infamy.IsolatedMacVlan(hport_client1) as ns_client1, \
infamy.IsolatedMacVlan(hport_client2) as ns_client2:
ns_server.addip("192.168.0.2")
ns_server.addroute("default", "192.168.0.1")
ns_client1.addip("192.168.1.2")
ns_client1.addroute("default", "192.168.1.1")
ns_client2.addip("192.168.2.2")
ns_client2.addroute("default", "192.168.2.1")
with test.step("Verify IPv4 connectivity with ping 192.168.0.2 from host:data1 and host:data2"):
util.parallel(
lambda: ns_client1.must_reach("192.168.0.2"),
lambda: ns_client2.must_reach("192.168.0.2")
)
test.succeed()
@@ -0,0 +1,52 @@
graph "roadwarrior" {
layout="neato";
overlap=false;
esep="+20";
splines=true;
node [shape=record, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
host [
label="{ <mgmt1> mgmt1 | <data> data } | host | { <mgmt2> mgmt2 } | { <data2> data2 | <mgmt3> mgmt3 | <data1> data1 | <mgmt4> mgmt4 }",
pos="200,250!",
requires="controller",
];
server [
label="{ <data> data | <mgmt> mgmt } | { server | 192.168.100.1 } | <wan> wan",
pos="50,150!",
requires="infix",
];
router [
label="<wan> wan | { <mgmt> mgmt | router | (internet) } | { <lan1> lan1 | <lan2> lan2 }",
pos="200,150!",
requires="infix",
];
client1 [
label="<link> link | { <data> data | <mgmt> mgmt } | { client1 | 192.168.50.2 }",
pos="325,175!",
requires="infix",
];
client2 [
label="<link> link | { <mgmt> mgmt | <data> data } | { client2 | 192.168.51.2 }",
pos="325,125!",
requires="infix",
];
host:mgmt1 -- server:mgmt [requires="mgmt", color="lightgray"];
host:mgmt2 -- router:mgmt [requires="mgmt", color="lightgray"];
host:mgmt3 -- client1:mgmt [requires="mgmt", color="lightgray"];
host:mgmt4 -- client2:mgmt [requires="mgmt", color="lightgray"];
server:wan -- router:wan [label="192.168.100.0/24", headlabel=".2", taillabel=".1", fontcolor="black", color="black"];
router:lan1 -- client1:link [label="192.168.50.0/24", headlabel=".2", taillabel=".1", fontcolor="black", color="black"];
router:lan2 -- client2:link [label="192.168.51.0/24", headlabel=".2", taillabel=".1", fontcolor="black", color="black"];
host:data -- server:data [headlabel=".1", label="192.168.0.0/24", taillabel=".2", labeldistance=1, fontcolor="black", color="black"];
host:data1 -- client1:data [headlabel=".1", label="192.168.1.0/24", taillabel=".2", labeldistance=1, fontcolor="black", color="black"];
host:data2 -- client2:data [headlabel=".1", label="192.168.2.0/24", taillabel=".2", labeldistance=1, fontcolor="black", color="black"];
}
@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: roadwarrior Pages: 1 -->
<svg width="883pt" height="398pt"
viewBox="0.00 0.00 883.41 398.25" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 394.25)">
<title>roadwarrior</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-394.25 879.41,-394.25 879.41,4 -4,4"/>
<!-- host -->
<g id="node1" class="node">
<title>host</title>
<polygon fill="none" stroke="black" points="351.21,-297.75 351.21,-389.75 575.21,-389.75 575.21,-297.75 351.21,-297.75"/>
<text text-anchor="middle" x="380.21" y="-363.05" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
<polyline fill="none" stroke="black" points="351.21,-343.75 409.21,-343.75 "/>
<text text-anchor="middle" x="380.21" y="-317.05" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="409.21,-297.75 409.21,-389.75 "/>
<text text-anchor="middle" x="434.21" y="-340.05" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
<polyline fill="none" stroke="black" points="459.21,-297.75 459.21,-389.75 "/>
<text text-anchor="middle" x="488.21" y="-340.05" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
<polyline fill="none" stroke="black" points="517.21,-297.75 517.21,-389.75 "/>
<text text-anchor="middle" x="546.21" y="-374.55" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
<polyline fill="none" stroke="black" points="517.21,-366.75 575.21,-366.75 "/>
<text text-anchor="middle" x="546.21" y="-351.55" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt3</text>
<polyline fill="none" stroke="black" points="517.21,-343.75 575.21,-343.75 "/>
<text text-anchor="middle" x="546.21" y="-328.55" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
<polyline fill="none" stroke="black" points="517.21,-320.75 575.21,-320.75 "/>
<text text-anchor="middle" x="546.21" y="-305.55" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt4</text>
</g>
<!-- server -->
<g id="node2" class="node">
<title>server</title>
<polygon fill="none" stroke="black" points="52.08,-118.33 52.08,-164.33 267.08,-164.33 267.08,-118.33 52.08,-118.33"/>
<text text-anchor="middle" x="77.08" y="-149.13" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="52.08,-141.33 102.08,-141.33 "/>
<text text-anchor="middle" x="77.08" y="-126.13" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="102.08,-118.33 102.08,-164.33 "/>
<text text-anchor="middle" x="164.08" y="-149.13" font-family="DejaVu Sans Mono, Book" font-size="14.00">server</text>
<polyline fill="none" stroke="black" points="102.08,-141.33 226.08,-141.33 "/>
<text text-anchor="middle" x="164.08" y="-126.13" font-family="DejaVu Sans Mono, Book" font-size="14.00">192.168.100.1</text>
<polyline fill="none" stroke="black" points="226.08,-118.33 226.08,-164.33 "/>
<text text-anchor="middle" x="246.58" y="-137.63" font-family="DejaVu Sans Mono, Book" font-size="14.00">wan</text>
</g>
<!-- host&#45;&#45;server -->
<g id="edge1" class="edge">
<title>host:mgmt1&#45;&#45;server:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M351.21,-366.75C351.21,-366.75 114.07,-325.63 32.08,-184.33 12.9,-151.27 0,-119.11 32.08,-98.33 50.31,-86.52 76.68,-118.3 76.68,-118.3"/>
</g>
<!-- host&#45;&#45;server -->
<g id="edge8" class="edge">
<title>host:data&#45;&#45;server:data</title>
<path fill="none" stroke="black" stroke-width="2" d="M351.21,-320.75C351.21,-320.75 76.58,-164.33 76.58,-164.33"/>
<text text-anchor="middle" x="159.39" y="-246.34" font-family="DejaVu Serif, Book" font-size="14.00">192.168.0.0/24</text>
<text text-anchor="middle" x="86.55" y="-161.44" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
<text text-anchor="middle" x="341.24" y="-316.23" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
</g>
<!-- router -->
<g id="node3" class="node">
<title>router</title>
<polygon fill="none" stroke="black" points="368.21,-106.83 368.21,-175.83 558.21,-175.83 558.21,-106.83 368.21,-106.83"/>
<text text-anchor="middle" x="388.71" y="-137.63" font-family="DejaVu Sans Mono, Book" font-size="14.00">wan</text>
<polyline fill="none" stroke="black" points="409.21,-106.83 409.21,-175.83 "/>
<text text-anchor="middle" x="458.71" y="-160.63" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="409.21,-152.83 508.21,-152.83 "/>
<text text-anchor="middle" x="458.71" y="-137.63" font-family="DejaVu Sans Mono, Book" font-size="14.00">router</text>
<polyline fill="none" stroke="black" points="409.21,-129.83 508.21,-129.83 "/>
<text text-anchor="middle" x="458.71" y="-114.63" font-family="DejaVu Sans Mono, Book" font-size="14.00">(internet)</text>
<polyline fill="none" stroke="black" points="508.21,-106.83 508.21,-175.83 "/>
<text text-anchor="middle" x="533.21" y="-155.13" font-family="DejaVu Sans Mono, Book" font-size="14.00">lan1</text>
<polyline fill="none" stroke="black" points="508.21,-141.83 558.21,-141.83 "/>
<text text-anchor="middle" x="533.21" y="-120.63" font-family="DejaVu Sans Mono, Book" font-size="14.00">lan2</text>
</g>
<!-- host&#45;&#45;router -->
<g id="edge2" class="edge">
<title>host:mgmt2&#45;&#45;router:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M488.21,-297.75C488.21,-297.75 458.21,-176.33 458.21,-176.33"/>
</g>
<!-- client1 -->
<g id="node4" class="node">
<title>client1</title>
<polygon fill="none" stroke="black" points="608.23,-168.93 608.23,-214.93 824.23,-214.93 824.23,-168.93 608.23,-168.93"/>
<text text-anchor="middle" x="633.23" y="-188.23" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="658.23,-168.93 658.23,-214.93 "/>
<text text-anchor="middle" x="683.23" y="-199.73" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="658.23,-191.93 708.23,-191.93 "/>
<text text-anchor="middle" x="683.23" y="-176.73" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="708.23,-168.93 708.23,-214.93 "/>
<text text-anchor="middle" x="766.23" y="-199.73" font-family="DejaVu Sans Mono, Book" font-size="14.00">client1</text>
<polyline fill="none" stroke="black" points="708.23,-191.93 824.23,-191.93 "/>
<text text-anchor="middle" x="766.23" y="-176.73" font-family="DejaVu Sans Mono, Book" font-size="14.00">192.168.50.2</text>
</g>
<!-- host&#45;&#45;client1 -->
<g id="edge3" class="edge">
<title>host:mgmt3&#45;&#45;client1:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M575.21,-355.75C575.21,-355.75 773.03,-344.97 844.23,-234.93 865,-202.84 872.88,-174.24 844.23,-148.93 817.23,-125.07 683.32,-168.88 683.32,-168.88"/>
</g>
<!-- host&#45;&#45;client1 -->
<g id="edge9" class="edge">
<title>host:data1&#45;&#45;client1:data</title>
<path fill="none" stroke="black" stroke-width="2" d="M575.21,-331.75C575.21,-331.75 683.23,-214.93 683.23,-214.93"/>
<text text-anchor="middle" x="574.72" y="-277.14" font-family="DejaVu Serif, Book" font-size="14.00">192.168.1.0/24</text>
<text text-anchor="middle" x="680.18" y="-220.75" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
<text text-anchor="middle" x="578.26" y="-318.52" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
</g>
<!-- client2 -->
<g id="node5" class="node">
<title>client2</title>
<polygon fill="none" stroke="black" points="608.23,-67.72 608.23,-113.72 824.23,-113.72 824.23,-67.72 608.23,-67.72"/>
<text text-anchor="middle" x="633.23" y="-87.02" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="658.23,-67.72 658.23,-113.72 "/>
<text text-anchor="middle" x="683.23" y="-98.52" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="658.23,-90.72 708.23,-90.72 "/>
<text text-anchor="middle" x="683.23" y="-75.52" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="708.23,-67.72 708.23,-113.72 "/>
<text text-anchor="middle" x="766.23" y="-98.52" font-family="DejaVu Sans Mono, Book" font-size="14.00">client2</text>
<polyline fill="none" stroke="black" points="708.23,-90.72 824.23,-90.72 "/>
<text text-anchor="middle" x="766.23" y="-75.52" font-family="DejaVu Sans Mono, Book" font-size="14.00">192.168.51.2</text>
</g>
<!-- host&#45;&#45;client2 -->
<g id="edge4" class="edge">
<title>host:mgmt4&#45;&#45;client2:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M575.21,-308.75C575.21,-308.75 769.05,-333.52 844.23,-234.93 867.41,-204.54 868.2,-178.71 844.23,-148.93 832.75,-134.67 683.23,-113.72 683.23,-113.72"/>
</g>
<!-- host&#45;&#45;client2 -->
<g id="edge10" class="edge">
<title>host:data2&#45;&#45;client2:data</title>
<path fill="none" stroke="black" stroke-width="2" d="M575.21,-378.75C575.21,-378.75 774.54,-351.22 844.23,-234.93 865.62,-199.25 875.41,-75.27 844.23,-47.72 790.23,0 683.32,-67.67 683.32,-67.67"/>
<text text-anchor="middle" x="729.24" y="-357.4" font-family="DejaVu Serif, Book" font-size="14.00">192.168.2.0/24</text>
<text text-anchor="middle" x="688.88" y="-55.66" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
<text text-anchor="middle" x="583.51" y="-369.47" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
</g>
<!-- server&#45;&#45;router -->
<g id="edge5" class="edge">
<title>server:wan&#45;&#45;router:wan</title>
<path fill="none" stroke="black" stroke-width="2" d="M267.58,-141.33C267.58,-141.33 368.21,-141.33 368.21,-141.33"/>
<text text-anchor="middle" x="317.89" y="-130.13" font-family="DejaVu Serif, Book" font-size="14.00">192.168.100.0/24</text>
<text text-anchor="middle" x="361.21" y="-145.13" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
<text text-anchor="middle" x="274.58" y="-145.13" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
</g>
<!-- router&#45;&#45;client1 -->
<g id="edge6" class="edge">
<title>router:lan1&#45;&#45;client1:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M558.21,-159.33C558.21,-159.33 608.23,-191.93 608.23,-191.93"/>
<text text-anchor="middle" x="524.22" y="-179.43" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.0/24</text>
<text text-anchor="middle" x="601.23" y="-195.73" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
<text text-anchor="middle" x="565.21" y="-163.13" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
</g>
<!-- router&#45;&#45;client2 -->
<g id="edge7" class="edge">
<title>router:lan2&#45;&#45;client2:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M558.21,-124.33C558.21,-124.33 608.23,-90.72 608.23,-90.72"/>
<text text-anchor="middle" x="524.22" y="-96.32" font-family="DejaVu Serif, Book" font-size="14.00">192.168.51.0/24</text>
<text text-anchor="middle" x="601.23" y="-94.52" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
<text text-anchor="middle" x="565.21" y="-128.13" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

+90
View File
@@ -0,0 +1,90 @@
"""
Fetch WireGuard interface status from remote device.
"""
import json
def get_peers(target, iface):
"""Get all WireGuard peers from interface as a list"""
interface = target.get_iface(iface)
if interface is None:
return []
wg = interface.get("wireguard") or interface.get("infix-if-wireguard:wireguard")
if wg is None:
return []
# Peers are under 'peer-status' in operational state
peer_status = wg.get('peer-status')
if peer_status is None:
return []
# The peer-status container has a 'peer' list inside it
if isinstance(peer_status, dict):
peer_list = peer_status.get('peer', [])
if isinstance(peer_list, list):
return peer_list
# Single peer might not be in a list
if peer_list:
return [peer_list]
return []
# If peer_status is already a list
if isinstance(peer_status, list):
return peer_status
return []
def get_peer_by_pubkey(target, iface, public_key):
"""Get specific WireGuard peer by public key"""
peers = get_peers(target, iface)
for peer in peers:
if peer.get('public-key') == public_key:
return peer
return None
def is_peer_up(target, iface, public_key):
"""Check if WireGuard peer connection status is 'up'"""
peer = get_peer_by_pubkey(target, iface, public_key)
if peer is None:
return False
status = peer.get('connection-status')
if status is None:
return False
return status.lower() == 'up'
def get_peer_endpoint(target, iface, public_key):
"""Get WireGuard peer endpoint"""
peer = get_peer_by_pubkey(target, iface, public_key)
if peer is None:
return None
return peer.get('endpoint')
def get_peer_transfer(target, iface, public_key):
"""Get WireGuard peer transfer statistics (tx, rx in bytes)"""
peer = get_peer_by_pubkey(target, iface, public_key)
if peer is None:
return None, None
tx = peer.get('transfer-tx')
rx = peer.get('transfer-rx')
return tx, rx
def get_peer_handshake(target, iface, public_key):
"""Get WireGuard peer latest handshake timestamp"""
peer = get_peer_by_pubkey(target, iface, public_key)
if peer is None:
return None
return peer.get('latest-handshake')