wireguard

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Jan 28, 2026 License: MIT Imports: 21 Imported by: 0

README

WireGuard

A Go implementation of WireGuard protocol for programmatic VPN connections.

Overview

This library provides a pure Go implementation of the WireGuard protocol, allowing you to create and manage WireGuard VPN connections programmatically. It's designed for applications that need to establish WireGuard tunnels without relying on external WireGuard tools or kernel modules.

Features

  • Pure Go Implementation: No external dependencies on WireGuard tools
  • Multiple Peers: Support for multiple peers with flexible routing
  • Custom Dialer: net.Dialer-compatible interface for routing connections through WireGuard
  • Configuration Files: Parse and generate standard WireGuard configuration files
  • Flexible Routing: Support for specific subnets or full internet routing (0.0.0.0/0)
  • HTTP Client Integration: Easy integration with Go's http.Client

Installation

go get github.com/hydrz/wireguard

Requirements

  • Go 1.25.5 or later

Quick Start

package main

import (
    "log"
    "github.com/hydrz/wireguard"
)

func main() {
    // Generate private key
    privateKey, err := wireguard.GeneratePrivateKey()
    if err != nil {
        log.Fatal(err)
    }

    // Create device
    device, err := wireguard.NewDevice(privateKey)
    if err != nil {
        log.Fatal(err)
    }
    defer device.Close()

    // Bind to port
    if err := device.Bind(":51820"); err != nil {
        log.Fatal(err)
    }

    // Add a peer (use the peer's actual public key in production)
    peerPublicKey, _ := wireguard.ParsePublicKey("xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=")
    peer, err := device.AddPeer(
        peerPublicKey,
        wireguard.NoiseSymmetricKey{}, // No preshared key
        "vpn.example.com:51820",
        []string{"10.0.0.2/32"},
    )
    if err != nil {
        log.Fatal(err)
    }

    // Start device
    if err := device.Start(); err != nil {
        log.Fatal(err)
    }

    log.Printf("Device started with public key: %s", 
        wireguard.EncodePublicKey(device.PublicKey()))
}

Usage Examples

Using the Dialer for HTTP Requests

Route HTTP requests through a WireGuard tunnel:

import (
    "context"
    "net/http"
    "time"
    "github.com/hydrz/wireguard"
)

// Note: Error handling omitted for brevity. Always handle errors in production.

// Create device and add peer with full internet routing
privateKey, _ := wireguard.GeneratePrivateKey()
device, _ := wireguard.NewDevice(privateKey)
defer device.Close()

device.Bind(":51820")
device.Start()

// Use the actual peer's public key in production
peerPublicKey, _ := wireguard.ParsePublicKey("xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=")
peer, _ := device.AddPeer(
    peerPublicKey,
    wireguard.NoiseSymmetricKey{},
    "vpn.example.com:51820",
    []string{"0.0.0.0/0"}, // Route all traffic through this peer
)

// Create HTTP client with WireGuard dialer
dialer := wireguard.NewDialer(device, peer)
httpClient := &http.Client{
    Transport: &http.Transport{
        DialContext: dialer.DialContext,
    },
    Timeout: 30 * time.Second,
}

// Make requests - they will go through the WireGuard tunnel
resp, err := httpClient.Get("http://api.ipify.org")
Multiple Exit Nodes

Use multiple peers with 0.0.0.0/0 routing and choose which one to use:

import (
    "context"
    "github.com/hydrz/wireguard"
)

// Note: Error handling omitted for brevity. Always handle errors in production.

// Add multiple exit nodes with their actual public keys
exitNodes := []struct {
    name      string
    endpoint  string
    publicKey string
}{
    {"US Exit Node", "us.vpn.example.com:51820", "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="},
    {"EU Exit Node", "eu.vpn.example.com:51820", "TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0="},
}

peers := make([]*wireguard.Peer, 0)
for _, node := range exitNodes {
    pubKey, _ := wireguard.ParsePublicKey(node.publicKey)
    peer, _ := device.AddPeer(
        pubKey,
        wireguard.NoiseSymmetricKey{},
        node.endpoint,
        []string{"0.0.0.0/0"},
    )
    peers = append(peers, peer)
}

// Use different peers for different connections
ctx := context.Background()
for _, peer := range peers {
    dialer := wireguard.NewDialer(device, peer)
    conn, _ := dialer.DialContext(ctx, "tcp", "example.com:80")
    // Use connection...
}
Configuration Files

Parse and use standard WireGuard configuration files:

import (
    "fmt"
    "log"
    "os"
    "github.com/hydrz/wireguard"
)

// Parse configuration
file, err := os.Open("wg0.conf")
if err != nil {
    log.Fatal(err)
}
config, err := wireguard.ParseConfig(file)
if err != nil {
    log.Fatal(err)
}

// Create device from config
device, err := wireguard.CreateDeviceFromConfig(config)
if err != nil {
    log.Fatal(err)
}
defer device.Close()

device.Start()

// Generate config back
generatedConfig := wireguard.GenerateConfig(device, config.Address)
fmt.Println(generatedConfig)
Configuration File Format

Standard WireGuard configuration format is supported:

[Interface]
PrivateKey = YAnz5TF+lXXJte14tji3zlMNftft3YfudVQxcKE/SXI=
ListenPort = 51820
Address = 10.0.0.1/24
MTU = 1420

[Peer]
PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

[Peer]
PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
Endpoint = 192.168.1.100:51820
AllowedIPs = 10.0.0.2/32

API Documentation

Device

The main interface for managing a WireGuard device:

  • NewDevice(privateKey NoisePrivateKey) (*Device, error) - Create a new device
  • Bind(addr string) error - Bind to a UDP address
  • Start() error - Start the device
  • Close() error - Close the device
  • AddPeer(publicKey NoisePublicKey, presharedKey NoiseSymmetricKey, endpoint string, allowedIPs []string) (*Peer, error) - Add a peer
  • RemovePeer(publicKey NoisePublicKey) error - Remove a peer
  • PublicKey() NoisePublicKey - Get device public key
Dialer

net.Dialer-compatible interface for routing connections:

  • NewDialer(device *Device, peer *Peer) *Dialer - Create a new dialer
  • DialContext(ctx context.Context, network, address string) (net.Conn, error) - Dial with context
  • SetTimeout(timeout time.Duration) - Set dial timeout
  • SetKeepalive(keepalive time.Duration) - Set keepalive interval
Key Management
  • GeneratePrivateKey() (NoisePrivateKey, error) - Generate a new private key
  • ParsePrivateKey(s string) (NoisePrivateKey, error) - Parse base64-encoded private key
  • EncodePrivateKey(key NoisePrivateKey) string - Encode private key to base64
  • EncodePublicKey(key NoisePublicKey) string - Encode public key to base64

Testing

Run the test suite:

go test ./...

Run benchmarks:

go test -bench=. ./...

Run integration tests:

go test -tags=integration ./...

Examples

See the example directory for complete working examples:

cd example
go run main.go

Security Considerations

  • Always use secure key generation: GeneratePrivateKey()
  • Store private keys securely (never commit them to version control)
  • Use preshared keys for additional security when possible
  • Validate peer public keys before establishing connections

License

This project is open source. Please check the repository for license information.

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

Acknowledgments

This implementation follows the WireGuard protocol specification and uses cryptographic primitives from golang.org/x/crypto.

Documentation

Index

Constants

View Source
const (
	DefaultMTU     = 1420
	MinMTU         = 576 // Minimum IPv4 MTU
	MaxMTU         = 65535
	IPv4HeaderSize = 20
	IPv6HeaderSize = 40
	InvalidIndex   = 0 // Invalid peer index value
)
View Source
const (
	MessageInitiationType    = 1
	MessageResponseType      = 2
	MessageCookieReplyType   = 3
	MessageTransportDataType = 4

	MessageInitiationSize      = 148
	MessageResponseSize        = 92
	MessageCookieReplySize     = 64
	MessageTransportHeaderSize = 16
	MessageKeepaliveSize       = MessageTransportHeaderSize

	MessageTransportMaxSize = 65536
)

Variables

View Source
var (
	ErrInvalidPublicKey  = errors.New("invalid public key")
	ErrInvalidPrivateKey = errors.New("invalid private key")
	ErrDecryptionFailed  = errors.New("decryption failed")
)
View Source
var (
	ErrDeviceClosed = errors.New("device closed")
	ErrNoRoute      = errors.New("no route to host")
)
View Source
var (
	ErrDialerClosed      = errors.New("dialer closed")
	ErrNoPeerSpecified   = errors.New("no peer specified")
	ErrNoDeviceSpecified = errors.New("no device specified")
)
View Source
var (
	ErrInvalidMessageSize   = errors.New("invalid message size")
	ErrInvalidMessageType   = errors.New("invalid message type")
	ErrInvalidMessageFormat = errors.New("invalid message format")
)
View Source
var (
	ErrPeerNotFound     = errors.New("peer not found")
	ErrHandshakeTimeout = errors.New("handshake timeout")
	ErrInvalidHandshake = errors.New("invalid handshake")
	ErrCounterExhausted = errors.New("counter exhausted")
)
View Source
var (
	ErrInvalidConfig = errors.New("invalid configuration")
)

Functions

func EncodePrivateKey

func EncodePrivateKey(key NoisePrivateKey) string

EncodePrivateKey encodes a private key to base64

func EncodePublicKey

func EncodePublicKey(key NoisePublicKey) string

EncodePublicKey encodes a public key to base64

func GenerateConfig

func GenerateConfig(device *Device, interfaceAddrs []string) string

GenerateConfig generates a configuration string

Types

type Config

type Config struct {
	PrivateKey string
	ListenPort int
	Address    []string
	MTU        int
	DNS        []string
	Peers      []PeerConfig
}

Config represents a WireGuard configuration

func ParseConfig

func ParseConfig(r io.Reader) (*Config, error)

ParseConfig parses a WireGuard configuration file

type Conn

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

Conn represents a WireGuard connection

func (*Conn) Close

func (c *Conn) Close() error

Close implements net.Conn

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() net.Addr

LocalAddr implements net.Conn

func (*Conn) Read

func (c *Conn) Read(b []byte) (n int, err error)

Read implements net.Conn

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() net.Addr

RemoteAddr implements net.Conn

func (*Conn) SetDeadline

func (c *Conn) SetDeadline(t time.Time) error

SetDeadline implements net.Conn

func (*Conn) SetReadDeadline

func (c *Conn) SetReadDeadline(t time.Time) error

SetReadDeadline implements net.Conn

func (*Conn) SetWriteDeadline

func (c *Conn) SetWriteDeadline(t time.Time) error

SetWriteDeadline implements net.Conn

func (*Conn) Write

func (c *Conn) Write(b []byte) (n int, err error)

Write implements net.Conn

type Device

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

Device represents a WireGuard device

func CreateDeviceFromConfig

func CreateDeviceFromConfig(config *Config) (*Device, error)

CreateDeviceFromConfig creates a device from a configuration

func NewDevice

func NewDevice(privateKey NoisePrivateKey) (*Device, error)

NewDevice creates a new WireGuard device

func (*Device) AddPeer

func (device *Device) AddPeer(publicKey NoisePublicKey, presharedKey NoiseSymmetricKey, endpoint string, allowedIPs []string) (*Peer, error)

AddPeer adds a peer to the device

func (*Device) Bind

func (device *Device) Bind(addr string) error

Bind binds the device to a UDP address

func (*Device) Close

func (device *Device) Close() error

Close closes the device

func (*Device) Conn

func (device *Device) Conn() *net.UDPConn

Conn returns the device's UDP connection

func (*Device) GetPeer

func (device *Device) GetPeer(publicKey NoisePublicKey) (*Peer, error)

GetPeer returns a peer by public key

func (*Device) IsClosed

func (device *Device) IsClosed() bool

IsClosed returns whether the device is closed

func (*Device) LookupPeer

func (device *Device) LookupPeer(ip net.IP) *Peer

LookupPeer finds a peer by destination IP

func (*Device) Peers

func (device *Device) Peers() []*Peer

Peers returns a list of all peers

func (*Device) PublicKey

func (device *Device) PublicKey() NoisePublicKey

PublicKey returns the device's public key

func (*Device) Read

func (device *Device) Read(peer *Peer, data []byte) (int, error)

Read implements io.Reader for a specific peer

func (*Device) ReceivePacket

func (device *Device) ReceivePacket(peer *Peer, timeout time.Duration) ([]byte, error)

ReceivePacket receives a packet from a peer

func (*Device) RemovePeer

func (device *Device) RemovePeer(publicKey NoisePublicKey) error

RemovePeer removes a peer

func (*Device) SendPacket

func (device *Device) SendPacket(data []byte, dstIP net.IP) error

SendPacket sends a packet through the device

func (*Device) SetPacketHandler

func (device *Device) SetPacketHandler(handler func([]byte))

SetPacketHandler sets the handler function for received packets

func (*Device) Start

func (device *Device) Start() error

Start starts the device

func (*Device) Stats

func (device *Device) Stats() (txBytes, rxBytes, txPackets, rxPackets uint64)

Stats returns device statistics

func (*Device) Write

func (device *Device) Write(peer *Peer, data []byte) (int, error)

Write implements io.Writer for a specific peer

type Dialer

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

Dialer implements a net.Dialer-like interface for WireGuard connections

