From 7a55db3a5f92f802f82e541e6e550efe63d83162 Mon Sep 17 00:00:00 2001 From: Martin Eskdale Moen Date: Mon, 28 Dec 2020 19:39:07 +0000 Subject: [PATCH] Refactor add to better support library usage This change exposes the template generation so we can more easily reuse dsnet templates in other applications (i.e. dsnet-gui) --- add.go | 152 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 50 deletions(-) diff --git a/add.go b/add.go index e30f0d6..026797e 100644 --- a/add.go +++ b/add.go @@ -1,12 +1,29 @@ package dsnet import ( + "bytes" "fmt" "os" "text/template" "time" ) +// PeerConfType is what configuration to use when generating +// peer config files +type PeerConfType int + +const ( + // WGQuick is used by wg-quick to set up a peer + // https://manpages.debian.org/unstable/wireguard-tools/wg-quick.8.en.html + WGQuick PeerConfType = iota + // Vyatta is used by Ubiquiti routers + // https://github.com/WireGuard/wireguard-vyatta-ubnt/ + Vyatta + // NixOS is a declartive linux distro + // https://nixos.wiki/wiki/Wireguard + NixOS +) + const wgQuickPeerConf = `[Interface] {{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}} Address={{ .Peer.IP }}/{{ .CidrSize }} @@ -99,6 +116,74 @@ const nixosPeerConf = `networking.wireguard.interfaces = {{ "{" }} {{ "};" }} ` +func getPeerConfTplString(peerType PeerConfType) string { + switch peerType { + case WGQuick: + return wgQuickPeerConf + case Vyatta: + return vyattaPeerConf + case NixOS: + return nixosPeerConf + default: + ExitFail("Unrecognised peer template type") + return "" + } +} + +func getIfName(conf *DsnetConfig) string { + // derive deterministic interface name + wgifSeed := 0 + for _, b := range conf.IP { + wgifSeed += int(b) + } + + for _, b := range conf.IP6 { + wgifSeed += int(b) + } + return fmt.Sprintf("wg%d", wgifSeed%999) +} + +// GetWGPeerTemplate returns a template file +func GetWGPeerTemplate(peerConfType PeerConfType, peer *PeerConfig, conf *DsnetConfig) (*bytes.Buffer, error) { + peerConf := getPeerConfTplString(peerConfType) + + // See DsnetConfig type for explanation + var endpoint string + + if conf.ExternalHostname != "" { + endpoint = conf.ExternalHostname + } else if len(conf.ExternalIP) > 0 { + endpoint = conf.ExternalIP.String() + } else if len(conf.ExternalIP6) > 0 { + endpoint = conf.ExternalIP6.String() + } else { + ExitFail("Config does not contain ExternalIP, ExternalIP6 or ExternalHostname") + } + + t := template.Must(template.New("peerConf").Parse(peerConf)) + cidrSize, _ := conf.Network.IPNet.Mask.Size() + cidrSize6, _ := conf.Network6.IPNet.Mask.Size() + + var templateBuff bytes.Buffer + err := t.Execute(&templateBuff, map[string]interface{}{ + "Peer": peer, + "DsnetConfig": conf, + "Keepalive": time.Duration(KEEPALIVE).Seconds(), + "CidrSize": cidrSize, + "CidrSize6": cidrSize6, + // vyatta requires an interface in range/format wg0-wg999 + // deterministically choosing one in this range will probably allow use + // of the config without a colliding interface name + "Wgif": getIfName(conf), + "Endpoint": endpoint, + }) + if err != nil { + return nil, err + } + return &templateBuff, nil +} + +// Add prompts for the required information and creates a new peer func Add() { if len(os.Args) != 3 { // TODO non-red @@ -144,67 +229,34 @@ func Add() { ExitFail("No IPv4 or IPv6 network defined in config") } + // TODO Some kind of recovery here would be nice, to avoid + // leaving things in a potential broken state conf.MustAddPeer(peer) - PrintPeerCfg(peer, conf) + PrintPeerCfg(&peer, conf) conf.MustSave() ConfigureDevice(conf) } -func PrintPeerCfg(peer PeerConfig, conf *DsnetConfig) { - var peerConf string - +// PrintPeerCfg outputs a config that can be used by a peer +// to connect, DSNET_OUTPUT is the target peer type +// (i.e. vyatta, wg-quick) +func PrintPeerCfg(peer *PeerConfig, conf *DsnetConfig) { + var peerType PeerConfType + // Translate DSNET_OUTPUT string to enum switch os.Getenv("DSNET_OUTPUT") { - // https://manpages.debian.org/unstable/wireguard-tools/wg-quick.8.en.html case "", "wg-quick": - peerConf = wgQuickPeerConf - // https://github.com/WireGuard/wireguard-vyatta-ubnt/ + peerType = WGQuick case "vyatta": - peerConf = vyattaPeerConf - // https://nixos.wiki/wiki/Wireguard + peerType = Vyatta case "nixos": - peerConf = nixosPeerConf + peerType = NixOS default: ExitFail("Unrecognised DSNET_OUTPUT type") } - - cidrSize, _ := conf.Network.IPNet.Mask.Size() - cidrSize6, _ := conf.Network6.IPNet.Mask.Size() - - // derive deterministic interface name - wgifSeed := 0 - for _, b := range conf.IP { - wgifSeed += int(b) - } - - for _, b := range conf.IP6 { - wgifSeed += int(b) - } - - // See DsnetConfig type for explanation - var endpoint string - - if conf.ExternalHostname != "" { - endpoint = conf.ExternalHostname - } else if len(conf.ExternalIP) > 0 { - endpoint = conf.ExternalIP.String() - } else if len(conf.ExternalIP6) > 0 { - endpoint = conf.ExternalIP6.String() - } else { - ExitFail("Config does not contain ExternalIP, ExternalIP6 or ExternalHostname") - } - - t := template.Must(template.New("peerConf").Parse(peerConf)) - err := t.Execute(os.Stdout, map[string]interface{}{ - "Peer": peer, - "DsnetConfig": conf, - "Keepalive": time.Duration(KEEPALIVE).Seconds(), - "CidrSize": cidrSize, - "CidrSize6": cidrSize6, - // vyatta requires an interface in range/format wg0-wg999 - // deterministically choosing one in this range will probably allow use - // of the config without a colliding interface name - "Wgif": fmt.Sprintf("wg%d", wgifSeed%999), - "Endpoint": endpoint, - }) + // Grab a template writer + t, err := GetWGPeerTemplate(peerType, peer, conf) check(err) + + // Pump out the conf to the stdout + os.Stdout.Write(t.Bytes()) }