wgctrl

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 21, 2026 License: MIT Imports: 7 Imported by: 0

README

awgctrl-go

A Go library for controlling WireGuard and AmneziaWG devices on Linux.

This is a fork of WireGuard/wgctrl-go extended with complete AmneziaWG support — reading and writing AWG obfuscation parameters via netlink, parameter validation, and userspace daemon support.

Installation

go get github.com/advanced-wg/awgctrl-go

Requires Go 1.21 or later. Linux only for AWG kernel support; other platforms support standard WireGuard only.

What's different from wgctrl-go

Feature wgctrl-go awgctrl-go
Standard WireGuard
AmneziaWG — write params
AmneziaWG — read params
Auto-generate AWG params
Validate AWG params
Userspace AWG daemon support
Single netlink round-trip

Usage

Read a device
package main

import (
    "fmt"
    "log"

    wgctrl "github.com/advanced-wg/awgctrl-go"
)

func main() {
    client, err := wgctrl.New()
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    device, err := client.Device("awg0")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Name:      %s\n", device.Name)
    fmt.Printf("IsAmnezia: %v\n", device.IsAmnezia)
    fmt.Printf("PublicKey: %s\n", device.PublicKey)

    if device.IsAmnezia {
        fmt.Printf("Jc=%d Jmin=%d Jmax=%d\n", device.Jc, device.Jmin, device.Jmax)
        fmt.Printf("S1=%d S2=%d S3=%d S4=%d\n", device.S1, device.S2, device.S3, device.S4)
        fmt.Printf("H1=%s H2=%s H3=%s H4=%s\n", device.H1, device.H2, device.H3, device.H4)
    }

    for _, peer := range device.Peers {
        fmt.Printf("Peer: %s  RX=%d  TX=%d  LastHandshake=%s\n",
            peer.PublicKey, peer.ReceiveBytes, peer.TransmitBytes, peer.LastHandshakeTime)
    }
}
Configure with auto-generated AWG parameters
package main

import (
    "log"

    wgctrl "github.com/advanced-wg/awgctrl-go"
    "github.com/advanced-wg/awgctrl-go/wgtypes"
)

func main() {
    client, err := wgctrl.New()
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    cfg := &wgtypes.Config{}

    // Populate with randomized, DPI-resistant obfuscation values.
    cfg.GenerateAmneziaParams()

    // Always validate before applying.
    if err := cfg.Validate(); err != nil {
        log.Fatal(err)
    }

    if err := client.ConfigureDevice("awg0", *cfg); err != nil {
        log.Fatal(err)
    }
}
Configure with manual AWG parameters
package main

import (
    "log"

    wgctrl "github.com/advanced-wg/awgctrl-go"
    "github.com/advanced-wg/awgctrl-go/wgtypes"
)

func intPtr(i int) *int    { return &i }
func strPtr(s string) *string { return &s }

func main() {
    client, err := wgctrl.New()
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    cfg := wgtypes.Config{
        Jc:   intPtr(4),
        Jmin: intPtr(80),
        Jmax: intPtr(160),
        S1:   intPtr(30),
        S2:   intPtr(40),
        S3:   intPtr(50),
        S4:   intPtr(8),
        H1:   strPtr("200000000-280000000"),
        H2:   strPtr("400000000-480000000"),
        H3:   strPtr("600000000-680000000"),
        H4:   strPtr("350000000-430000000"),
    }

    if err := cfg.Validate(); err != nil {
        log.Fatal(err)
    }

    if err := client.ConfigureDevice("awg0", cfg); err != nil {
        log.Fatal(err)
    }
}
Add a peer
package main

import (
    "log"
    "net"

    wgctrl "github.com/advanced-wg/awgctrl-go"
    "github.com/advanced-wg/awgctrl-go/wgtypes"
)

