Skip to content

Conversation

@JoshuaWatt
Copy link
Contributor

@JoshuaWatt JoshuaWatt commented Jan 13, 2026

Add VPN support

Proof of Concept for adding VPN support that allows clients to connect a
VPN to a NetworkInterface via a RawNetworkInterfaceDriver.

The setup for this is a little complicated, so it will be explained
here:

Client Side Setup

On the client side, the design of the VPN is centered around using a
network/user namespace. This namespace is created with the unshare
command, and inside the namespace, the current user has root
permissions. This allows changing any networking settings inside the
namespace (since a new, empty network namespace was is also created).
Access to any non-networking resources outside of the namespace are done
with the calling users credentials (per the normal user namespace
rules). Critically, unprivledged users are allowed to create these types
of namespaces, so sudo access on the client side is not required.

The method for connecting the network namespace to the exporter
interface is to create a TAP inside the network namespace that is
processed by a program that sends and receives frames over stdio. This
program sends the frames over an SSH connection to a corresponding
program running on the exporter.

Exporter Setup

On the exporter side, a helper script is invoked using sudo to setup
the interface (unlike the client, setting up the VPN without using root
permissions is not a requirement for the exporter). This script creates
a macvlan interface bound to the target interface, then uses the same
program as the client to stream frames from its tap over stdio back to
the client.

Trying it out

VPN support is added to labgrid-client. For a quick demo of the
capabilities, try:

labgrid-client vpn -- wireshark -k -i tap0

API

RawNetworkInterfaceDriver has a new API called vpn_ns(). This is a
context manager that establishes the VPN while it is active. It yields a
VPNNamespace object which is more or less a proxy for
subprocess.run() and subprocess.Popen() except that it runs the
command inside the network namespace.

@JoshuaWatt JoshuaWatt force-pushed the vpn branch 2 times, most recently from f7f8a0a to 6e5c352 Compare January 13, 2026 22:09
@codecov
Copy link

codecov bot commented Jan 13, 2026

Codecov Report

❌ Patch coverage is 5.15152% with 313 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.1%. Comparing base (5e4119a) to head (1fe3bae).
⚠️ Report is 3 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
labgrid/taptunnel.py 0.0% 254 Missing ⚠️
labgrid/driver/rawnetworkinterfacedriver.py 24.5% 46 Missing ⚠️
labgrid/remote/client.py 13.3% 13 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##           master   #1797     +/-   ##
========================================
- Coverage    45.1%   44.1%   -1.0%     
========================================
  Files         172     173      +1     
  Lines       13617   13947    +330     
========================================
+ Hits         6143    6160     +17     
- Misses       7474    7787    +313     
Flag Coverage Δ
3.10 44.1% <5.1%> (-1.0%) ⬇️
3.11 44.1% <5.1%> (-1.0%) ⬇️
3.12 44.1% <5.1%> (-1.0%) ⬇️
3.13 44.1% <5.1%> (-1.0%) ⬇️
3.14 44.1% <5.1%> (-1.0%) ⬇️
3.9 44.2% <5.1%> (-1.0%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@JoshuaWatt JoshuaWatt force-pushed the vpn branch 3 times, most recently from 954438b to f582467 Compare January 20, 2026 19:00
@JoshuaWatt JoshuaWatt changed the title POC: Add VPN support Add VPN support Jan 20, 2026
@JoshuaWatt JoshuaWatt force-pushed the vpn branch 2 times, most recently from c72c96c to f678a86 Compare January 20, 2026 21:54
Proof of Concept for adding VPN support that allows clients to connect a
VPN to a NetworkInterface via a RawNetworkInterfaceDriver.

The setup for this is a little complicated, so it will be explained
here:

**Client Side Setup**

On the client side, the design of the VPN is centered around using a
network/user namespace. This namespace is created with the unshare
command, and inside the namespace, the current user has root
permissions. This allows changing any networking settings inside the
namespace (since a new, empty network namespace was is also created).
Access to any non-networking resources outside of the namespace are done
with the calling users credentials (per the normal user namespace
rules). Critically, unprivledged users are allowed to create these types
of namespaces, so sudo access on the client side is not required.

The method for connecting the network namespace to the exporter
interface is to create a TAP inside the network namespace that is
processed by a program that sends and receives frames over stdio.  This
program sends the frames over an SSH connection to a corresponding
program running on the exporter.

**Exporter Setup**

On the exporter side, a helper script is invoked using `sudo` to setup
the interface (unlike the client, setting up the VPN without using root
permissions is not a requirement for the exporter). This script creates
a macvlan interface bound to the target interface, then uses the same
program as the client to stream frames from its tap over stdio back to
the client.

**Trying it out**

VPN support is added to `labgrid-client`. For a quick demo of the
capabilities, try:

```
labgrid-client vpn -- wireshark -k -i tap0
```

**API**

`RawNetworkInterfaceDriver` has a new API called `vpn_ns()`. This is a
context manager that establishes the VPN while it is active. It yields a
`VPNNamespace` object which is more or less a proxy for
`subprocess.run()` and `subprocess.Popen()` except that it runs the
command inside the network namespace.

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
Waits for all addresses to exit the transitive state; otherwise then
cannot be used to bind/connect with sockets

Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
In order for connect() or bind() to succeed, then need to be called
while the socket is in the network namespace, so add explict API to do
that and then pass the socket out to the caller
@JoshuaWatt
Copy link
Contributor Author

Superseded by #1805

@JoshuaWatt JoshuaWatt closed this Jan 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant