2021-01-21 10:25:04 +01:00
|
|
|
|
// nolint: gochecknoglobals, gomnd, goerr113
|
2019-03-27 17:26:09 +01:00
|
|
|
|
package wgquick
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"encoding"
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"text/template"
|
|
|
|
|
"time"
|
2019-03-29 12:40:15 +01:00
|
|
|
|
|
2019-10-30 18:04:10 +01:00
|
|
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
2019-03-27 17:26:09 +01:00
|
|
|
|
)
|
|
|
|
|
|
2021-01-21 10:25:04 +01:00
|
|
|
|
// Config represents full wg-quick like config structure.
|
2019-03-27 17:26:09 +01:00
|
|
|
|
type Config struct {
|
|
|
|
|
wgtypes.Config
|
|
|
|
|
|
2021-01-21 10:25:04 +01:00
|
|
|
|
// Address list of IP (v4 or v6) addresses (optionally with CIDR masks) to be assigned
|
|
|
|
|
// to the interface. May be specified multiple times.
|
2019-03-28 14:22:30 +01:00
|
|
|
|
Address []net.IPNet
|
2019-03-27 17:26:09 +01:00
|
|
|
|
|
2021-01-21 10:25:04 +01:00
|
|
|
|
// list of IP (v4 or v6) addresses to be set as the interface’s DNS servers. May be specified multiple times.
|
|
|
|
|
// Upon bringing the interface up, this runs ‘resolvconf -a tun.INTERFACE -m 0 -x‘ and upon bringing it down,
|
|
|
|
|
// this runs ‘resolvconf -d tun.INTERFACE‘. If these particular invocations of resolvconf(8) are undesirable,
|
|
|
|
|
// the PostUp and PostDown keys below may be used instead.
|
2019-03-27 17:26:09 +01:00
|
|
|
|
DNS []net.IP
|
2019-03-28 12:54:26 +01:00
|
|
|
|
|
2021-01-21 10:25:04 +01:00
|
|
|
|
// MTU is automatically determined from the endpoint addresses or the system default route,
|
|
|
|
|
// which is usually a sane choice. However, to manually specify an MTU to override this automatic discovery,
|
|
|
|
|
// this value may be specified explicitly.
|
2019-03-27 17:26:09 +01:00
|
|
|
|
MTU int
|
|
|
|
|
|
|
|
|
|
// Table — Controls the routing table to which routes are added.
|
|
|
|
|
Table int
|
|
|
|
|
|
2021-01-21 10:25:04 +01:00
|
|
|
|
// PreUp, PostUp, PreDown, PostDown — script snippets which will be executed by bash(1)
|
|
|
|
|
// before/after setting up/tearing down the interface, most commonly used to configure
|
|
|
|
|
// custom DNS options or firewall rules. The special string ‘%i’ is expanded to INTERFACE.
|
|
|
|
|
// Each one may be specified multiple times, in which case the commands are executed in order.
|
2019-03-27 17:26:09 +01:00
|
|
|
|
PreUp string
|
|
|
|
|
PostUp string
|
|
|
|
|
PreDown string
|
|
|
|
|
PostDown string
|
|
|
|
|
|
2019-03-28 14:39:45 +01:00
|
|
|
|
// RouteProtocol to set on the route. See linux/rtnetlink.h Use value > 4 or default 0
|
|
|
|
|
RouteProtocol int
|
|
|
|
|
|
2019-03-29 12:40:15 +01:00
|
|
|
|
// RouteMetric sets this metric on all managed routes. Lower number means pick this one
|
|
|
|
|
RouteMetric int
|
|
|
|
|
|
2019-03-28 14:39:45 +01:00
|
|
|
|
// Address label to set on the link
|
|
|
|
|
AddressLabel string
|
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
// SaveConfig — if set to ‘true’, the configuration is saved from the current state of the interface upon shutdown.
|
|
|
|
|
// Currently unsupported
|
|
|
|
|
SaveConfig bool
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-21 10:25:04 +01:00
|
|
|
|
var (
|
|
|
|
|
_ encoding.TextMarshaler = (*Config)(nil)
|
|
|
|
|
_ encoding.TextUnmarshaler = (*Config)(nil)
|
|
|
|
|
)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
|
|
|
|
|
func (cfg *Config) String() string {
|
|
|
|
|
b, err := cfg.MarshalText()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
return string(b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func serializeKey(key *wgtypes.Key) string {
|
|
|
|
|
return base64.StdEncoding.EncodeToString(key[:])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func toSeconds(duration time.Duration) int {
|
|
|
|
|
return int(duration / time.Second)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var funcMap = template.FuncMap(map[string]interface{}{
|
2019-03-28 12:38:22 +01:00
|
|
|
|
"wgKey": serializeKey,
|
2019-03-27 17:26:09 +01:00
|
|
|
|
"toSeconds": toSeconds,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var cfgTemplate = template.Must(
|
|
|
|
|
template.
|
|
|
|
|
New("wg-cfg").
|
|
|
|
|
Funcs(funcMap).
|
|
|
|
|
Parse(wgtypeTemplateSpec))
|
|
|
|
|
|
|
|
|
|
func (cfg *Config) MarshalText() (text []byte, err error) {
|
|
|
|
|
buff := &bytes.Buffer{}
|
|
|
|
|
if err := cfgTemplate.Execute(buff, cfg); err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return nil, fmt.Errorf("%w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
return buff.Bytes(), nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-21 10:25:04 +01:00
|
|
|
|
// nolint: lll
|
2019-03-27 17:26:09 +01:00
|
|
|
|
const wgtypeTemplateSpec = `[Interface]
|
|
|
|
|
{{- range .Address }}
|
|
|
|
|
Address = {{ . }}
|
|
|
|
|
{{- end }}
|
|
|
|
|
{{- range .DNS }}
|
|
|
|
|
DNS = {{ . }}
|
|
|
|
|
{{- end }}
|
|
|
|
|
PrivateKey = {{ .PrivateKey | wgKey }}
|
|
|
|
|
{{- if .ListenPort }}{{ "\n" }}ListenPort = {{ .ListenPort }}{{ end }}
|
|
|
|
|
{{- if .MTU }}{{ "\n" }}MTU = {{ .MTU }}{{ end }}
|
|
|
|
|
{{- if .Table }}{{ "\n" }}Table = {{ .Table }}{{ end }}
|
|
|
|
|
{{- if .PreUp }}{{ "\n" }}PreUp = {{ .PreUp }}{{ end }}
|
|
|
|
|
{{- if .PostUp }}{{ "\n" }}PostUp = {{ .PostUp }}{{ end }}
|
|
|
|
|
{{- if .PreDown }}{{ "\n" }}PreDown = {{ .PreDown }}{{ end }}
|
|
|
|
|
{{- if .PostDown }}{{ "\n" }}PostDown = {{ .PostDown }}{{ end }}
|
|
|
|
|
{{- if .SaveConfig }}{{ "\n" }}SaveConfig = {{ .SaveConfig }}{{ end }}
|
|
|
|
|
{{- range .Peers }}
|
|
|
|
|
{{- "\n" }}
|
|
|
|
|
[Peer]
|
|
|
|
|
PublicKey = {{ .PublicKey | wgKey }}
|
|
|
|
|
AllowedIPs = {{ range $i, $el := .AllowedIPs }}{{if $i}}, {{ end }}{{ $el }}{{ end }}
|
|
|
|
|
{{- if .PresharedKey }}{{ "\n" }}PresharedKey = {{ .PresharedKey }}{{ end }}
|
2019-04-18 09:50:23 +02:00
|
|
|
|
{{- if .PersistentKeepaliveInterval }}{{ "\n" }}PersistentKeepalive = {{ .PersistentKeepaliveInterval | toSeconds }}{{ end }}
|
2019-03-27 17:26:09 +01:00
|
|
|
|
{{- if .Endpoint }}{{ "\n" }}Endpoint = {{ .Endpoint }}{{ end }}
|
|
|
|
|
{{- end }}
|
|
|
|
|
`
|
|
|
|
|
|
2021-01-21 10:25:04 +01:00
|
|
|
|
// ParseKey parses the base64 encoded wireguard private key.
|
2019-03-27 17:26:09 +01:00
|
|
|
|
func ParseKey(key string) (wgtypes.Key, error) {
|
|
|
|
|
var pkey wgtypes.Key
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
pkeySlice, err := base64.StdEncoding.DecodeString(key)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return pkey, fmt.Errorf("%w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
|
|
|
|
copy(pkey[:], pkeySlice)
|
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
return pkey, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type parseState int
|
2019-03-28 12:38:22 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
const (
|
|
|
|
|
unknown parseState = iota
|
2019-03-28 12:38:22 +01:00
|
|
|
|
inter = iota
|
|
|
|
|
peer = iota
|
2019-03-27 17:26:09 +01:00
|
|
|
|
)
|
2019-03-28 12:38:22 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
func (cfg *Config) UnmarshalText(text []byte) error {
|
|
|
|
|
*cfg = Config{} // Zero out the config
|
|
|
|
|
state := unknown
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
var peerCfg *wgtypes.PeerConfig
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
for no, line := range strings.Split(string(text), "\n") {
|
|
|
|
|
ln := strings.TrimSpace(line)
|
|
|
|
|
if len(ln) == 0 || ln[0] == '#' {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
switch ln {
|
|
|
|
|
case "[Interface]":
|
|
|
|
|
state = inter
|
|
|
|
|
case "[Peer]":
|
|
|
|
|
state = peer
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
cfg.Peers = append(cfg.Peers, wgtypes.PeerConfig{})
|
2019-03-28 12:38:22 +01:00
|
|
|
|
peerCfg = &cfg.Peers[len(cfg.Peers)-1]
|
2019-03-27 17:26:09 +01:00
|
|
|
|
default:
|
|
|
|
|
parts := strings.Split(ln, "=")
|
|
|
|
|
if len(parts) < 2 {
|
|
|
|
|
return fmt.Errorf("cannot parse line %d, missing =", no)
|
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
lhs := strings.TrimSpace(parts[0])
|
|
|
|
|
rhs := strings.TrimSpace(strings.Join(parts[1:], "="))
|
|
|
|
|
|
|
|
|
|
switch state {
|
|
|
|
|
case inter:
|
|
|
|
|
if err := parseInterfaceLine(cfg, lhs, rhs); err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("[line %d]: %w", no+1, err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
|
|
|
|
case peer:
|
|
|
|
|
if err := parsePeerLine(peerCfg, lhs, rhs); err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("[line %d]: %w", no+1, err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
case unknown:
|
2019-03-29 10:48:33 +01:00
|
|
|
|
return fmt.Errorf("[line %d] cannot parse, unknown state", no+1)
|
2021-01-21 10:25:04 +01:00
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("switch couldnt find state")
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
|
|
|
|
// nolint: funlen
|
2019-03-27 17:26:09 +01:00
|
|
|
|
func parseInterfaceLine(cfg *Config, lhs string, rhs string) error {
|
|
|
|
|
switch lhs {
|
|
|
|
|
case "Address":
|
|
|
|
|
for _, addr := range strings.Split(rhs, ",") {
|
|
|
|
|
ip, cidr, err := net.ParseCIDR(strings.TrimSpace(addr))
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("%w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-28 14:22:30 +01:00
|
|
|
|
cfg.Address = append(cfg.Address, net.IPNet{IP: ip, Mask: cidr.Mask})
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
|
|
|
|
case "DNS":
|
|
|
|
|
for _, addr := range strings.Split(rhs, ",") {
|
2019-03-28 12:38:22 +01:00
|
|
|
|
ip := net.ParseIP(strings.TrimSpace(addr))
|
2019-03-27 17:26:09 +01:00
|
|
|
|
if ip == nil {
|
|
|
|
|
return fmt.Errorf("cannot parse IP")
|
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
cfg.DNS = append(cfg.DNS, ip)
|
|
|
|
|
}
|
|
|
|
|
case "MTU":
|
|
|
|
|
mtu, err := strconv.ParseInt(rhs, 10, 64)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("%w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
cfg.MTU = int(mtu)
|
|
|
|
|
case "Table":
|
|
|
|
|
tbl, err := strconv.ParseInt(rhs, 10, 64)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("%w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
cfg.Table = int(tbl)
|
|
|
|
|
case "ListenPort":
|
|
|
|
|
portI64, err := strconv.ParseInt(rhs, 10, 64)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("%w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
port := int(portI64)
|
|
|
|
|
cfg.ListenPort = &port
|
|
|
|
|
case "PreUp":
|
|
|
|
|
cfg.PreUp = rhs
|
|
|
|
|
case "PostUp":
|
|
|
|
|
cfg.PostUp = rhs
|
|
|
|
|
case "PreDown":
|
|
|
|
|
cfg.PreDown = rhs
|
|
|
|
|
case "PostDown":
|
|
|
|
|
cfg.PostDown = rhs
|
|
|
|
|
case "SaveConfig":
|
|
|
|
|
save, err := strconv.ParseBool(rhs)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("%w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
cfg.SaveConfig = save
|
|
|
|
|
case "PrivateKey":
|
|
|
|
|
key, err := ParseKey(rhs)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("cannot decode key %w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
cfg.PrivateKey = &key
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("unknown directive %s", lhs)
|
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parsePeerLine(peerCfg *wgtypes.PeerConfig, lhs string, rhs string) error {
|
|
|
|
|
switch lhs {
|
|
|
|
|
case "PublicKey":
|
|
|
|
|
key, err := ParseKey(rhs)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("cannot decode key %w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
peerCfg.PublicKey = key
|
|
|
|
|
case "PresharedKey":
|
|
|
|
|
key, err := ParseKey(rhs)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("cannot decode key %w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
if peerCfg.PresharedKey != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("preshared key already defined %w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
peerCfg.PresharedKey = &key
|
|
|
|
|
case "AllowedIPs":
|
|
|
|
|
for _, addr := range strings.Split(rhs, ",") {
|
|
|
|
|
ip, cidr, err := net.ParseCIDR(strings.TrimSpace(addr))
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("cannot parse %s: %w", addr, err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-28 12:38:22 +01:00
|
|
|
|
peerCfg.AllowedIPs = append(peerCfg.AllowedIPs, net.IPNet{IP: ip, Mask: cidr.Mask})
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
|
|
|
|
case "Endpoint":
|
|
|
|
|
addr, err := net.ResolveUDPAddr("", rhs)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("%w", err)
|
2019-03-27 17:26:09 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
peerCfg.Endpoint = addr
|
2019-03-29 10:48:33 +01:00
|
|
|
|
case "PersistentKeepalive":
|
|
|
|
|
t, err := strconv.ParseInt(rhs, 10, 64)
|
|
|
|
|
if err != nil {
|
2021-01-21 10:25:04 +01:00
|
|
|
|
return fmt.Errorf("%w", err)
|
2019-03-29 10:48:33 +01:00
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-29 10:48:33 +01:00
|
|
|
|
dur := time.Duration(t * int64(time.Second))
|
|
|
|
|
peerCfg.PersistentKeepaliveInterval = &dur
|
2019-03-27 17:26:09 +01:00
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("unknown directive %s", lhs)
|
|
|
|
|
}
|
2021-01-21 10:25:04 +01:00
|
|
|
|
|
2019-03-27 17:26:09 +01:00
|
|
|
|
return nil
|
2019-03-28 12:38:22 +01:00
|
|
|
|
}
|