limguard

package module
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Mar 30, 2026 License: MIT Imports: 32 Imported by: 0

README

limguard - WireGuard Mesh Network Manager

limguard sets up an encrypted VPC for your cluster using WireGuard. Once set up, you can then use any CNI like Calico, Cilium etc to work with wg0 interface to run on this fabric.

The best use cases are the public nodes where you'd like your inter-node communication to be over private network and encrypted.

Installation

macOS (Homebrew)
brew install limrun-inc/limguard/limguard
Linux
ARCH=amd64
curl -Lo limguard https://github.com/limrun-inc/limguard/releases/latest/download/limguard-linux-${ARCH}
chmod +x limguard
sudo mv limguard /usr/local/bin/

Quick Start

1. Create config
# limguard.yaml
nodes:
  node-1:
    wireguardIP: "10.200.0.1"
    endpoint: "203.0.113.10:51820"
    ssh:
      host: "203.0.113.10"
      user: root

  node-2:
    wireguardIP: "10.200.0.2"
    endpoint: "203.0.113.11:51820"
    ssh:
      host: "203.0.113.11"
      user: root
2. Deploy
limguard apply --config limguard.yaml

Once deployed, the mesh network is established and the daemon watches for routes that your CNI adds and makes sure WireGuard is aware of them as well, which means pod & service CIDRs work just as if all is in the same private network.

Config Format

One YAML file used everywhere:

# Optional (defaults shown)
linuxInterfaceName: wg0     # WireGuard interface on Linux
darwinInterfaceName: utun9  # WireGuard interface on macOS

# Version of limguard to download from GitHub releases.
# If omitted, the latest release is fetched and written back to this file.
# version: v1.0.0

# All nodes
nodes:
  node-name:
    wireguardIP: "10.200.0.1"   # WireGuard mesh IP
    endpoint: "203.0.113.10:51820"  # Public IP/hostname with port
    publicKey: "..."            # Filled in by apply
    # localBinaryPath: /path/to/limguard  # Optional: use local binary instead of downloading
    ssh:                        # Only needed for apply
      host: "203.0.113.10"
      port: 22
      user: root

  # Local node (join mesh from this machine)
  ops-laptop:
    wireguardIP: "10.200.0.50"
    ssh:
      host: self               # Special value: configure locally, no SSH

Commands

limguard apply --config limguard.yaml   # Deploy to all nodes
limguard validate --config limguard.yaml  # Validate mesh connectivity only
limguard run --config /etc/limguard/limguard.yaml  # Run daemon
limguard version  # Print version

Joining the Mesh Locally

You can temporarily join the mesh from your local machine (e.g., for operations):

nodes:
  ops-laptop:
    wireguardIP: "10.200.0.50"
    ssh:
      host: self    # Special value: no SSH, configure locally

Run:

limguard apply --config limguard.yaml --local-wireguard-conf-path ops-laptop.conf

It will write a WireGuard config at ops-laptop.conf that you can import in your WireGuard GUI and connect the network.

Once you're done, you can mark ops-laptop for deletion so that its public key is removed from the nodes:

nodes:
  ops-laptop:
    action: Delete
    ...

Run:

limguard apply --config limguard.yaml

How It Works

  • Each node runs limguard run as a service
  • The daemon loads config on startup and configures WireGuard peers
  • To update peers, restart the service after updating the config file
  • Routes through the WireGuard interface are synced to allowed IPs.
    • Allowed IPs include the CIDRs your CNI adds on the host so you have full network capabilities of a LAN.

Versioning

By default, limguard apply downloads binaries from GitHub releases:

  • If version is not set in the config, the latest release is fetched
  • The resolved version is automatically written back to your config file
  • Subsequent runs will use the same version for reproducibility

To upgrade, either:

  • Remove the version field to fetch the latest
  • Set version: vX.Y.Z to pin a specific release

Using Local Binaries

For development or testing, you can use locally built binaries instead of downloading:

nodes:
  node-1:
    wireguardIP: "10.200.0.1"
    endpoint: "203.0.113.10:51820"
    localBinaryPath: "/path/to/limguard-linux-arm64"
    ssh:
      host: "203.0.113.10"

When localBinaryPath is set for a node, that binary is used instead of downloading from GitHub releases. If all nodes have localBinaryPath set, version resolution and downloads are skipped entirely.