func NewDialer

func NewDialer(device *Device, peer *Peer) *Dialer

NewDialer creates a new Dialer

func (*Dialer) Close

func (d *Dialer) Close() error

Close closes the dialer

func (*Dialer) Dial

func (d *Dialer) Dial(network, address string) (net.Conn, error)

Dial establishes a connection using the configured device and peer

func (*Dialer) DialContext

func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error)

DialContext establishes a connection with context

func (*Dialer) SetDevice

func (d *Dialer) SetDevice(device *Device)

SetDevice sets the device for the dialer

func (*Dialer) SetKeepalive

func (d *Dialer) SetKeepalive(keepalive time.Duration)

SetKeepalive sets the keepalive interval

func (*Dialer) SetPeer

func (d *Dialer) SetPeer(peer *Peer)

SetPeer sets the peer for the dialer

func (*Dialer) SetTimeout

func (d *Dialer) SetTimeout(timeout time.Duration)

SetTimeout sets the connection timeout

type Keypair

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

Keypair represents a symmetric Keypair for transport

func (*Keypair) NextSendNonce

func (kp *Keypair) NextSendNonce() uint64

func (*Keypair) ValidateReceiveNonce

func (kp *Keypair) ValidateReceiveNonce(nonce uint64) bool

type NoisePrivateKey

type NoisePrivateKey [noisePrivateKeySize]byte

NoisePrivateKey represents a Curve25519 private key

func DecodePrivateKey

func DecodePrivateKey(s string) (NoisePrivateKey, error)

DecodePrivateKey decodes a base64 private key

func GeneratePrivateKey

func GeneratePrivateKey() (NoisePrivateKey, error)

GeneratePrivateKey generates a new Curve25519 private key

func (*NoisePrivateKey) PublicKey

func (key *NoisePrivateKey) PublicKey() NoisePublicKey

PublicKey derives the public key from a private key

func (*NoisePrivateKey) SharedSecret

func (key *NoisePrivateKey) SharedSecret(pub NoisePublicKey) (NoiseSymmetricKey, error)

SharedSecret computes the DH shared secret

type NoisePublicKey

type NoisePublicKey [noisePublicKeySize]byte

NoisePublicKey represents a Curve25519 public key

func DecodePublicKey

func DecodePublicKey(s string) (NoisePublicKey, error)

DecodePublicKey decodes a base64 public key

func (*NoisePublicKey) IsZero

func (key *NoisePublicKey) IsZero() bool

IsZero checks if the key is all zeros

type NoiseSymmetricKey

type NoiseSymmetricKey [noiseSymmetricKeySize]byte

NoiseSymmetricKey represents a symmetric encryption key

type Peer

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

Peer represents a WireGuard peer

func NewPeer

func NewPeer(device *Device, publicKey NoisePublicKey, presharedKey NoiseSymmetricKey, endpoint *net.UDPAddr) *Peer

func (*Peer) AddAllowedIP

func (peer *Peer) AddAllowedIP(ipnet net.IPNet)

AddAllowedIP adds an allowed IP range

func (*Peer) ContainsIP

func (peer *Peer) ContainsIP(ip net.IP) bool

ContainsIP checks if an IP is in the allowed range

func (*Peer) CreateMessageResponse

func (peer *Peer) CreateMessageResponse(receiverIndex uint32) (*messageResponse, error)

CreateMessageResponse creates a handshake response message (public interface)

func (*Peer) DecryptPacket

func (peer *Peer) DecryptPacket(data []byte) ([]byte, error)

DecryptPacket decrypts a packet

func (*Peer) EncryptPacket

func (peer *Peer) EncryptPacket(plaintext []byte) ([]byte, error)

EncryptPacket encrypts a packet with the current keypair

func (*Peer) GetEndpoint

func (peer *Peer) GetEndpoint() *net.UDPAddr

GetEndpoint returns the peer's endpoint

func (*Peer) GetKeypair

func (peer *Peer) GetKeypair() *Keypair

GetKeypair returns the current keypair

func (*Peer) PublicKey

func (peer *Peer) PublicKey() NoisePublicKey

PublicKey returns the peer's public key

func (*Peer) SetEndpoint

func (peer *Peer) SetEndpoint(addr *net.UDPAddr)

SetEndpoint updates the peer's endpoint

type PeerConfig

type PeerConfig struct {
	PublicKey           string
	PresharedKey        string
	Endpoint            string
	AllowedIPs          []string
	PersistentKeepalive int
}

PeerConfig represents a Peer section

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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