wgctrl-go icon indicating copy to clipboard operation
wgctrl-go copied to clipboard

wgctrl: add a high-level configuration interface

Open mdlayher opened this issue 6 years ago • 6 comments

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()
})

mdlayher avatar May 13 '19 16:05 mdlayher

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.

the-maldridge avatar May 20 '19 05:05 the-maldridge

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)

mdlayher avatar May 20 '19 13:05 mdlayher

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.

JordanDeBeer avatar Feb 15 '20 05:02 JordanDeBeer

@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.

the-maldridge avatar Feb 15 '20 07:02 the-maldridge

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.

stv0g avatar Feb 08 '22 14:02 stv0g

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

SLoeuillet avatar Dec 01 '23 11:12 SLoeuillet