WireGuard / wgctrl-go

Package wgctrl enables control of WireGuard interfaces on multiple platforms.
https://godoc.org/golang.zx2c4.com/wireguard/wgctrl
MIT License
730 stars 85 forks source link

wgctrl: add a high-level configuration interface #41

Open mdlayher opened 5 years ago

mdlayher commented 5 years ago

The current interface only implements the raw configuration parameters expected by WireGuard devices, but it could be nice to implement some kind of higher level interface to ease configuration for end users.

Perhaps a transactional interface would be nice (named and exact operations TBD):

m, err := wgctrl.NewManager()

err := m.Apply("wg0", func(tx *wgctrl.Transaction) error {
    tx.SetPrivateKey(wgtypes.GeneratePrivateKey()),
    tx.ClearPeers()
    tx.AddPeer(peer)

    return tx.Commit()
})
the-maldridge commented 5 years ago

here's an example of what I've currently got to configure an interface. Its quite messy and misses certain configuration values, but my use case is just managing keys and addresses. It also is quite messy in terms of time complexity making multiple passes to figure out what to change.

https://github.com/the-maldridge/locksmith/blob/wg-driver/internal/keyhole/util.go#L20

Easily the most annoying part of this is trying to diff out the addresses, which at the time of this comment remains un-implemented. The goal though is to be idempotent in applying desired intent, rather than specifying individual deltas.

EDIT: I realized that checking the addresses wasn't complex, just annoying, so my implementation does that now too.

mdlayher commented 5 years ago

It would seem to me that perhaps it makes sense to expose a couple of APIs here that can be composed with the existing c.ConfigureDevice() method.

package wgctrl

// Converge calculates *wgtypes.Config which applies only the changes needed to update a
// device from the current device configuration to the desired configuration.
func Converge(current *wgtypes.Device, desired *wgtypes.Config) (*wgtypes.Config, error) {}

// A Change is a function which expresses a high level WireGuard device operation, such as
// adding or removing an individual peer.
type Change func(current *wgtypes.Config) (*wgtypes.Config, error)

// Apply produces a *wgtypes.Config expressed via a several of higher level operations, such
// as adding or removing individual peers.
func Apply(changes []Change) (*wgtypes.Config, error) {}

Then, I could envision something like (pseudo-code of course):

c, _ := wgctrl.New()

d, _ := c.Device("wg0")

addPeer, _ := wgctrl.Apply(
    // Maybe it makes more sense to express convergence this way?
    // wgctrl.Initial(d),
    wgctrl.ClearPeers(),
    wgctrl.AddPeer(pubkey, []net.IPNet{"2001:db8::/32"}),
)

conf, _ := wgctrl.Converge(d, addPeer)

_ = c.ConfigureDevice(d.Name, conf)
JordanDeBeer commented 4 years ago

I have a workload that could use a declarative interface. I could look at implementing it here instead of "client-side" if this sort of functionality is desired.

the-maldridge commented 4 years ago

@JordanDeBeer I think that would be a good idea. I would be happy to collaborate here and try to move my implementation here as well. That way there would be one implementation rather than us each having our own in our separate projects.

stv0g commented 2 years ago

I have implemented (de-)serialization of wg/wg-quick configuration files to/from wgtypes.Config here: https://github.com/stv0g/wice/blob/master/internal/wg/device_config.go

I would be willing to contribute this to wgctrl as well.

SLoeuillet commented 7 months ago

If someone is interrested, there is wgrest which uses wgctrl-go code and exposes a REST API to configure wireguard : https://github.com/suquant/wgrest