forked from naggie/dsnet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathadd.go
263 lines (236 loc) · 7.78 KB
/
add.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
package dsnet
import (
"bytes"
"fmt"
"os"
"text/template"
"time"
"github.com/spf13/viper"
)
// 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 }}
{{ end -}}
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
Address={{ .Peer.IP6 }}/{{ .CidrSize6 }}
{{ end -}}
PrivateKey={{ .Peer.PrivateKey.Key }}
{{- if .DsnetConfig.DNS }}
DNS={{ .DsnetConfig.DNS }}
{{ end }}
[Peer]
PublicKey={{ .DsnetConfig.PrivateKey.PublicKey.Key }}
PresharedKey={{ .Peer.PresharedKey.Key }}
Endpoint={{ .Endpoint }}:{{ .DsnetConfig.ListenPort }}
PersistentKeepalive={{ .Keepalive }}
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
AllowedIPs={{ .DsnetConfig.Network }}
{{ end -}}
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
AllowedIPs={{ .DsnetConfig.Network6 }}
{{ end -}}
{{ range .DsnetConfig.Networks -}}
AllowedIPs={{ . }}
{{ end -}}
`
// TODO use random wg0-wg999 to hopefully avoid conflict by default?
const vyattaPeerConf = `configure
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
set interfaces wireguard {{ .Wgif }} address {{ .Peer.IP }}/{{ .CidrSize }}
{{ end -}}
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
set interfaces wireguard {{ .Wgif }} address {{ .Peer.IP6 }}/{{ .CidrSize6 }}
{{ end -}}
set interfaces wireguard {{ .Wgif }} route-allowed-ips true
set interfaces wireguard {{ .Wgif }} private-key {{ .Peer.PrivateKey.Key }}
set interfaces wireguard {{ .Wgif }} description {{ .DsnetConfig.InterfaceName }}
{{- if .DsnetConfig.DNS }}
#set service dns forwarding name-server {{ .DsnetConfig.DNS }}
{{ end }}
set interfaces wireguard {{ .Wgif }} peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} endpoint {{ .Endpoint }}:{{ .DsnetConfig.ListenPort }}
set interfaces wireguard {{ .Wgif }} peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} persistent-keepalive {{ .Keepalive }}
set interfaces wireguard {{ .Wgif }} peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} preshared-key {{ .Peer.PresharedKey.Key }}
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
set interfaces wireguard {{ .Wgif }} peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} allowed-ips {{ .DsnetConfig.Network }}
{{ end -}}
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
set interfaces wireguard {{ .Wgif }} peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} allowed-ips {{ .DsnetConfig.Network6 }}
{{ end -}}
{{ range .DsnetConfig.Networks -}}
set interfaces wireguard {{ .Wgif }} peer {{ .DsnetConfig.PrivateKey.PublicKey.Key }} allowed-ips {{ . }}
{{ end -}}
commit; save
`
const nixosPeerConf = `networking.wireguard.interfaces = {{ "{" }}
dsnet = {{ "{" }}
ips = [
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
"{{ .Peer.IP }}/{{ .CidrSize }}"
{{ end -}}
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
"{{ .Peer.IP6 }}/{{ .CidrSize6 }}"
{{ end -}}
];
privateKey = "{{ .Peer.PrivateKey.Key }}";
{{- if .DsnetConfig.DNS }}
dns = [ "{{ .DsnetConfig.DNS }}" ];
{{ end }}
peers = [
{{ "{" }}
publicKey = "{{ .DsnetConfig.PrivateKey.PublicKey.Key }}";
presharedKey = "{{ .Peer.PresharedKey.Key }}";
allowedIPs = [
{{ if gt (.DsnetConfig.Network.IPNet.IP | len) 0 -}}
"{{ .DsnetConfig.Network }}"
{{ end -}}
{{ if gt (.DsnetConfig.Network6.IPNet.IP | len) 0 -}}
"{{ .DsnetConfig.Network6 }}"
{{ end -}}
];
endpoint = "{{ .Endpoint }}:{{ .DsnetConfig.ListenPort }}";
persistentKeepalive = {{ .Keepalive }};
{{ "}" }}
];
{{ "};" }}
{{ "};" }}
`
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 string to be used when
// configuring a peer
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(hostname, owner, description string, confirm bool) {
// TODO accept existing pubkey
conf := MustLoadDsnetConfig()
if owner == "" {
owner = MustPromptString("owner", true)
}
if description == "" {
description = MustPromptString("Description", true)
}
// publicKey := MustPromptString("PublicKey (optional)", false)
if !confirm {
ConfirmOrAbort("\nDo you want to add the above configuration?")
}
// newline (not on stdout) to separate config
fmt.Fprintln(os.Stderr)
privateKey := GenerateJSONPrivateKey()
publicKey := privateKey.PublicKey()
peer := PeerConfig{
Owner: owner,
Hostname: hostname,
Description: description,
Added: time.Now(),
PublicKey: publicKey,
PrivateKey: privateKey, // omitted from server config JSON!
PresharedKey: GenerateJSONKey(),
Networks: []JSONIPNet{},
}
if len(conf.Network.IPNet.Mask) > 0 {
peer.IP = conf.MustAllocateIP()
}
if len(conf.Network6.IPNet.Mask) > 0 {
peer.IP6 = conf.MustAllocateIP6()
}
if len(conf.IP) == 0 && len(conf.IP6) == 0 {
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)
conf.MustSave()
MustConfigureDevice(conf)
}
// 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 viper.GetString("output") {
case "wg-quick":
peerType = WGQuick
case "vyatta":
peerType = Vyatta
case "nixos":
peerType = NixOS
default:
ExitFail("Unrecognised OUTPUT type")
}
// Grab a template writer
t, err := GetWGPeerTemplate(peerType, peer, conf)
check(err)
// Pump out the conf to the stdout
os.Stdout.Write(t.Bytes())
}