-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathenv.go
389 lines (333 loc) · 11.8 KB
/
env.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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
package vpncscript
import (
"encoding/xml"
"fmt"
"os"
"strconv"
"strings"
log "github.com/sirupsen/logrus"
)
// env contains openconnect parameters passed through the environment:
//
// List of parameters passed through environment
//
// - reason -- why this script was called, one of:
// -- pre-init connect disconnect reconnect
// -- attempt-reconnect
//
// - VPNGATEWAY -- VPN gateway address (always present)
//
// - VPNPID -- PID of the process controlling the VPN (OpenConnect v9.0+)
//
// - TUNDEV -- tunnel device (always present)
//
// - IDLE_TIMEOUT -- gateway's idle timeout in seconds (OpenConnect v8.06+); unused
//
// - INTERNAL_IP4_ADDRESS -- address (always present)
//
// - INTERNAL_IP4_MTU -- MTU (often unset)
//
// - INTERNAL_IP4_NETMASK -- netmask (often unset)
//
// - INTERNAL_IP4_NETMASKLEN -- netmask length (often unset)
//
// - INTERNAL_IP4_NETADDR -- address of network (only present if netmask is set)
//
// - INTERNAL_IP4_DNS -- list of DNS servers
//
// - INTERNAL_IP4_NBNS -- list of WINS servers
//
// - INTERNAL_IP6_ADDRESS -- IPv6 address
//
// - INTERNAL_IP6_NETMASK -- IPv6 netmask
//
// - INTERNAL_IP6_DNS -- IPv6 list of dns servers
//
// - CISCO_DEF_DOMAIN -- default domain name
//
// - CISCO_BANNER -- banner from server
//
// - CISCO_SPLIT_DNS -- DNS search domain list
//
// - CISCO_SPLIT_INC -- number of networks in split-network-list
//
// - CISCO_SPLIT_INC_%d_ADDR -- network address
//
// - CISCO_SPLIT_INC_%d_MASK -- subnet mask (for example: 255.255.255.0)
//
// - CISCO_SPLIT_INC_%d_MASKLEN -- subnet masklen (for example: 24)
//
// - CISCO_SPLIT_INC_%d_PROTOCOL -- protocol (often just 0); unused
//
// - CISCO_SPLIT_INC_%d_SPORT -- source port (often just 0); unused
//
// - CISCO_SPLIT_INC_%d_DPORT -- destination port (often just 0); unused
//
// - CISCO_IPV6_SPLIT_INC -- number of networks in IPv6 split-network-list
//
// - CISCO_IPV6_SPLIT_INC_%d_ADDR -- IPv6 network address
//
// - CISCO_IPV6_SPLIT_INC_$%d_MASKLEN -- IPv6 subnet masklen
//
// The split tunnel variables above have *_EXC* counterparts for network
// addresses to be excluded from the VPN tunnel.
//
// Other/undocumented environment variables
// - CISCO_CSTP_OPTIONS -- List of all CSTP headers, includes dynamic
// -- DNS-based Split Exclude and Bypass Virtual
// -- Subnets Only settings in nested
// -- X-CSTP-Post-Auth-XML header
type env struct {
// general settings
reason string
vpnGateway string
vpnPID string
tunDev string
idleTimeout string
// IPv4 settings
internalIP4Address string
internalIP4MTU string
internalIP4Netmask string
internalIP4NetmaskLen string
internalIP4NetAddr string
internalIP4DNS string
internalIP4NBNS string
// IPv6 settings
internalIP6Address string
internalIP6Netmask string
internalIP6DNS string
// cisco settings
ciscoDefDomain string
ciscoBanner string
ciscoSplitDNS string
ciscoSplitInc []string
ciscoSplitExc []string
ciscoIPv6SplitInc []string
ciscoIPv6SplitExc []string
// other/undocumented settings
ciscoCSTPOptions []string
dnsSplitExc []string
bypassVirtualSubnetsOnlyV4 bool
disableAlwaysOnVPN bool
// openconnect daemon token
token string
}
// parseEnvironmentSplit parses split include/exclude parameters identified by
// prefix and returns them; note: only uses ADDR and MASKLEN
// TODO: this might not work with IPv6, check and fix it
func parseEnvironmentSplit(prefix string) []string {
splits := []string{}
// get number of settings
num := os.Getenv(prefix)
if num == "" {
return splits
}
// make sure it is a number
n, err := strconv.Atoi(num)
if err != nil {
log.WithError(err).Error("VPNCScript could not convert number of split settings")
return splits
}
// parse all addresses and masklens
for i := 0; i < n; i++ {
// construct name of address and masklen environment variables
addr := fmt.Sprintf("%s_%d_ADDR", prefix, i)
masklen := fmt.Sprintf("%s_%d_MASKLEN", prefix, i)
// get values of address and masklen environment variables
a := os.Getenv(addr)
m := os.Getenv(masklen)
// add address/masklen to split parameters
if a == "" || m == "" {
continue
}
s := fmt.Sprintf("%s/%s", a, m)
splits = append(splits, s)
}
// return all split parameters
return splits
}
// parseDNSSplitExcXML parses the DNS-based Split Exclude List contained in
// postAuthXML
func parseDNSSplitExcXML(postAuthXML string) []string {
type DNSSplitExc struct {
Domains string `xml:"config>opaque>custom-attr>dynamic-split-exclude-domains"`
}
d := &DNSSplitExc{}
err := xml.Unmarshal([]byte(postAuthXML), d)
if err != nil {
log.WithError(err).
Error("VPNCScript could not parse split excludes in post auth XML")
return nil
}
return strings.Split(d.Domains, ",")
}
// parseBypassVSubnetsXML parses the Bypass Virtual Subnets Only V4 setting
// contained in postAuthXML
func parseBypassVSubnetsXML(postAuthXML string) bool {
type BypassVSubnets struct {
Bypass bool `xml:"config>opaque>custom-attr>BypassVirtualSubnetsOnlyV4"`
}
b := &BypassVSubnets{}
err := xml.Unmarshal([]byte(postAuthXML), b)
if err != nil {
log.WithError(err).
Error("VPNCScript could not parse bypass virtual subnets in post auth XML")
return false
}
return b.Bypass
}
// getPostAuthXML gets the post auth xml from ciscoCSTPOptions
func getPostAuthXML(ciscoCSTPOptions []string) string {
for _, opt := range ciscoCSTPOptions {
pair := strings.SplitN(opt, "=", 2)
key := pair[0]
if key != "X-CSTP-Post-Auth-XML" {
continue
}
if len(pair) != 2 || pair[1] == "" {
return ""
}
value := pair[1]
return value
}
return ""
}
// parseDNSSplitExc parses the DNS-based Split Exclude List contained in
// ciscoCSTPOptions
func parseDNSSplitExc(ciscoCSTPOptions []string) []string {
xml := getPostAuthXML(ciscoCSTPOptions)
if xml != "" {
return parseDNSSplitExcXML(xml)
}
return nil
}
// parseBypassVSubnets parses the bypass virtual subnets only v4 setting in
// ciscoCSTPOptions
func parseBypassVSubnets(ciscoCSTPOptions []string) bool {
xml := getPostAuthXML(ciscoCSTPOptions)
if xml != "" {
return parseBypassVSubnetsXML(xml)
}
return false
}
// parseDisableAlwaysOnVPN parses the disable always on vpn setting in
// ciscoCSTPOptions
func parseDisableAlwaysOnVPN(ciscoCSTPOptions []string) bool {
for _, opt := range ciscoCSTPOptions {
pair := strings.SplitN(opt, "=", 2)
key := pair[0]
if key != "X-CSTP-Disable-Always-On-VPN" {
continue
}
if len(pair) != 2 || pair[1] != "true" {
return false
}
return true
}
return false
}
// parseEnvironment parses environment variables and collects
// openconnect settings
func parseEnvironment() *env {
e := &env{}
// parse reason:
// reason -- why this script was called, one of:
// pre-init connect disconnect reconnect attempt-reconnect
// TODO: check values
e.reason = os.Getenv("reason")
// parse vpn gateway:
// VPNGATEWAY -- VPN gateway address (always present)
e.vpnGateway = os.Getenv("VPNGATEWAY")
// parse vpn PID:
// VPNPID -- PID of the process controlling the VPN (OpenConnect v9.0+)
e.vpnPID = os.Getenv("VPNPID")
// parse tunnel device:
// TUNDEV -- tunnel device (always present)
e.tunDev = os.Getenv("TUNDEV")
// parse idle timeout
// IDLE_TIMEOUT -- gateway's idle timeout in seconds (OpenConnect
// v8.06+); unused
e.idleTimeout = os.Getenv("IDLE_TIMEOUT")
// parse internal ipv4 address
// INTERNAL_IP4_ADDRESS -- address (always present)
e.internalIP4Address = os.Getenv("INTERNAL_IP4_ADDRESS")
// parse internal ipv4 mtu
// INTERNAL_IP4_MTU -- MTU (often unset)
e.internalIP4MTU = os.Getenv("INTERNAL_IP4_MTU")
// parse internal ipv4 netmask
// INTERNAL_IP4_NETMASK -- netmask (often unset)
e.internalIP4Netmask = os.Getenv("INTERNAL_IP4_NETMASK")
// parse internal ipv4 netmask length
// INTERNAL_IP4_NETMASKLEN -- netmask length (often unset)
e.internalIP4NetmaskLen = os.Getenv("INTERNAL_IP4_NETMASKLEN")
// parse internal ipv4 network address
// INTERNAL_IP4_NETADDR -- address of network (only present if netmask
// is set)
e.internalIP4NetAddr = os.Getenv("INTERNAL_IP4_NETADDR")
// parse internal ipv4 dns servers
// INTERNAL_IP4_DNS -- list of DNS servers
e.internalIP4DNS = os.Getenv("INTERNAL_IP4_DNS")
// parse internal ipv4 wins servers
// INTERNAL_IP4_NBNS -- list of WINS servers
e.internalIP4NBNS = os.Getenv("INTERNAL_IP4_NBNS")
// parse internal ipv6 address
// INTERNAL_IP6_ADDRESS -- IPv6 address
e.internalIP6Address = os.Getenv("INTERNAL_IP6_ADDRESS")
// parse internal ipv6 netmask
// INTERNAL_IP6_NETMASK -- IPv6 netmask
e.internalIP6Netmask = os.Getenv("INTERNAL_IP6_NETMASK")
// parse internal ipv6 dns servers
// INTERNAL_IP6_DNS -- IPv6 list of dns servers
e.internalIP6DNS = os.Getenv("INTERNAL_IP6_DNS")
// parse default domain
// CISCO_DEF_DOMAIN -- default domain name
e.ciscoDefDomain = os.Getenv("CISCO_DEF_DOMAIN")
// parse banner
// CISCO_BANNER -- banner from server
e.ciscoBanner = os.Getenv("CISCO_BANNER")
// parse split dns
// CISCO_SPLIT_DNS -- DNS search domain list
e.ciscoSplitDNS = os.Getenv("CISCO_SPLIT_DNS")
// parse split include ipv4 network list
// CISCO_SPLIT_INC -- number of networks in
// split-network-list
// CISCO_SPLIT_INC_%d_ADDR -- network address
// CISCO_SPLIT_INC_%d_MASK -- subnet mask (for example:
// 255.255.255.0)
// CISCO_SPLIT_INC_%d_MASKLEN -- subnet masklen (for example: 24)
// CISCO_SPLIT_INC_%d_PROTOCOL -- protocol (often just 0); unused
// CISCO_SPLIT_INC_%d_SPORT -- source port (often just 0); unused
// CISCO_SPLIT_INC_%d_DPORT -- destination port (often just 0);
// unused
e.ciscoSplitInc = parseEnvironmentSplit("CISCO_SPLIT_INC")
// parse split exclude ipv4 network list
e.ciscoSplitExc = parseEnvironmentSplit("CISCO_SPLIT_EXC")
// parse split include ipv6 network list
// CISCO_IPV6_SPLIT_INC -- number of networks in IPv6
// split-network-list
// CISCO_IPV6_SPLIT_INC_%d_ADDR -- IPv6 network address
// CISCO_IPV6_SPLIT_INC_$%d_MASKLEN -- IPv6 subnet masklen
e.ciscoIPv6SplitInc = parseEnvironmentSplit("CISCO_IPV6_SPLIT_INC")
// parse split exclude ipv6 network list
e.ciscoIPv6SplitExc = parseEnvironmentSplit("CISCO_IPV6_SPLIT_EXC")
// parse cstp options
// CISCO_CSTP_OPTIONS -- List of all CSTP headers, includes dynamic
// DNS-based Split Exclude settings in nested
// X-CSTP-Post-Auth-XML header
e.ciscoCSTPOptions = strings.Split(os.Getenv("CISCO_CSTP_OPTIONS"), "\n")
// parse dynamic dns-based split exclude list
e.dnsSplitExc = parseDNSSplitExc(e.ciscoCSTPOptions)
// parse bypass virtual subnets only v4 setting
e.bypassVirtualSubnetsOnlyV4 = parseBypassVSubnets(e.ciscoCSTPOptions)
// parse Disable Always On VPN
e.disableAlwaysOnVPN = parseDisableAlwaysOnVPN(e.ciscoCSTPOptions)
// parse openconnect daemon token
e.token = os.Getenv("oc_daemon_token")
return e
}
// printDebugEnvironment prints all environment variables as debug output
func printDebugEnvironment() {
for _, e := range os.Environ() {
log.WithField("variable", e).Debug("VPNCScript got environment variable")
}
}