vpn

package
v0.55.18 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 5, 2026 License: Apache-2.0 Imports: 1 Imported by: 0

README

VPN add-on

An optional, network-level path from an enrolled device to the Authority, in addition to the standard reach paths of the homelab identity target (§8). It is never a default in any mode or kit: it exists only after the Owner switches it on at the Authority, and a VPN is never the realization of private remote access. A peer is reach, not identity: every request through it still carries the sign-in and device proof its route asks for.

Contract: #VPNAddOnV1 in foundation/network.cue. Tool catalog with pinned images and licences: tools.cue (the CLI runs exactly these pins).

Tools

One tool at a time, chosen by the Owner. Each runs upstream-faithful: the distribution's wireguard-tools, or the upstream image pinned by digest.

Tool Runs on the Authority The Owner supplies Device client
WireGuard host wireguard-tools, interface wg-stackkit (UDP 51820) --endpoint host:port that reaches UDP 51820; the peer service is host:9444 any WireGuard client with its own key pair
Headscale headscale and a Tailscale node (tailscale0) --endpoint host:port that reaches TCP 9444 (the peer service carries the control endpoint and the embedded DERP relay); UDP 3478 (STUN) and 41641 for direct paths the stock Tailscale client
Tailscale a Tailscale node (tailscale0) in the Owner's tailnet --tailnet and an OAuth client (scopes auth_keys, devices:core, devices:routes; owner of tag:stackkit-node and tag:stackkit-device) and a tailnet policy that lets tag:stackkit-device reach only the site address the stock Tailscale client
Pangolin Newt, dialling out to the Owner's Pangolin server the server origin, its Integration API URL, organization, the site's Newt ID and secret, an organization API key (<keyId>.<secret>; clients and site resources), optionally the server's CA the stock Olm client

The Headscale and Tailscale tools need the host's Tailscale node for the add-on alone: an Authority that already runs its own Tailscale is refused. A coordinated tool stays unavailable without its authenticated coordination path: a missing or refused credential fails enable, and the add-on stays off.

Owner console (root, at the Authority)

sudo stackkit vpn enable --tool wireguard --endpoint vpn.example.net:51820
sudo stackkit vpn status
sudo stackkit vpn reconcile
sudo stackkit vpn disable

enable records the opt-in, starts the tool, registers the Authority as the tool's node that routes only its site address (/32), confines the tool's interface to the site address with an nftables guard, starts stackkit-vpn.service (the peer service and the reconciler) and opens exactly the tool's public ports in the StackKits Home firewall, which otherwise admits only private sources (WireGuard: UDP 51820 and TCP 9444; Headscale: TCP 9444, UDP 3478 and 41641; Tailscale: UDP 41641; Pangolin: none, Newt dials out). disable removes every peer from the tool, the tool's runtime, the service and those ports. Tool secrets are read from files into the add-on's custody (.stackkit/custody/vpn-addon/, 0600) and never printed.

Peers at enrollment

The device client, holding the first credential its enrollment returned, asks the peer service for its peer right away, so the device receives it in the same enrollment. Every call carries the device credential (Authorization: DPoP …) and a DPoP proof for exactly its method and URL; a pending away enrollment has no credential and gets no peer.

Call Body Answer
GET https://<site>:9444/api/v1/vpn the tool and this device's peer
POST https://<site>:9444/api/v1/vpn/peer WireGuard: {"publicKey": "<the device's own key>"}; otherwise {} WireGuard: its address and a wg-quick configuration without the private key; Headscale and Tailscale: a single-use join key (10 minutes); Pangolin: a machine client's ID and secret. Secrets appear once and are never stored.
POST https://<site>:9444/api/v1/vpn/peer/confirm {}, sent through the VPN connected; binds a Headscale or Tailscale node to the device

The confirmation is the enrollment's probe from the device's own vantage. The service's certificate comes from the homelab's step-ca; native clients pin the root the enrollment bootstrap names and the server name stackkit-home-vpn.internal. Away, WireGuard and Headscale devices reach the service at the Owner-declared endpoint host; Tailscale and Pangolin devices receive their peer at home or through another enabled path.

A homelab declared local-only (lab.home) hands its peers the StackKits resolver on the site address: WireGuard as the tunnel's DNS server, Headscale as split DNS for the homelab domain. An Android device, which has no per-domain DNS, reaches lab.home this way.

Revocation

Revoking a device, undoing its enrollment or removing its person (revocation registry artifact homelab.vpn_peer) removes its peer: the WireGuard peer leaves the interface (wg syncconf), a Headscale or Tailscale node and its join key are deleted, a Pangolin client is deleted. The reconciler watches the device registry and applies a change at once, and passes over every device at least every 30 seconds for expiries, inside the 60 second revocation target. A node that no active device bound within 15 minutes is deleted too, so a peer never outlives an unfinished enrollment. A failed deletion stays in the peer ledger and is retried.

Confinement

A peer reaches only the node site address (and the WireGuard node address): the guard (stackkit_vpn_guard, loaded at boot by stackkit-vpn-guard.service before the container engine and wg-quick, and refreshed on every reconcile) drops everything else that arrives through the tool, and no peer reaches another peer. On tailscale0 only the addresses of bound peers of active devices pass; any other tailnet node reaches nothing but the peer service's probe. Newt runs on its own bridge (br-stackkit-vpn), which may reach the site address, its Pangolin server and the internet, never another private address. Headscale approves no route automatically; the Authority node's single host route is approved by its node ID. Ubuntu 26.04 enforces AppArmor profiles for wg and wg-quick that admit only /etc/wireguard; the add-on keeps the node key in custody and adds exactly one read rule for it to /etc/apparmor.d/local/wg, the profile's site-local include, in a marked block it alone manages, then reloads the profile. The profile stays enforced.

Documentation

Overview

Package vpn embeds the tool catalog of the optional VPN add-on so the stackkit CLI runs exactly the components tools.cue pins.

Index

Constants

This section is empty.

Variables

View Source
var ToolsCUE string

ToolsCUE is tools.cue: the four tools, their components, pinned images and licences. It imports nothing and evaluates on its own.

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL