wg-quicker/config.go

326 lines
8.2 KiB
Go
Raw Permalink Normal View History

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 interfaces 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
// 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
// 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{}{
"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 }}
{{- 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-27 17:26:09 +01:00
const (
unknown parseState = iota
inter = iota
peer = iota
2019-03-27 17:26:09 +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{})
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, ",") {
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
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
}