Troubleshooting

See OPERATIONS.md for helpful tips.

License

MIT

Documentation

Index

Constants

View Source
const (
	DefaultPrivateKeyPath = "/etc/limguard/privatekey"
	DefaultConfigPath     = "/etc/limguard/limguard.yaml"
	DefaultBinaryPath     = "/usr/local/bin/limguard"
	DefaultListenPort     = 51820
)

Default configuration values.

View Source
const DefaultDarwinInterfaceName = "utun9"

DefaultDarwinInterfaceName is the default WireGuard interface name on macOS. Must be a specific utun interface (e.g., utun9).

View Source
const DefaultLinuxInterfaceName = "wg0"

DefaultLinuxInterfaceName is the default WireGuard interface name on Linux.

View Source
const GitHubRepo = "limrun-inc/limguard"

GitHubRepo is the repository to download releases from.

Variables

View Source
var Version = "v0.0.0"

Version is the current version of limguard, overridden during build.

Functions

func Apply added in v0.9.0

func Apply(ctx context.Context, args []string, log *slog.Logger) error

Apply deploys limguard to remote nodes via SSH.

func EnsurePrivateKey

func EnsurePrivateKey(keyPath string) (wgtypes.Key, error)

EnsurePrivateKey reads or generates a WireGuard private key. Returns an error if the file exists but cannot be read or parsed.

func Run added in v0.9.0

func Run(ctx context.Context, args []string, log *slog.Logger) error

Run starts the limguard daemon. It returns an error if the daemon fails to start or encounters a fatal error.

func Validate added in v0.10.5

func Validate(ctx context.Context, args []string, log *slog.Logger) error

Validate checks mesh connectivity by running peer-to-peer pings across remote nodes.

Types

type ApplyOptions added in v0.9.0

type ApplyOptions struct {
	ConfigPath      string
	SSHKeyPath      string
	LocalWGConfPath string
	Debug           bool
}

ApplyOptions holds options for the Apply command.

type Config added in v0.9.0

type Config struct {
	LinuxInterfaceName  string          `yaml:"linuxInterfaceName,omitempty"`  // Default for Linux nodes
	DarwinInterfaceName string          `yaml:"darwinInterfaceName,omitempty"` // Default for macOS nodes
	Version             string          `yaml:"version,omitempty"`             // GitHub release tag (e.g., v1.0.0); resolved to latest if empty
	Nodes               map[string]Node `yaml:"nodes"`
}

Config is the unified configuration for limguard. The same file is used for deployment and runtime on all nodes.

func LoadConfig added in v0.9.0

func LoadConfig(path string) (*Config, error)

LoadConfig reads and parses a config file.

func (*Config) GetPeers added in v0.9.0

func (c *Config) GetPeers(selfName string) map[string]Node

GetPeers returns all nodes except the given name, excluding nodes marked for deletion.

func (*Config) GetSelf added in v0.9.0

func (c *Config) GetSelf(name string) (Node, bool)

GetSelf returns the node config for the given name.

func (*Config) InterfaceName added in v0.9.0

func (c *Config) InterfaceName(nodeName string) string

InterfaceName returns the WireGuard interface name for a node on the current platform. If the node has a per-node override, that is used. Otherwise, the platform default is used.

func (*Config) NodeListenPort added in v0.9.0

func (c *Config) NodeListenPort(nodeName string) (int, error)

NodeListenPort parses and returns the WireGuard listen port from a node's endpoint. Returns DefaultListenPort if endpoint is empty (for local nodes behind NAT).

func (*Config) PeerEndpoint added in v0.9.0

func (c *Config) PeerEndpoint(peerName string) string

PeerEndpoint returns the endpoint (host:port) for a peer node.

func (*Config) Save added in v0.9.0

func (c *Config) Save(path string) error

Save writes the config back to the given path.

func (*Config) ToYAML added in v0.9.0

func (c *Config) ToYAML() ([]byte, error)

ToYAML serializes the config to YAML.

func (*Config) Validate added in v0.9.0

func (c *Config) Validate() error

Validate checks the config for runtime use. Empty publicKeys are allowed (used during bootstrap for self node).

func (*Config) ValidateForDeploy added in v0.9.0

func (c *Config) ValidateForDeploy() error

ValidateForDeploy checks the config for deployment (SSH info required, public keys optional).

