save work in progress
This commit is contained in:
parent
cdaf0dd68d
commit
9dc7d19811
|
@ -1,3 +1,3 @@
|
|||
config.json
|
||||
test.sh
|
||||
openvpn-dm-mgt-server
|
||||
openvpn-dm-mgt-server.conf
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
config
|
||||
{
|
||||
profiles:
|
||||
{
|
||||
CONTRACT:
|
||||
{
|
||||
servers: [ "dc-11.office.daily","dc-12.office.daily","dc-13.office.daily" ]
|
||||
baseDN: "ou=Users_External,OU=Dailymotion,DC=office,DC=daily",
|
||||
bindCn: "CN=VPN Service,OU=Services,OU=Dailymotion,DC=office,DC=daily",
|
||||
bindPw: "********************",
|
||||
searchFilter: "(&(sAMAccountName=%s))"
|
||||
primaryAttribute: "memberOf"
|
||||
secondaryAttribute: "mail"
|
||||
validGroups:
|
||||
[
|
||||
"CN=SEC_VPN_Users_External,OU=Security,OU=Groups,OU=Dailymotion,DC=office,DC=daily",
|
||||
]
|
||||
otp: "okta"
|
||||
cert: "ignore"
|
||||
ip_range: "192.168.207.1 - 192.168.207.254",
|
||||
routes:
|
||||
[
|
||||
"10.189.10.9 255.255.255.255",
|
||||
"10.190.32.2 255.255.255.255",
|
||||
"10.190.32.20 255.255.255.255",
|
||||
"10.190.22.1 255.255.255.255",
|
||||
"10.190.22.2 255.255.255.255",
|
||||
"188.65.124.35 255.255.255.255",
|
||||
"195.8.215.129 255.255.255.255",
|
||||
"195.8.215.140 255.255.255.255",
|
||||
"10.190.52.100 255.255.255.255",
|
||||
"10.190.62.150 255.255.255.255",
|
||||
]
|
||||
}
|
||||
CORP:
|
||||
{
|
||||
servers: [ "dc-11.office.daily","dc-12.office.daily","dc-13.office.daily" ]
|
||||
baseDN: "OU=Dailymotion,DC=office,DC=daily",
|
||||
bindCn: "CN=VPN Service,OU=Services,OU=Dailymotion,DC=office,DC=daily",
|
||||
bindPw: "********************",
|
||||
searchFilter: "(&(sAMAccountName=%s))"
|
||||
primaryAttribute: "memberOf"
|
||||
secondaryAttribute: "mail"
|
||||
validGroups:
|
||||
[
|
||||
"CN=SEC_VPN,OU=Security,OU=Groups,OU=Dailymotion,DC=office,DC=daily",
|
||||
]
|
||||
otp: "okta"
|
||||
cert: "optionnal"
|
||||
upgrade-to: "DEV"
|
||||
ip_range: "192.168.201.1 - 192.168.203.254"
|
||||
}
|
||||
DEV:
|
||||
{
|
||||
servers: [ "ldap-auth.vip.dailymotion.com" ]
|
||||
baseDN: "dc=dailymotion,dc=com"
|
||||
bindCn: "cn=readonly,dc=dailymotion,dc=com"
|
||||
bindPw: "**********"
|
||||
searchFilter: "(&(mail=%s))"
|
||||
primaryAttribute: "description"
|
||||
secondaryAttribute: "sshPublicKey"
|
||||
upgrade-from: "CORP"
|
||||
upgrade-to: "ADMINS"
|
||||
otp: "okta"
|
||||
cert: "optionnal"
|
||||
ip_range: "192.168.204.1 - 192.168.206.254"
|
||||
}
|
||||
ADMINS:
|
||||
{
|
||||
validGroups:
|
||||
[
|
||||
"infra",
|
||||
"net",
|
||||
"datacenter",
|
||||
]
|
||||
upgrade-from: "DEV"
|
||||
otp: [ "internal", "slack" ]
|
||||
cert: "mandatory"
|
||||
ip_range: "192.168.200.2 - 192.168.200.254"
|
||||
}
|
||||
}
|
||||
cacheDir: "/var/run/openvpn/"
|
||||
masterSecrets: [ "*******************************J" ]
|
||||
vpnLogUrl: "https://install.dm.gg/vpn-log.php"
|
||||
slackToken: "*************************************************************************"
|
||||
slackChannels: [ "#squad-it-office" ]
|
||||
configParser: "/etc/openvpn/roadwarrior_([a-zA-Z0-9]*).conf"
|
||||
mailRelay: "mailrelay.dailymotion.com:25"
|
||||
mailFrom: "engineering-infra@dailymotion.com"
|
||||
ccPwnPassword: "security-incident-report@dailymotion.com"
|
||||
pwnTemplate: "Mime-Version: 1.0;\nContent-Type: text/html; charset=\"ISO-8859-1\";\nContent-Transfer-Encoding: 7bit;\nFrom: {{.MailFrom}}\nSubject: [Dailymotion] Your current okta password is compromised\nTo: {{.Mail}}\nCc: {{.CcPwnPassword}}\n\n<html><body>Hello<br>\n<br>\nWe have detected that you recently connected to the dailymotion's corporate VPN with login {{.Login}} and a password which was part a password-related breach - possibly related to your own account on a third party website - and which is now widely known to hackers.<br>\n<br>\nPlease contact the security team and go to the Okta homepage to change your password immediately : <a href=\"https://dailymotion.okta.com\">https://dailymotion.okta.com</a>/<br>\n<br>\nIf you were using the same unsafe password anywhere else, you should change it everywhere and make sure you use a unique password for every service (password managers make this feasible).<br>\n<br>\nWe remind you that you should always keep your passwords strong and strictly unique, especially when it comes to your dailymotion accounts. A robust password can, for example, be generated using an easily remembered phrase and retaining certain letters: for example, the phrase \"a bird in the hand is worth two in the bush\" would give the password \"1bitH=2itB\" (this example must not be used as a password).<br>\n<br>\nRegards,<br>\n<br>\n--<br>\nThe Dailymotion Security Team</body></html>"
|
||||
newAsTemplate: "From: {{.MailFrom}}\nSubject: A new connection from you to the Dailymotion VPN\nTo: {{.Mail}}\n\nHello\n\nWe have detected a new connection to the vpn from {{.Login}}.\nIt was detected the {{.Time}} coming from the ip {{.IP}} ({{.AsName}}).\n\nIt's not the usual internet provider you connect to the Dailymotion VPN from.\nOr maybe it's the first time you use the VPN from this location.\n\nIf you think there is something suspicious, please contact {{.CcPwnPassword}}\nIf it's you who connected to the VPN, we are sorry for the spam. You won't receive another mail if you connect from this location.\n\nRegards,\n\n--\nThe Dailymotion Infra and Security Teams"
|
||||
slackTemplate: "Hello.\nYou tried to connect to the VPN without an OTP code from your phone app.\nIf you have a Mac, use `{{.Login}}@{{.OTP}}` as your login.\nIf you have a PC with windows or linux, the VPN application should ask you for an OTP code after your login and password.\nThe OTP code is `{{.OTP}}`\n\nPS : you can check <https://wiki.dailymotion.com/display/officeit/OpenVPN#OpenVPN-OneTimePassword|the documentation> to learn how to use a proper OTP application and get rid of these messages"
|
||||
slackTemplate2: "User *{{.Login}}* required the slack OTP token"
|
||||
}
|
37
tcpserver.go
37
tcpserver.go
|
@ -13,11 +13,18 @@ import (
|
|||
|
||||
// Server represents the server
|
||||
type OpenVpnMgt struct {
|
||||
Port string
|
||||
buf *bufio.ReadWriter
|
||||
connected bool
|
||||
m sync.RWMutex
|
||||
ret chan []string
|
||||
Port string
|
||||
buf *bufio.ReadWriter
|
||||
connected bool
|
||||
m sync.RWMutex
|
||||
ret chan []string
|
||||
vpnlogUrl string
|
||||
mailRelay string
|
||||
MailFrom string
|
||||
CcPwnPassword string
|
||||
pwnTemplate string
|
||||
newAsTemplate string
|
||||
syslog bool
|
||||
}
|
||||
|
||||
// NewServer returns a pointer to a new server
|
||||
|
@ -94,8 +101,20 @@ func (s *OpenVpnMgt) ClientDisconnect(line string) {
|
|||
}
|
||||
|
||||
func (s *OpenVpnMgt) ClientConnect(line string) {
|
||||
msg := <-s.ret
|
||||
log.Println(msg)
|
||||
client := NewVPNSession("log in")
|
||||
|
||||
client.ParseSessionId(line)
|
||||
|
||||
infos := <-s.ret
|
||||
|
||||
client.ParseEnv(&infos)
|
||||
|
||||
// err, msg := s.sendCommand([]string{fmt.Sprintf("client-deny %d %d \"Need OTP\" \"CRV1:R:blabla:eC5oZW5uZXI=:OTP Code \"", client.cID, client.kID)})
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// log.Println(msg)
|
||||
|
||||
}
|
||||
|
||||
func (s *OpenVpnMgt) handleConn(conn net.Conn) {
|
||||
|
@ -136,6 +155,8 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
|
|||
switch {
|
||||
// a new openvpn server is connected
|
||||
case strings.HasPrefix(line, ">INFO"):
|
||||
// command sucessfull, we can ignore
|
||||
case strings.HasPrefix(line, ">SUCCESS: client-deny command succeeded"):
|
||||
|
||||
// new bloc for a disconnect event.
|
||||
// We start the receiving handler, which will wait for the Channel message
|
||||
|
@ -154,6 +175,6 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
|
|||
default:
|
||||
response = append(response, line)
|
||||
}
|
||||
log.Print(line)
|
||||
//log.Print(line)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type vpnSession struct {
|
||||
Time time.Time `json:"time"`
|
||||
Login string `json:"username"`
|
||||
Operation string `json:"operation"`
|
||||
Status string `json:"status"`
|
||||
Profile string `json:"profile"`
|
||||
TwoFA bool `json:"2fa_auth"`
|
||||
IP string `json:"client_ip"`
|
||||
PrivIP string `json:"private_ip"`
|
||||
AsNumber string `json:"as_number"`
|
||||
AsName string `json:"as_name"`
|
||||
NewAS bool `json:"as_new"`
|
||||
PwnedPasswd bool `json:"pwned_passwd"`
|
||||
Hostname string `json:"hostname"`
|
||||
TooMuchPwn bool `json:"too_much_pwn"`
|
||||
Mail string `json:"-"`
|
||||
cID int `json:"-"`
|
||||
kID int `json:"-"`
|
||||
port int `json:"-"`
|
||||
dev string `json:"-"`
|
||||
password string `json:"-"`
|
||||
otpCode string `json:"-"`
|
||||
}
|
||||
|
||||
func NewVPNSession(operation string) *vpnSession {
|
||||
v := vpnSession{
|
||||
Time: time.Now().Round(time.Second),
|
||||
Status: "system failure",
|
||||
Operation: operation,
|
||||
}
|
||||
v.Hostname, _ = os.Hostname()
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
func (c *vpnSession) ParseSessionId(line string) error {
|
||||
var err error
|
||||
client_id := strings.Split(strings.Replace(line, ">CLIENT:CONNECT,", "", 1), ",")
|
||||
if c.cID, err = strconv.Atoi(client_id[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.kID, err = strconv.Atoi(client_id[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *vpnSession) ParseEnv(infos *[]string) {
|
||||
for _, line := range *infos {
|
||||
p := strings.Split(strings.Replace(line, ">CLIENT:ENV,", "", 1), "=")
|
||||
switch p[0] {
|
||||
case "trusted_port":
|
||||
c.port, _ = strconv.Atoi(p[1])
|
||||
case "trusted_ip":
|
||||
c.IP = p[1]
|
||||
case "untrusted_port":
|
||||
c.port, _ = strconv.Atoi(p[1])
|
||||
case "untrusted_ip":
|
||||
c.IP = p[1]
|
||||
case "password":
|
||||
switch {
|
||||
case strings.HasPrefix(c.password, "CRV1"):
|
||||
split := strings.Split(c.password, ":")
|
||||
if len(split) != 5 {
|
||||
break
|
||||
}
|
||||
c.password = split[2]
|
||||
c.otpCode = split[4]
|
||||
|
||||
case strings.HasPrefix(c.password, "SCRV1"):
|
||||
split := strings.Split(c.password, ":")
|
||||
if len(split) != 3 {
|
||||
break
|
||||
}
|
||||
data, err := base64.StdEncoding.DecodeString(split[1])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
c.password = string(data)
|
||||
|
||||
data, err = base64.StdEncoding.DecodeString(split[2])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
c.otpCode = string(data)
|
||||
|
||||
default:
|
||||
c.password = p[1]
|
||||
c.otpCode = "***"
|
||||
}
|
||||
|
||||
case "username":
|
||||
c.Login = p[1]
|
||||
case "dev":
|
||||
c.dev = p[1]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *vpnSession) String() string {
|
||||
if res, err := json.MarshalIndent(c, " ", " "); err == nil {
|
||||
return string(res)
|
||||
}
|
||||
return ""
|
||||
}
|
Loading…
Reference in New Issue