From ff3abf4d6b6d5f034a3139979b3087632262b88b Mon Sep 17 00:00:00 2001 From: Teddy_Zhu Date: Mon, 29 Apr 2024 20:42:03 +0800 Subject: [PATCH] feat: support get OS platform for VPNClient System (#61) --- pkg/cache/openvpn.go | 42 ++++++++++++++ pkg/schema/openvpn.go | 1 + pkg/switch/openvpn.go | 128 +++++++++++++++++++++++++++++++++++------- 3 files changed, 152 insertions(+), 19 deletions(-) diff --git a/pkg/cache/openvpn.go b/pkg/cache/openvpn.go index 0ae69ca..4df9184 100755 --- a/pkg/cache/openvpn.go +++ b/pkg/cache/openvpn.go @@ -43,7 +43,29 @@ func (o *vpnClient) GetDevice(name string) string { } return "" } +func (o *vpnClient) scanClientStatus(reader io.Reader, + clients map[string]*schema.VPNClient) error { + if clients == nil { + return nil + } + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + line := scanner.Text() + if line != "" { + columns := strings.SplitN(line, ",", 2) + for _, client := range clients { + if client.Name == columns[0] { + client.System = columns[1] + } + } + } + } + if err := scanner.Err(); err != nil { + return err + } + return nil +} func (o *vpnClient) scanStatus(network string, reader io.Reader, clients map[string]*schema.VPNClient) error { readAt := "header" @@ -131,6 +153,15 @@ func (o *vpnClient) statusFile(name string) []string { return files } +func (o *vpnClient) clientStatusFile(name string) []string { + files, err := filepath.Glob(o.Dir(name, "*ivplat.status")) + if err != nil { + libol.Warn("vpnClient.clientStatusFile %v", err) + return []string{} + } + return files +} + func (o *vpnClient) readStatus(network string) map[string]*schema.VPNClient { clients := make(map[string]*schema.VPNClient, 32) for _, file := range o.statusFile(network) { @@ -144,6 +175,17 @@ func (o *vpnClient) readStatus(network string) map[string]*schema.VPNClient { } reader.Close() } + for _, file := range o.clientStatusFile(network) { + reader, err := os.Open(file) + if err != nil { + libol.Debug("vpnClient.readStatus %v", err) + return nil + } + if err := o.scanClientStatus(reader, clients); err != nil { + libol.Warn("vpnClient.readStatus %v", err) + } + reader.Close() + } return clients } diff --git a/pkg/schema/openvpn.go b/pkg/schema/openvpn.go index 29ec3b5..7c9c702 100755 --- a/pkg/schema/openvpn.go +++ b/pkg/schema/openvpn.go @@ -14,4 +14,5 @@ type VPNClient struct { State string `json:"state"` AliveTime int64 `json:"aliveTime"` Address string `json:"address"` + System string `json:"system"` } diff --git a/pkg/switch/openvpn.go b/pkg/switch/openvpn.go index 52fef12..a403805 100755 --- a/pkg/switch/openvpn.go +++ b/pkg/switch/openvpn.go @@ -23,25 +23,26 @@ const ( ) type OpenVPNData struct { - Local string - Port string - CertNot bool - Ca string - Cert string - Key string - DhPem string - TlsAuth string - Cipher string - Server string - Device string - Protocol string - Script string - Routes []string - Renego int - Stats string - IpIp string - Push []string - ClientConfigDir string + Local string + Port string + CertNot bool + Ca string + Cert string + Key string + DhPem string + TlsAuth string + Cipher string + Server string + Device string + Protocol string + Script string + Routes []string + Renego int + Stats string + IpIp string + Push []string + ClientConfigDir string + ClientStatusScriptDir string } const ( @@ -69,6 +70,8 @@ ifconfig-pool-persist {{ .Protocol }}{{ .Port }}ipp tls-auth {{ .TlsAuth }} 0 cipher {{ .Cipher }} status {{ .Protocol }}{{ .Port }}server.status 2 +client-connect "{{ .ClientStatusScriptDir }}/client-connect.sh" +client-disconnect "{{ .ClientStatusScriptDir }}/client-disconnect.sh" {{- if .CertNot }} client-cert-not-required {{- else }} @@ -103,6 +106,23 @@ cipher {{ .Cipher }} status {{ .Protocol }}{{ .Port }}server.status 2 client-config-dir {{ .ClientConfigDir }} verb 3 +` + clientConnectScriptTmpl = `#!/bin/bash +log_file="{{ .ClientStatusScriptDir }}/{{ .Protocol }}{{ .Port }}ivplat.status" +if [ -n "$common_name" ]; then + if grep -q "^$common_name," "$log_file"; then + sed -i "s/^$common_name,.*/$common_name,$IV_PLAT/" "$log_file" + else + if [ -z "$IV_PLAT" ]; then + IV_PLAT="Unknown" + fi + echo "$common_name,$IV_PLAT" >> "$log_file" + fi +fi +` + clientDisConnectScriptTmpl = `#!/bin/bash +log_file="{{ .ClientStatusScriptDir }}/{{ .Protocol }}{{ .Port }}ivplat.status" +sed -i "/^$common_name,/d" "$log_file" ` ) @@ -136,6 +156,7 @@ func NewOpenVPNDataFromConf(obj *OpenVPN) *OpenVPNData { } } data.ClientConfigDir = obj.DirectoryClientConfig() + data.ClientStatusScriptDir = obj.ClientIvplatDir() return data } @@ -242,6 +263,22 @@ func (o *OpenVPN) ServerTmpl() string { return tmplStr } +func (o *OpenVPN) ClientConnectScriptTmpl() string { + tmplStr := clientConnectScriptTmpl + + cfgTmpl := filepath.Join(o.Cfg.Directory, o.ID()+"connectivplat.tmpl") + _ = ioutil.WriteFile(cfgTmpl, []byte(tmplStr), 0600) + return tmplStr +} + +func (o *OpenVPN) ClientDisConnectScriptTmpl() string { + tmplStr := clientDisConnectScriptTmpl + + cfgTmpl := filepath.Join(o.Cfg.Directory, o.ID()+"disconnectivplat.tmpl") + _ = ioutil.WriteFile(cfgTmpl, []byte(tmplStr), 0600) + return tmplStr +} + func (o *OpenVPN) FileIpp(full bool) string { if o.Cfg == nil { return "" @@ -269,6 +306,13 @@ func (o *OpenVPN) DirectoryClientConfig() string { return path.Join(o.Cfg.Directory, "ccd") } +func (o *OpenVPN) ClientIvplatDir() string { + if o.Cfg == nil { + return DefaultCurDir + } + return o.Cfg.Directory +} + func (o *OpenVPN) WriteConf(path string) error { fp, err := libol.CreateFile(path) if err != nil || fp == nil { @@ -280,6 +324,9 @@ func (o *OpenVPN) WriteConf(path string) error { if data.ClientConfigDir != "" { _ = o.writeClientConfig() } + if data.ClientStatusScriptDir != "" { + _ = o.writeClientStatusScripts(data) + } tmplStr := o.ServerTmpl() if tmpl, err := template.New("main").Parse(tmplStr); err != nil { return err @@ -311,6 +358,49 @@ func (o *OpenVPN) writeClientConfig() error { return nil } +func createExecutableFile(path string) (*os.File, error) { + return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) +} +func (o *OpenVPN) writeClientStatusScripts(data *OpenVPNData) error { + // make client dir and config file + cid := o.ClientIvplatDir() + if err := os.Mkdir(cid, 0600); err != nil { + o.out.Info("OpenVPN.writeClientStatusScripts %s", err) + } + clientConnectScriptFile := filepath.Join(cid, "client-connect.sh") + fp, err := createExecutableFile(clientConnectScriptFile) + if err != nil || fp == nil { + return err + } + defer fp.Close() + + tmplStr := o.ClientConnectScriptTmpl() + if tmpl, err := template.New("clientScript").Parse(tmplStr); err != nil { + return err + } else { + if err := tmpl.Execute(fp, data); err != nil { + return err + } + } + + clientDisConnectFile := filepath.Join(cid, "client-disconnect.sh") + fp2, err := createExecutableFile(clientDisConnectFile) + if err != nil || fp == nil { + return err + } + defer fp2.Close() + + tmplDisConnectStr := o.ClientDisConnectScriptTmpl() + if tmpl, err := template.New("clientDisConnectScript").Parse(tmplDisConnectStr); err != nil { + return err + } else { + if err := tmpl.Execute(fp2, data); err != nil { + return err + } + } + return nil +} + func (o *OpenVPN) Clean() { ccd := o.DirectoryClientConfig() for _, fic := range o.Cfg.Clients {