func main() {
    client, err := wgctrl.New()
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    pubKey, err := wgtypes.ParseKey("base64encodedpublickey=")
    if err != nil {
        log.Fatal(err)
    }

    _, allowedIP, err := net.ParseCIDR("10.0.0.2/32")
    if err != nil {
        log.Fatal(err)
    }

    cfg := wgtypes.Config{
        Peers: []wgtypes.PeerConfig{
            {
                PublicKey:  pubKey,
                AllowedIPs: []net.IPNet{*allowedIP},
            },
        },
    }

    if err := client.ConfigureDevice("awg0", cfg); err != nil {
        log.Fatal(err)
    }
}

AWG parameter reference

Param Range Description
Jc 0–10 Number of junk packets sent before each handshake
Jmin 64–1024 Minimum junk packet size in bytes
Jmax 64–1024 Maximum junk packet size in bytes (must be ≥ Jmin)
S1 0–64 Padding bytes prepended to Initiation packet
S2 0–64 Padding bytes prepended to Response packet
S3 0–64 Padding bytes prepended to Cookie packet
S4 0–32 Padding bytes prepended to Transport packet
H1 string or range Magic header for Initiation (e.g. "123456789" or "100000000-200000000")
H2 string or range Magic header for Response
H3 string or range Magic header for Cookie
H4 string or range Magic header for Transport
I1I5 string Custom init packet chain (AWG 2.0). If I1 is absent, the entire chain is skipped and AWG behaves as 1.0.

Platform support

Platform Kernel WG Kernel AWG Userspace WG Userspace AWG
Linux
FreeBSD
OpenBSD
Windows

AWG kernel support requires the AmneziaWG kernel module. AWG userspace support requires the amneziawg-go daemon.

Requirements

  • Linux kernel with AmneziaWG module loaded (modprobe amneziawg), or
  • amneziawg-go userspace daemon running
  • Root privileges or CAP_NET_ADMIN capability

License

MIT — Copyright (C) 2018-2022 Matt Layher. See LICENSE.md.

AmneziaWG advanced security per peer

The kernel marks each peer with an AdvancedSecurity flag that indicates whether AWG obfuscation is active for that peer. You must set it explicitly when adding/updating peers on an AWG device:

pubKey, _ := wgtypes.ParseKey("base64encodedpublickey=")
_, allowedIP, _ := net.ParseCIDR("10.0.0.2/32")

cfg := wgtypes.Config{
    Peers: []wgtypes.PeerConfig{
        {
            PublicKey:        pubKey,
            AllowedIPs:       []net.IPNet{*allowedIP},
            AdvancedSecurity: true, // enable AWG obfuscation for this peer
        },
    },
}
client.ConfigureDevice("awg0", cfg)

When reading a device, peer.AdvancedSecurity reflects the kernel's current state for each peer.

I1–I5 tag syntax reference

The kernel supports the following tags in I1–I5 strings (tags can be combined):

Tag Example Description
<r N> <r 20> N random bytes
<b 0xHEX> <b 0xdeadbeef> Literal bytes (hex-encoded)
<c> <c> 4-byte packet counter (big-endian uint32)
<t VAL> <t 1> Timestamp-based field
<rc VAL> <rc 4> Count-based random bytes
<rd VAL> <rd 8> Deterministic random bytes

Tags can be combined in a single field: "<r 10><b 0xff><c>".

GenerateAmneziaParams() uses <r N> only, which is the simplest and most DPI-resistant option.

Documentation

Overview

Copyright (C) 2018-2022 Matt Layher Copyright (C) 2025 Advanced-WG, V. Bantserov

Package wgctrl enables control of WireGuard and AmneziaWG devices on multiple platforms.

For more information on WireGuard, please see https://www.wireguard.com/. For AmneziaWG, see https://github.com/amnezia-vpn/amneziawg-linux-kernel-module.

