service

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InterfaceCreate

func InterfaceCreate(cfg models.InterfaceConfig) error

func InterfaceDelete

func InterfaceDelete(cfg models.InterfaceConfig) error

InterfaceDelete tears cfg's interface down and removes the OS-level link itself (netlink.LinkDel) — without the LinkDel call, DELETE /interfaces/{name} only dropped the agent's JSON record while the actual `ip link` survived on the host. Idempotent: a link that's already gone (e.g. removed by hand) is treated as already deleted rather than an error.

PreDown/PostDown hooks run around the teardown but, unlike InterfaceCreate's PreUp/PostUp (which abort on failure), are best-effort: removing the link is the whole point here, so a failing hook is logged and teardown continues.

func InterfaceUpdate

func InterfaceUpdate(prev *models.InterfaceConfig, cfg models.InterfaceConfig) error

InterfaceUpdate reconfigures an already-existing link in place and reconciles its hooks: any rules the previous config set up (prev's PreDown/PostDown) are torn down first, then the new config's PreUp/PostUp set up the current ones. This lets an admin edit an interface — e.g. add or remove a tunnel's routing rules — and have them applied and reverted without recreating the link (which would drop its peers/clients). prev is nil on agent-startup re-apply, in which case only the new up-hooks run. The teardown side is best-effort so a rule that's already gone doesn't block the update.

func IsInterfaceExist

func IsInterfaceExist(cfg models.InterfaceConfig) bool

func SetBackend

func SetBackend(b Backend)

SetBackend selects the link backend for this process. Call it once, early in main, before loading interfaces. Not safe to call concurrently with interface operations.

func WatchStorage

func WatchStorage(ctx context.Context, dir string) error

WatchStorage watches dir (the agent's config storage root, see agent/storage/fs.Dir) for interface config files disappearing while the agent is already running, and tears the corresponding link down immediately via InterfaceDelete — instead of leaving it dangling until the agent happens to restart (see Handler.DetectOrphans, which only catches this at startup).

Unlike DetectOrphans, this needs no "is this really mine" judgment call: dir is a directory this agent exclusively writes (*iface*.json) into via Set/Delete (agent/storage/fs.go) — there is no other source for files in it, so any file disappearing from it by definition was one of this agent's own interface records, however it disappeared (DELETE /interfaces/{name}, which already calls InterfaceDelete itself before removing the file — so this is a harmless idempotent no-op for that case, see InterfaceDelete's own existence check — or someone removing the file by hand while the agent is up, which is the actual gap this closes). A plain function rather than a Handler method: tearing down a link only needs netlink (InterfaceDelete), not storage.Storage or a wgctrl.Client, so there's no reason to make the caller open one just for this.

Runs until ctx is cancelled; logs (rather than returns) errors from individual events so a single bad event doesn't tear down the watch.

Types

type Backend

type Backend interface {
	// Add brings the interface's OS link into existence. amnezia selects the
	// interface type: true creates an AmneziaWG link (obfuscation-capable),
	// false a plain WireGuard one — driven by the admin's "Amnezia Interface"
	// toggle (see models.InterfaceConfig.IsAmnezia). Kernel: netlink RTM_NEWLINK
	// with the matching kind. Userspace: the amneziawg-go device serves both, so
	// the TUN is created the same way regardless.
	Add(iface string, amnezia bool) error
	// Delete removes the OS link. Kernel: netlink RTM_DELLINK. Userspace: close
	// the amneziawg-go device (which removes the TUN). Idempotent: a link that's
	// already gone is not an error.
	Delete(iface string) error
	// Exists reports whether the interface's OS link is present.
	Exists(iface string) bool
	// Up brings the link administratively up.
	Up(iface string) error
	// Down brings the link administratively down.
	Down(iface string) error
	// SetMTU sets the link MTU.
	SetMTU(iface string, mtu int) error
	// AddrAdd assigns addr (a CIDR like 10.0.0.1/24) to the link.
	AddrAdd(iface, addr string) error
	// SyncAddr makes addr the link's only address, removing any others first.
	SyncAddr(iface, addr string) error
	// Info reports what this backend can create on the current host — its kind,
	// the interface variants it can bring up, and (kernel only) whether the
	// AmneziaWG kernel module is present. Probed on each call (it shells out to
	// modinfo for the kernel backend), so the agent calls it once at startup and
	// caches the result (see agent.Run → models.HostInfo).
	Info() BackendInfo
}

Backend abstracts the OS-level lifecycle of a WireGuard/AmneziaWG interface — the one part of the agent that differs between the two builds:

  • kernel (agent/internal/kernel): the AmneziaWG kernel module, driven over netlink (vishvananda/netlink). Wired in by cmd/awg-agent.
  • userspace (agent/internal/userspace): an in-process amneziawg-go TUN, with address/up/mtu done via the `ip` command (no netlink dependency, the way jwg / awg-quick's userspace path do it). Wired in by cmd/awg-agent-userspace.

Keeping this an interface (rather than either build importing the other) is what lets the userspace agent avoid pulling in vishvananda/netlink at all. Everything else the agent does is backend-agnostic:

  • pushing the device config and reading peer stats via wgctrl, which speaks both the kernel genl family and the userspace UAPI socket;
  • running PreUp/PostUp/PreDown/PostDown hooks (plain `sh -c`);
  • storing interface configs (agent/storage).

type BackendInfo

type BackendInfo struct {
	// Kind identifies the backend: "kernel" or "userspace".
	Kind string
	// KernelModule reports whether the AmneziaWG kernel module is available on
	// this host. Always false for the userspace backend (it needs no module).
	KernelModule bool
	// InterfaceKinds lists the interface variants creatable here: "amneziawg"
	// and/or "wireguard".
	InterfaceKinds []string
}

BackendInfo describes what the active link backend can create on the current host. Gathered once at startup (Backend.Info) and folded into the agent's GET /info response (models.HostInfo). Host-level facts that don't depend on the backend — whether Docker is usable, whether the agent runs in a container — are discovered separately by the agent package, not here.

func ActiveBackendInfo

func ActiveBackendInfo() BackendInfo

ActiveBackendInfo returns the active backend's capabilities (see BackendInfo). SetBackend must have been called first.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

func NewHandler

func NewHandler(store storage.Storage, awg *wgctrl.Client) *Handler

func (*Handler) All

func (h *Handler) All() error

func (*Handler) Delete

func (h *Handler) Delete(iface string) error

func (*Handler) DetectOrphans

func (h *Handler) DetectOrphans() ([]string, error)

DetectOrphans returns the name of every live WireGuard interface on the host that h's storage has no record of — e.g. a DELETE /interfaces/{name} call's JSON write succeeded but its netlink teardown didn't (shouldn't happen anymore, see InterfaceDelete, but old data or a manual `ip link` could still produce this), or the agent's storage was wiped/replaced without the corresponding interfaces being torn down first.

Deliberately read-only: the agent can't tell "this is my own orphan" from "an administrator created an unrelated WireGuard interface by hand, with nothing to do with awg-admin" — deciding what to do about a mismatch is left to a human (see awg-admin's agent↔DB reconciliation), this just surfaces the list.

func (*Handler) One

One applies cfg to the host: creates or (in place) updates the link, then pushes the full device config. prev is the previously-stored config for this interface (nil for a brand-new one), used by InterfaceUpdate to reconcile hooks on an in-place edit.

func (*Handler) StopEnabled

func (h *Handler) StopEnabled()

StopEnabled tears down the OS link of every enabled interface (best effort), leaving the stored configs intact. It's called on agent shutdown so tunnels don't keep carrying traffic — and lifecycle PreDown/PostDown rules stay in effect — while the agent is gone. Disabled interfaces are skipped (their link is already down). On the next start, All → One re-creates the enabled ones.

Jump to

Keyboard shortcuts

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