dwgd

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Apr 8, 2024 License: MIT Imports: 24 Imported by: 0

README

dwgd: Docker WireGuard Driver

dwgd is a Docker plugin that let your containers connect to a WireGuard network. This is achieved by moving a WireGuard network interface from dwgd running namespace into the designated container namespace.

Usage

1. Start the daemon

Start the dwgd daemon:

$ sudo dwgd -d /var/lib/dwgd.db
[...]
2. Create the docker network

Depending on which driver specific options (-o) you pass during the network creation phase, you can select two modes:

  • ifname mode: if you want to connect to a WireGuard interface that's living in the same host as the one you are running your containers on;

  • pubkey mode: if you want to connect to a WireGuard interface that's living in a different host as the one you are running your containers on.

Ifname mode

In this mode, the name of a local WireGuard interface is passed as an option. dwgd will create a new WireGuard interface that will peer to the one you passed and hand it to the containers.

Options that you need to pass:

  • dwgd.ifname: the name of the local interface;
  • dwgd.seed: secret seed that will be used to generate public and private keys by SHA256 hashing the {IP, seed} couple.
docker network create \
    --driver=dwgd \
    -o dwgd.ifname=wg0 \
    -o dwgd.seed=supersecretseed \
    --subnet=10.0.0.0/24 \
    --gateway=10.0.0.1 \
    dwgd-net
Pubkey mode

In this mode, an endpoint and a public key for a WireGuard peer to which containers should connect to are passed as arguments.

Note

Please note that you will likely need to modify manually the configuration of the remote WireGuard peer by adding each container as a peer.

This is doable because public and private keys are deterministically generated by hashing the {IP, seed} couple.

You can generate the public key for an {IP, seed} couple using the following command:

$ dwgd pubkey -s supersecretseed -i 10.0.0.2
oKetpvdq/I/c7hTW6/AtQPqVlSzgx3q2ClWCx/OXS00=

Options that you need to pass:

  • dwgd.pubkey: the public key of the remote WireGuard interface;
  • dwgd.seed: secret seed that will be used to generate public and private keys by SHA256 hashing the {IP, seed} couple;
  • dwgd.endpoint: the endpoint of the WireGuard peer you want your docker containers to connect to.

Create the docker network with the same seed you used to generate the public key:

$ docker network create \
    --driver=dwgd \
    -o dwgd.endpoint=example.com:51820 \
    -o dwgd.seed=supersecretseed \
    -o dwgd.pubkey="your remote WireGuard peer's public key" \
    --subnet=10.0.0.0/24 \
    --gateway=10.0.0.1 \
    dwgd_net
3. Start a container

Note that the IP must be set manually.

$ docker run -it --rm --network=dwgd_net --ip=10.0.0.2 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
5: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue qlen 1000
    link/[65534]
    inet 10.0.0.2/24 brd 10.0.0.255 scope global wg0
       valid_lft forever preferred_lft forever
/ # ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=54 time=9.98 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=54 time=8.65 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=54 time=8.34 ms
^C
--- 10.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 8.343/8.990/9.976/0.708 ms

Installation

This software has been tested in a Linux machine with Debian 12, but I guess it could work on any reasonably recent Linux system that respects the dependencies.

After cloning the repository you can build the binary and optionally install the systemd unit.

