This commit is contained in:
2019-08-15 11:19:40 +02:00
parent 65496cbce9
commit a82131c8c8
6 changed files with 331 additions and 276 deletions

View File

@@ -1,12 +1,12 @@
package main
import (
"bufio"
"errors"
"fmt"
"io"
"log"
"net"
"os"
"strconv"
"strings"
"sync"
@@ -15,37 +15,50 @@ import (
// Server represents the server
type OpenVpnMgt struct {
port string
buf map[string]*bufio.ReadWriter
m sync.RWMutex
ret chan []string
syslog bool
debug bool
hold bool
port string
m sync.RWMutex
debug bool
VpnRemotes []string `json:"remotes"`
vpnServers map[int]*OpenVpnSrv `json:"sessions"`
}
// NewServer returns a pointer to a new server
func NewVPNServer(port string) *OpenVpnMgt {
func NewVPNServer(port string, debug bool) *OpenVpnMgt {
return &OpenVpnMgt{
port: port,
ret: make(chan []string),
buf: make(map[string]*bufio.ReadWriter),
port: port,
debug: debug,
vpnServers: make(map[int]*OpenVpnSrv),
}
}
func (s *OpenVpnMgt) Lock() {
s.m.Lock()
}
func (s *OpenVpnMgt) Unlock() {
s.m.Unlock()
}
// Run starts a the server
func (s *OpenVpnMgt) Run() {
// get the endpoint list
if err := s.getServerList(); err != nil {
log.Println(err)
return
}
// Resolve the passed port into an address
addrs, err := net.ResolveTCPAddr("tcp", s.port)
if err != nil {
log.Println(err)
os.Exit(1)
return
}
// start listening to client connections
listener, err := net.ListenTCP("tcp", addrs)
if err != nil {
log.Println(err)
os.Exit(1)
return
}
// Infinite loop since we dont want the server to shut down
for {
@@ -60,86 +73,59 @@ func (s *OpenVpnMgt) Run() {
}
}
// 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
func (s *OpenVpnMgt) GetSession(remote int) (error, *OpenVpnSrv) {
if vpnServer, ok := s.vpnServers[remote]; ok {
return nil, vpnServer
}
for _, line := range msg {
if s.debug {
log.Println(line)
}
if _, err := s.buf[remote].WriteString(line + "\r\n"); err != nil {
return err, nil
}
}
if err := s.buf[remote].Flush(); err != nil {
return err, nil
}
// wait for the response
ret := <-s.ret
if s.debug {
for _, line := range ret {
log.Println(line)
}
}
return nil, ret
return errors.New(fmt.Sprintf("unknown session %d", vpnServers)), nil
}
// send the help command on all vpn servers. Kind of useless
func (s *OpenVpnMgt) Help() (error, map[string]map[string]string) {
ret := make(map[string]map[string]string)
re := rcache.Get("^(.*[^ ]) *: (.*)$")
for remote := range s.buf {
help := make(map[string]string)
err, msg := s.sendCommand([]string{"help"}, remote)
if err != nil {
return err, ret
}
for _, line := range msg {
match := re.FindStringSubmatch(line)
if len(match) == 0 {
continue
}
help[match[1]] = match[2]
}
ret[remote] = help
func (s *OpenVpnMgt) SetRemote(server string, remote int) error {
// check if the session is valid
err, session := s.GetSession(remote)
if err != nil {
return err
}
return nil, ret
for _, r := range s.VpnRemotes {
if r != server {
continue
}
return session.SetRemote(server)
}
return errors.New(fmt.Sprintf("unknown remote %s", server))
}
// send the version command on all vpn servers. Kind of useless
func (s *OpenVpnMgt) Version() (error, map[string][]string) {
ret := make(map[string][]string)
for remote := range s.buf {
err, msg := s.sendCommand([]string{"version"}, remote)
if err != nil {
return err, ret
func (s *OpenVpnMgt) Version() (error, map[int][]string) {
var err error
ret := make(map[int][]string)
for remote, srv := range s.vpnServers {
err, msg := srv.Version()
if err == nil {
ret[remote] = msg
}
ret[remote] = msg
}
return nil, ret
return err, ret
}
// main loop for a given openvpn server
func (s *OpenVpnMgt) handleConn(conn net.Conn) {
remote := conn.RemoteAddr().String()
pidRegexp := rcache.Get("^SUCCESS: pid=([0-9]+)$")
// >REMOTE:vpn.example.com,1194,udp
remoteRegexp := rcache.Get("^>REMOTE:(.*),([0-9]*),(.*)$")
defer conn.Close()
defer delete(s.buf, remote)
// we store the buffer pointer in the struct, to be accessed from other methods
s.buf[remote] = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
vpnServer := NewOpenVpnSrv(conn)
// most response are multilined, use response to concatenate them
response := []string{}
// remove bogus clients
line, err := s.buf[remote].ReadString('\n')
line, err := vpnServer.session.GetLine()
if err != nil {
log.Println(err)
return
@@ -151,8 +137,10 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
log.Printf("Valid openvpn connected from %s\n", remote)
go session.sendCommand([]string{"pid"})
for {
line, err := s.buf[remote].ReadString('\n')
line, err := session.GetLine()
// manage basic errors
switch {
@@ -165,6 +153,10 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
}
line = strings.Trim(line, "\n\r ")
if s.debug && strings.Index(line, "password") == -1 {
log.Print(line)
}
// manage exit commands
for _, terminator := range []string{"quit", "exit"} {
if line == terminator || strings.HasPrefix(line, terminator+" ") {
@@ -173,28 +165,36 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
}
}
// get the PID
match := pidRegexp.FindStringSubmatch(line)
if len(match) == 2 {
pid, _ := strconv.Atoi(match[1])
s.Lock()
s.vpnServers[pid] = vpnServer
s.Unlock()
defer delete(s.vpnServers, pid)
}
// manage all "terminator" lines
for _, terminator := range []string{"END", ">CLIENT:ENV,END", "SUCCESS", "ERROR"} {
if strings.HasPrefix(line, terminator) {
s.ret <- response
vpnServer.Response(response)
response = nil
line = ""
break
}
}
remoteMatch := remoteRegexp.FindStringSubmatch(line)
switch {
// command successfull, we can ignore
case strings.HasPrefix(line, ">SUCCESS: client-deny command succeeded"):
case strings.HasPrefix(line, ">HOLD"):
s.sendCommand([]string{"hold release"}, remote)
case strings.HasPrefix(line, ">REMOTE"):
s.sendCommand([]string{"remote ACCEPT"}, remote)
go vpnServer.waitForRelase()
case len(remoteMatch) > 0:
go vpnServer.ValidRemote(remoteMatch[1], remoteMatch[2], remoteMatch[3])
default:
response = append(response, line)
}
if s.debug && strings.Index(line, "password") == -1 {
log.Print(line)
}
}
}