Skip to content

Commit

Permalink
Refactor add to better support library usage
Browse files Browse the repository at this point in the history
This change exposes the template generation so we can more easily reuse
dsnet templates in other applications (i.e. dsnet-gui)
  • Loading branch information
botto committed Dec 28, 2020
1 parent df0a370 commit 7a55db3
Showing 1 changed file with 102 additions and 50 deletions.
152 changes: 102 additions & 50 deletions add.go
Original file line number Diff line number Diff line change
@@ -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 }}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())
}

0 comments on commit 7a55db3

Please sign in to comment.