manage auth-retry
This commit is contained in:
parent
7418a70afc
commit
1c02c700fa
12
main.go
12
main.go
@ -15,9 +15,10 @@ import (
|
||||
func main() {
|
||||
var err error
|
||||
var config *uconfig.UConfig
|
||||
// default configuration file is ./openvpn-dm-mgt-server.conf
|
||||
configFile := flag.String("config", "openvpn-dm-mgt-server.conf", "configuration file")
|
||||
// default configuration file is /etc/openvpn/dm-mgt-server.conf
|
||||
configFile := flag.String("config", "/etc/openvpn/dm-mgt-server.conf", "configuration file")
|
||||
logToSyslog := flag.Bool("syslog", false, "Log to syslog")
|
||||
debug := flag.Bool("debug", false, "log every message received")
|
||||
flag.Parse()
|
||||
|
||||
// parseconfig
|
||||
@ -39,6 +40,8 @@ func main() {
|
||||
server.cacheDir = config.GetString("config.cacheDir", "")
|
||||
server.authCa = config.GetString("config.authCa", "")
|
||||
server.otpMasterSecrets = parseConfigArray(config, "config.masterSecrets")
|
||||
server.ipRouteScript = config.GetString("config.ipRouteScript", "/bin/ip")
|
||||
|
||||
if len(server.otpMasterSecrets) == 0 {
|
||||
server.otpMasterSecrets = append(server.otpMasterSecrets, "*******************")
|
||||
}
|
||||
@ -54,6 +57,11 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
server.debug = false
|
||||
if *debug {
|
||||
server.debug = true
|
||||
}
|
||||
|
||||
for _, profile := range config.GetPaths("config.profiles") {
|
||||
profileName := strings.Split(profile, ".")[2]
|
||||
ldapConf := ldapConfig{
|
||||
|
@ -83,6 +83,7 @@ config
|
||||
}
|
||||
openvpnPort: "127.0.0.1:4000"
|
||||
httpPort: ":8443"
|
||||
ipRouteScript: "./iproute"
|
||||
httpCa: "/usr/local/share/ca-certificates/Dailymotion.crt"
|
||||
httpKey: "/etc/ssl/private/server-key.pem"
|
||||
httpCert: "/etc/ssl/certs/server-bundle.pem"
|
||||
|
89
vpnserver.go
89
vpnserver.go
@ -11,11 +11,13 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
hibp "github.com/mattevans/pwned-passwords"
|
||||
)
|
||||
|
||||
// Server represents the server
|
||||
type OpenVpnMgt struct {
|
||||
Port string
|
||||
port string
|
||||
buf map[string]*bufio.ReadWriter
|
||||
m sync.RWMutex
|
||||
ret chan []string
|
||||
@ -30,24 +32,28 @@ type OpenVpnMgt struct {
|
||||
newAsTemplate string
|
||||
cacheDir string
|
||||
syslog bool
|
||||
ipRouteScript string
|
||||
otpMasterSecrets []string
|
||||
hibpClient *hibp.Client
|
||||
debug bool
|
||||
}
|
||||
|
||||
// NewServer returns a pointer to a new server
|
||||
func NewVPNServer(port string) *OpenVpnMgt {
|
||||
return &OpenVpnMgt{
|
||||
Port: port,
|
||||
port: port,
|
||||
ret: make(chan []string),
|
||||
ldap: make(map[string]ldapConfig),
|
||||
buf: make(map[string]*bufio.ReadWriter),
|
||||
clients: make(map[string]map[int]*vpnSession),
|
||||
hibpClient: hibp.NewClient(),
|
||||
}
|
||||
}
|
||||
|
||||
// Run starts a the server
|
||||
func (s *OpenVpnMgt) Run() {
|
||||
// Resolve the passed port into an address
|
||||
addrs, err := net.ResolveTCPAddr("tcp", s.Port)
|
||||
addrs, err := net.ResolveTCPAddr("tcp", s.port)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
@ -71,12 +77,25 @@ func (s *OpenVpnMgt) Run() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OpenVpnMgt) CheckPwn(c *vpnSession) error {
|
||||
c.LogPrintln("checking pwn password")
|
||||
pwned, err := s.hibpClient.Pwned.Compromised(c.password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.PwnedPasswd = pwned
|
||||
return nil
|
||||
}
|
||||
|
||||
// send a command to the server. Set the channel to receive the response
|
||||
func (s *OpenVpnMgt) sendCommand(msg []string, remote string) (error, []string) {
|
||||
if len(s.buf) == 0 {
|
||||
return errors.New("No openvpn server present"), nil
|
||||
}
|
||||
for _, line := range msg {
|
||||
if s.debug {
|
||||
log.Println(line)
|
||||
}
|
||||
if _, err := s.buf[remote].WriteString(line + "\r\n"); err != nil {
|
||||
return err, nil
|
||||
}
|
||||
@ -88,6 +107,13 @@ func (s *OpenVpnMgt) sendCommand(msg []string, remote string) (error, []string)
|
||||
|
||||
// wait for the response
|
||||
ret := <-s.ret
|
||||
|
||||
if s.debug {
|
||||
for _, line := range ret {
|
||||
log.Println(line)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ret
|
||||
}
|
||||
|
||||
@ -136,7 +162,7 @@ func (s *OpenVpnMgt) ClientValidated(line, remote string) {
|
||||
c.Status = "success"
|
||||
infos := <-s.ret
|
||||
|
||||
if err := c.ParseEnv(&infos); err != nil {
|
||||
if err := c.ParseEnv(s, &infos); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
@ -159,7 +185,8 @@ func (s *OpenVpnMgt) ClientDisconnect(line, remote string) {
|
||||
}
|
||||
|
||||
// Don't log the initial auth failure due to absence of OTP code
|
||||
if c.Status != "Need OTP Code" {
|
||||
// And don't log the auth failure during re auth
|
||||
if c.Operation != "re auth" && c.Status != "Need OTP Code" {
|
||||
s.Log(c)
|
||||
}
|
||||
|
||||
@ -168,12 +195,12 @@ func (s *OpenVpnMgt) ClientDisconnect(line, remote string) {
|
||||
|
||||
// called at the initial connexion
|
||||
func (s *OpenVpnMgt) ClientConnect(line, remote string) {
|
||||
c := NewVPNSession("log in")
|
||||
c := NewVPNSession()
|
||||
c.vpnserver = remote
|
||||
c.ParseSessionId(line)
|
||||
s.clients[remote][c.cID] = c
|
||||
infos := <-s.ret
|
||||
if err := c.ParseEnv(&infos); err != nil {
|
||||
if err := c.ParseEnv(s, &infos); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
@ -181,6 +208,27 @@ func (s *OpenVpnMgt) ClientConnect(line, remote string) {
|
||||
c.Auth(s)
|
||||
}
|
||||
|
||||
func (s *OpenVpnMgt) ClientReAuth(line, remote string) {
|
||||
err, c := s.getClient(line, remote)
|
||||
if err != nil {
|
||||
log.Println(err, line)
|
||||
return
|
||||
}
|
||||
c.ParseSessionId(line)
|
||||
infos := <-s.ret
|
||||
if err := c.ParseEnv(s, &infos); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// reset some values
|
||||
c.Profile = ""
|
||||
c.Status = "system failure"
|
||||
c.Operation = "re auth"
|
||||
|
||||
c.Auth(s)
|
||||
}
|
||||
|
||||
// find a client among all registered sessions
|
||||
func (s *OpenVpnMgt) getClient(line, remote string) (error, *vpnSession) {
|
||||
re := regexp.MustCompile("^[^0-9]*,([0-9]+)[^0-9]*")
|
||||
@ -201,6 +249,25 @@ func (s *OpenVpnMgt) getClient(line, remote string) (error, *vpnSession) {
|
||||
return errors.New("unknown vpn client"), nil
|
||||
}
|
||||
|
||||
// update counters
|
||||
func (s *OpenVpnMgt) updateCounters(line, remote string) {
|
||||
p := strings.Split(strings.Replace(line, ":", ",", 1), ",")
|
||||
err, c := s.getClient(p[0]+","+p[1], remote)
|
||||
if err != nil {
|
||||
log.Println(err, line)
|
||||
return
|
||||
}
|
||||
if c.BwWrite, err = strconv.Atoi(p[2]); err != nil {
|
||||
c.LogPrintln(err)
|
||||
return
|
||||
}
|
||||
if c.BwRead, err = strconv.Atoi(p[3]); err != nil {
|
||||
c.LogPrintln(err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// main loop for a given openvpn server
|
||||
func (s *OpenVpnMgt) handleConn(conn net.Conn) {
|
||||
remote := conn.RemoteAddr().String()
|
||||
@ -282,7 +349,7 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
|
||||
|
||||
// trafic stats
|
||||
case strings.HasPrefix(line, ">BYTECOUNT_CLI"):
|
||||
//TODO use that
|
||||
go s.updateCounters(line, remote)
|
||||
|
||||
// new bloc for a disconnect event.
|
||||
// We start the receiving handler, which will wait for the Channel message
|
||||
@ -297,8 +364,14 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
|
||||
case strings.HasPrefix(line, ">CLIENT:CONNECT"):
|
||||
go s.ClientConnect(line, remote)
|
||||
|
||||
case strings.HasPrefix(line, ">CLIENT:REAUTH"):
|
||||
go s.ClientReAuth(line, remote)
|
||||
|
||||
default:
|
||||
response = append(response, line)
|
||||
}
|
||||
if s.debug && strings.Index(line, "password") == -1 {
|
||||
log.Print(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,11 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
hibp "github.com/mattevans/pwned-passwords"
|
||||
)
|
||||
|
||||
type vpnSession struct {
|
||||
@ -30,6 +29,8 @@ type vpnSession struct {
|
||||
PwnedPasswd bool `json:"pwned_passwd"`
|
||||
Hostname string `json:"hostname"`
|
||||
TooMuchPwn bool `json:"too_much_pwn"`
|
||||
BwRead int `json:"in_bytes"`
|
||||
BwWrite int `json:"out_bytes"`
|
||||
Mail string `json:"-"`
|
||||
cID int `json:"-"`
|
||||
kID int `json:"-"`
|
||||
@ -45,11 +46,11 @@ type vpnSession struct {
|
||||
CcPwnPassword string `json:"-"`
|
||||
}
|
||||
|
||||
func NewVPNSession(operation string) *vpnSession {
|
||||
func NewVPNSession() *vpnSession {
|
||||
v := vpnSession{
|
||||
Time: time.Now().Round(time.Second),
|
||||
Status: "system failure",
|
||||
Operation: operation,
|
||||
Operation: "log in",
|
||||
}
|
||||
v.Hostname, _ = os.Hostname()
|
||||
|
||||
@ -71,29 +72,29 @@ func (c *vpnSession) baseHash(salt string, i int64) string {
|
||||
return fmt.Sprintf("%s%s%s%s", salt, c.Login, c.IP, i)
|
||||
}
|
||||
|
||||
func (c *vpnSession) AddRoute(script, ip string) error {
|
||||
cmd := exec.Command(script, "route", "replace", ip, "dev", c.dev)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
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 {
|
||||
re := regexp.MustCompile("^>CLIENT:[^,]*,([0-9]+),([0-9]+)$")
|
||||
match := re.FindStringSubmatch(line)
|
||||
if len(match) == 0 {
|
||||
return errors.New("invalid message")
|
||||
}
|
||||
|
||||
if c.cID, err = strconv.Atoi(match[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.kID, err = strconv.Atoi(client_id[1]); err != nil {
|
||||
if c.kID, err = strconv.Atoi(match[2]); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *vpnSession) CheckPwn(password string) error {
|
||||
client := hibp.NewClient()
|
||||
pwned, err := client.Pwned.Compromised(password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.PwnedPasswd = pwned
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *vpnSession) ParseEnv(infos *[]string) error {
|
||||
func (c *vpnSession) ParseEnv(s *OpenVpnMgt, infos *[]string) error {
|
||||
var err error
|
||||
r := regexp.MustCompile("[^a-zA-Z0-9./_@-]")
|
||||
|
||||
@ -128,7 +129,7 @@ func (c *vpnSession) ParseEnv(infos *[]string) error {
|
||||
if c.otpCode == "" {
|
||||
c.otpCode = "***"
|
||||
}
|
||||
go c.CheckPwn(c.password)
|
||||
// don't check that password agains the ibp database
|
||||
|
||||
case strings.HasPrefix(p[1], "SCRV1"):
|
||||
split := strings.Split(p[1], ":")
|
||||
@ -151,11 +152,17 @@ func (c *vpnSession) ParseEnv(infos *[]string) error {
|
||||
if c.otpCode == "" {
|
||||
c.otpCode = "***"
|
||||
}
|
||||
|
||||
// only check if the password is pwned on the first connection
|
||||
if c.Operation == "log in" {
|
||||
go s.CheckPwn(c)
|
||||
}
|
||||
default:
|
||||
c.password = p[1]
|
||||
c.otpCode = ""
|
||||
go c.CheckPwn(c.password)
|
||||
// only check if the password is pwned on the first connection
|
||||
if c.Operation == "log in" {
|
||||
go s.CheckPwn(c)
|
||||
}
|
||||
}
|
||||
|
||||
case "username":
|
||||
@ -174,11 +181,15 @@ func (c *vpnSession) Auth(s *OpenVpnMgt) {
|
||||
|
||||
err, ok := c.auth(s)
|
||||
// if auth is ok, time to get an IP address
|
||||
if ok == 0 {
|
||||
if ok == 0 && c.PrivIP == "" {
|
||||
ip, errIP = s.getIP(c)
|
||||
if errIP != nil {
|
||||
ok = -10
|
||||
err = errIP
|
||||
} else {
|
||||
if err := c.AddRoute(s.ipRouteScript, ip); err != nil {
|
||||
c.LogPrintln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,6 +203,7 @@ func (c *vpnSession) Auth(s *OpenVpnMgt) {
|
||||
cmd = append(cmd, fmt.Sprintf("push \"route %s vpn_gateway\"", r))
|
||||
}
|
||||
cmd = append(cmd, "END")
|
||||
c.Status = "success"
|
||||
|
||||
case ok < 0:
|
||||
cmd = []string{fmt.Sprintf("client-deny %d %d \"%s\" \"%s\"",
|
||||
|
Loading…
x
Reference in New Issue
Block a user