$ go build -o /usr/bin/dwgd ./cmd/dwgd.go
$ chmod +x /usr/bin/dwgd
$ install systemd/* /etc/systemd/system/
Dependencies

You need to have WireGuard installed on your system and the iproute2 package: dwgd uses the ip command to create and delete the WireGuard interfaces.

You will also need the nsenter binary if you want dwgd to work with docker rootless.

Development

Please refer to the development directory.

Credits

This is a rewrite of the proof of concept presented in this great article.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DiagnosticsLog = log.New(os.Stderr, "", log.LstdFlags|log.LUTC)

Used for messages that can give the user a context of what the software is doing

View Source
var EventsLog = log.New(os.Stdout, "", log.Lmsgprefix)

Used for everything that can be considered a "result" and should be printed to standard output

View Source
var TraceLog = log.New(&EmptyWriter{}, "", log.LstdFlags|log.LUTC)

Used for very detailed messages, should not be used in a production environment. Disabled by default.

Functions

func GeneratePrivateKey

func GeneratePrivateKey(seed []byte, ip net.IP) *wgtypes.Key

func Jsonify

func Jsonify(data interface{}) string

func NewUnixListener

func NewUnixListener(c commander) (net.Listener, error)

Types

type Client

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

func (*Client) Config

func (c *Client) Config() wgtypes.Config

func (*Client) PeerConfig

func (c *Client) PeerConfig() wgtypes.PeerConfig

type Config

type Config struct {
	Db       string // path to the database
	Verbose  bool   // whether to print debug logs or not
	Rootless bool   // whether to run in rootless compatibility mode or not
}

A Config represents the configuration of an instance of a dwgd driver.

func NewConfig

func NewConfig() *Config

type Driver

type Driver struct {
	network.Driver
	// contains filtered or unexported fields
}

Docker WireGuard Driver

func NewDriver

func NewDriver(dbPath string, c commander, wgc wgController) (*Driver, error)

func (*Driver) Close

func (d *Driver) Close() error

func (*Driver) CreateEndpoint

func (*Driver) CreateNetwork

func (d *Driver) CreateNetwork(r *network.CreateNetworkRequest) error

func (*Driver) DeleteEndpoint

func (d *Driver) DeleteEndpoint(r *network.DeleteEndpointRequest) error

func (*Driver) DeleteNetwork

func (d *Driver) DeleteNetwork(r *network.DeleteNetworkRequest) error

func (*Driver) EndpointInfo

func (d *Driver) EndpointInfo(r *network.InfoRequest) (*network.InfoResponse, error)

func (*Driver) GetCapabilities

func (d *Driver) GetCapabilities() (*network.CapabilitiesResponse, error)

func (*Driver) Join

func (*Driver) Leave

func (d *Driver) Leave(r *network.LeaveRequest) error

type Dwgd

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

func NewDwgd

func NewDwgd(cfg *Config) (*Dwgd, error)

func (*Dwgd) Start

func (d *Dwgd) Start() error

func (*Dwgd) Stop

func (d *Dwgd) Stop() error

type EmptyWriter

type EmptyWriter struct{}

func (*EmptyWriter) Write

func (e *EmptyWriter) Write(p []byte) (n int, err error)

type Network

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

func (*Network) PeerConfig

func (n *Network) PeerConfig() wgtypes.PeerConfig

type RootlessSymlinker

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

func NewRootlessSymlinker

func NewRootlessSymlinker(c commander) (*RootlessSymlinker, error)

func (*RootlessSymlinker) Start

func (r *RootlessSymlinker) Start() error

func (*RootlessSymlinker) Stop

func (r *RootlessSymlinker) Stop() error

type Storage

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

func (*Storage) AddClient

func (s *Storage) AddClient(c *Client) error

func (*Storage) AddNetwork

func (s *Storage) AddNetwork(n *Network) error

func (*Storage) Close

func (s *Storage) Close() error

func (*Storage) GetClient

func (s *Storage) GetClient(id string) (*Client, error)

func (*Storage) GetNetwork

func (s *Storage) GetNetwork(id string) (*Network, error)

func (*Storage) Open

func (s *Storage) Open(path string) error

func (*Storage) RemoveClient

func (s *Storage) RemoveClient(id string) error

func (*Storage) RemoveNetwork

func (s *Storage) RemoveNetwork(id string) error

type UnixListener

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

func (*UnixListener) Accept

func (u *UnixListener) Accept() (net.Conn, error)

func (*UnixListener) Addr

func (u *UnixListener) Addr() net.Addr

func (*UnixListener) Close

func (u *UnixListener) Close() error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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