This package is a fork of WireGuard/wgctrl-go with full AmneziaWG support:

  • Reading AWG parameters (Jc, Jmin, Jmax, S1-S4, H1-H4, I1-I5) via Device()
  • Writing AWG parameters via ConfigureDevice()
  • Automatic parameter generation via Config.GenerateAmneziaParams()
  • Parameter validation via Config.Validate()
  • Userspace AWG daemon support (amneziawg-go)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

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

A Client provides access to WireGuard device information.

func New

func New() (*Client, error)

New creates a new Client.

func (*Client) Close

func (c *Client) Close() error

Close releases resources used by a Client.

All underlying clients are closed regardless of errors. If multiple clients fail to close, their errors are joined with errors.Join so callers can inspect individual errors via errors.Is / errors.As.

func (*Client) ConfigureDevice

func (c *Client) ConfigureDevice(ctx context.Context, name string, cfg wgtypes.Config) error

ConfigureDevice configures a WireGuard device by its interface name.

Because the zero value of some Go types may be significant to WireGuard for Config fields, only fields which are not nil will be applied when configuring a device.

If the device specified by name does not exist or is not a WireGuard device, an error is returned which can be checked using `errors.Is(err, os.ErrNotExist)`.

func (*Client) Device

func (c *Client) Device(ctx context.Context, name string) (*wgtypes.Device, error)

Device retrieves a WireGuard device by its interface name.

If the device specified by name does not exist or is not a WireGuard device, an error is returned which can be checked using `errors.Is(err, os.ErrNotExist)`.

func (*Client) Devices

func (c *Client) Devices(ctx context.Context) ([]*wgtypes.Device, error)

Devices retrieves all WireGuard devices on this system.

When multiple backend clients report the same device (identified by interface name), only the first occurrence is kept. This prevents duplicates when, for example, both kernel and userspace clients discover the same interface.

Directories

Path Synopsis
cmd
wgctrl command
Command wgctrl is a testing utility for interacting with WireGuard via package wgctrl.
Command wgctrl is a testing utility for interacting with WireGuard via package wgctrl.
internal
wgfreebsd
Package wgfreebsd provides internal access to FreeBSD's WireGuard ioctl interface.
Package wgfreebsd provides internal access to FreeBSD's WireGuard ioctl interface.
wgfreebsd/internal/nv
Package nv marshals and unmarshals Go maps to/from FreeBSDs nv(9) name/value lists See: https://www.freebsd.org/cgi/man.cgi?query=nv&sektion=9
Package nv marshals and unmarshals Go maps to/from FreeBSDs nv(9) name/value lists See: https://www.freebsd.org/cgi/man.cgi?query=nv&sektion=9
wgfreebsd/internal/wgh
Package wgh is an auto-generated package which contains constants and types used to access WireGuard information using ioctl calls.
Package wgh is an auto-generated package which contains constants and types used to access WireGuard information using ioctl calls.
wginternal
Package wginternal contains shared internal types for wgctrl.
Package wginternal contains shared internal types for wgctrl.
wglinux
Package wglinux provides internal access to Linux's WireGuard generic netlink interface.
Package wglinux provides internal access to Linux's WireGuard generic netlink interface.
wgopenbsd
Package wgopenbsd provides internal access to OpenBSD's WireGuard ioctl interface.
Package wgopenbsd provides internal access to OpenBSD's WireGuard ioctl interface.
wgopenbsd/internal/wgh
Package wgh is an auto-generated package which contains constants and types used to access WireGuard information using ioctl calls.
Package wgh is an auto-generated package which contains constants and types used to access WireGuard information using ioctl calls.
wgtest
Package wgtest contains shared testing utilities for package wgctrl.
Package wgtest contains shared testing utilities for package wgctrl.
wguser
Package wguser provides internal access to the userspace WireGuard configuration protocol interface.
Package wguser provides internal access to the userspace WireGuard configuration protocol interface.
Package wgtypes provides shared types for the wgctrl family of packages.
Package wgtypes provides shared types for the wgctrl family of packages.

Jump to

Keyboard shortcuts

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