type NetworkManager

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

NetworkManager handles WireGuard interface and peer management on Linux.

func NewNetworkManager

func NewNetworkManager(iface, privateKeyPath string, listenPort int, wireguardIP string, log *slog.Logger) (*NetworkManager, error)

NewNetworkManager creates the WireGuard interface and configures it.

func (*NetworkManager) Close added in v0.9.0

func (nm *NetworkManager) Close() error

Close stops the NetworkManager and releases resources.

func (*NetworkManager) CurrentPeers added in v0.9.0

func (nm *NetworkManager) CurrentPeers() map[string]string

CurrentPeers returns the current peer public keys.

func (*NetworkManager) RemovePeer added in v0.9.0

func (nm *NetworkManager) RemovePeer(ctx context.Context, publicKey string) error

RemovePeer removes a WireGuard peer.

func (*NetworkManager) SetPeer

func (nm *NetworkManager) SetPeer(ctx context.Context, publicKey, endpoint, wireguardIP string) error

SetPeer adds or updates a WireGuard peer.

type Node added in v0.9.0

type Node struct {
	Action          NodeAction `yaml:"action,omitempty"` // Apply (default) or Delete
	WireguardIP     string     `yaml:"wireguardIP"`
	Endpoint        string     `yaml:"endpoint"`                  // Must be host:port format
	PublicKey       string     `yaml:"publicKey,omitempty"`       // Filled in after bootstrap
	InterfaceName   string     `yaml:"interfaceName,omitempty"`   // Per-node override
	LocalBinaryPath string     `yaml:"localBinaryPath,omitempty"` // Local binary to use instead of downloading
	SSH             *SSH       `yaml:"ssh,omitempty"`             // Used only by deploy command
}

Node represents a node in the WireGuard mesh.

func (Node) IsDelete added in v0.9.1

func (n Node) IsDelete() bool

IsDelete returns true if the node is marked for deletion.

func (Node) IsLocal added in v0.9.2

func (n Node) IsLocal() bool

IsLocal returns true if this is a local node (ssh.host: self).

type NodeAction added in v0.9.1

type NodeAction string

NodeAction represents the desired action for a node.

const (
	// NodeActionApply is the default action - ensure the node is configured and running.
	NodeActionApply NodeAction = "Apply"
	// NodeActionDelete removes the node from peers and stops/uninstalls the service.
	NodeActionDelete NodeAction = "Delete"
)

type ReleaseDownloader added in v0.9.0

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

ReleaseDownloader handles downloading binaries from GitHub Releases.

func NewReleaseDownloader added in v0.9.0

func NewReleaseDownloader() (*ReleaseDownloader, error)

NewReleaseDownloader creates a new downloader with a temporary cache directory.

func (*ReleaseDownloader) Cleanup added in v0.9.0

func (d *ReleaseDownloader) Cleanup()

Cleanup removes the cache directory.

func (*ReleaseDownloader) DownloadBinary added in v0.9.0

func (d *ReleaseDownloader) DownloadBinary(ctx context.Context, version, osName, arch string) (string, error)

DownloadBinary downloads the binary for the given version, OS, and architecture. Returns the local path to the downloaded binary. The binary is cached locally and verified against SHA256 checksums from the release.

func (*ReleaseDownloader) ResolveLatestVersion added in v0.9.0

func (d *ReleaseDownloader) ResolveLatestVersion(ctx context.Context) (string, error)

ResolveLatestVersion fetches the latest release tag from GitHub.

type RunOptions added in v0.9.0

type RunOptions struct {
	ConfigPath string
	NodeName   string
	Debug      bool
}

RunOptions holds options for the Run command.

type SSH added in v0.9.0

type SSH struct {
	Host         string  `yaml:"host"`
	Port         int     `yaml:"port,omitempty"`
	User         string  `yaml:"user,omitempty"`
	IdentityFile string  `yaml:"identityFile,omitempty"`
	SudoPassword *string `yaml:"sudoPassword,omitempty"`
}

SSH holds SSH connection details for a node (used only by deploy command).

type ValidateOptions added in v0.10.5

type ValidateOptions struct {
	ConfigPath string
	SSHKeyPath string
	Debug      bool
}

ValidateOptions holds options for the Validate command.

Directories

Path Synopsis
cmd
limguard command

Jump to

Keyboard shortcuts

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