-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhelpers.go
156 lines (139 loc) · 4.13 KB
/
helpers.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
// vpnnotify - notify a user of their brand new vpn session!
// helpers.go: a collection of functions
//
// Copyright 2017-2022 F5 Inc.
// Licensed under the BSD 3-clause license; see LICENSE for more information.
package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"os/exec"
"strconv"
"strings"
"time"
"github.com/slack-go/slack"
"gopkg.in/ldap.v2"
"gopkg.in/redis.v5"
)
// whatEnv - determines if you're VPNing into DEV or PROD depending on the
// fqdn of the host. This is, admittedly, not great - and it is frustrating
// that Golang doesnt have some sort of way of getting the output of hostname -f
// "natively" so this will have to do.
func whatEnv() string {
cmd := exec.Command("/bin/hostname", "-f")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
fqdn := out.String()
fqdn = fqdn[:len(fqdn)-1] // removing EOL
env := ""
if strings.Contains(fqdn, "dev") {
env = "dev"
} else if strings.Contains(fqdn, "prod") {
env = "prod"
} else if strings.Contains(fqdn, "bunker") {
env = "bunker"
} else if strings.Contains(fqdn, "ua") {
env = "ua"
} else {
env = "NONE"
}
return env
}
// NewConfig - read in and parse a JSON configuration file into the
// VPNNotifyConfig struct located in types.go.
func NewConfig(fname string) VPNNotifyConfig {
data, err := ioutil.ReadFile(fname)
if err != nil {
panic(err)
}
config := VPNNotifyConfig{}
err = json.Unmarshal(data, &config)
if err != nil {
panic(err)
}
return config
}
// updateState - updates the Redis database with the last time we saw an IP
// connect for a particular user.
func updateState(rcli *redis.Client, lt time.Time, commonName string, untrustedIP string) {
err := rcli.Set(fmt.Sprintf("vpn:%s:lastip", commonName), untrustedIP, 0).Err()
if err != nil {
fmt.Printf("%s VPNNotify: %s couldnt save to redis: %s \n", lt.Format("Mon Jan _2 15:04:05 2006"), commonName, err)
}
err = rcli.Set(fmt.Sprintf("vpn:%s:lasttime", commonName), int64(lt.Unix()), 0).Err()
if err != nil {
fmt.Printf("%s VPNNotify: %s couldnt save to redis: %s \n", lt.Format("Mon Jan _2 15:04:05 2006"), commonName, err)
}
}
// sendSlack - send a slack message to a user informing them of a VPN login.
func sendSlack(key string, recipient string, message string) (err error) {
api := slack.New(key)
opts := slack.MsgOptionCompose(
slack.MsgOptionAsUser(true),
slack.MsgOptionDisableLinkUnfurl(),
slack.MsgOptionText(message, false),
)
// Fire message
channelID, timestamp, err := api.PostMessage(recipient, opts)
if err != nil {
return err
}
timestamp64, err := strconv.ParseFloat(timestamp, 64)
if err != nil {
return err
}
tm := time.Unix(int64(timestamp64), 0).Format("Mon Jan _2 15:04:05 2006")
// This'll go into openvpn.log.
fmt.Printf("%s VPNNotify: message sent to %s (%s)\n", tm, channelID, recipient)
return nil
}
// getSlackName - Get a user's Slack name from LDAP. This requires a new LDAP
// schema entry -- see documentation.
func getSlackName(config VPNNotifyConfig, commonName string) (name string, err error) {
conntimeout := time.Duration(5) * time.Second
server, err := net.DialTimeout("tcp",
fmt.Sprintf("%s:%d", config.LDAPServer, config.LDAPPort), conntimeout)
if err != nil {
return "", err
}
l := ldap.NewConn(server, false)
l.Start()
defer l.Close()
// Need a place to store TLS configuration
tlsConfig := &tls.Config{
InsecureSkipVerify: config.LDAPSkipVerify,
ServerName: config.LDAPServer,
}
// TLS our connection up
err = l.StartTLS(tlsConfig)
if err != nil {
return "", err
}
// Set up an LDAP search and actually do the search
searchRequest := ldap.NewSearchRequest(
config.LDAPBaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(%s=%s)", config.LDAPUserAttrib, commonName),
[]string{"slackName"},
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
return "", err
}
if len(sr.Entries) == 0 {
return "", errors.New("ENOENTRIES")
} else if len(sr.Entries) > 1 {
return "", errors.New("ETOOMANYENTRIES")
}
return fmt.Sprintf("@%s", sr.Entries[0].GetAttributeValue("slackName")), nil
}