diff --git a/pkg/rest/setup.go b/pkg/rest/setup.go
index 52f51d3..d100c2c 100644
--- a/pkg/rest/setup.go
+++ b/pkg/rest/setup.go
@@ -312,6 +312,11 @@ func (c *Context) vpnSetupHandler(w http.ResponseWriter, r *http.Request) {
c.returnError(w, fmt.Errorf("could write vpn config: %s", err), http.StatusBadRequest)
return
}
+ err = wireguard.ReloadVPNServerConfig()
+ if err != nil {
+ c.returnError(w, fmt.Errorf("unable to reload server config: %s", err), http.StatusBadRequest)
+ return
+ }
}
if rewriteClientConfigs {
// rewrite client configs
diff --git a/pkg/rest/stats.go b/pkg/rest/stats.go
index fc66f4a..e7d6abc 100644
--- a/pkg/rest/stats.go
+++ b/pkg/rest/stats.go
@@ -3,8 +3,10 @@ package rest
import (
"bufio"
"bytes"
+ "compress/gzip"
"encoding/json"
"fmt"
+ "io"
"math"
"net/http"
"path"
@@ -282,13 +284,19 @@ func (c *Context) packetLogsHandler(w http.ResponseWriter, r *http.Request) {
Data: []LogRow{},
}
// logs
- statsFiles := []string{
- path.Join(wireguard.VPN_STATS_DIR, wireguard.VPN_PACKETLOGGER_DIR, userID+"-"+date.Format("2006-01-02")+".log"),
+ statsFiles := []string{}
+ if offset > 0 {
+ statsFiles = append(statsFiles, path.Join(wireguard.VPN_STATS_DIR, wireguard.VPN_PACKETLOGGER_DIR, userID+"-"+date.AddDate(0, 0, -1).Format("2006-01-02")+".log"))
}
+ statsFiles = append(statsFiles, path.Join(wireguard.VPN_STATS_DIR, wireguard.VPN_PACKETLOGGER_DIR, userID+"-"+date.Format("2006-01-02")+".log"))
if !dateutils.DateEqual(time.Now(), date) { // date is in local timezone, and we are UTC, so also read next file
statsFiles = append(statsFiles, path.Join(wireguard.VPN_STATS_DIR, wireguard.VPN_PACKETLOGGER_DIR, userID+"-"+date.AddDate(0, 0, 1).Format("2006-01-02")+".log"))
}
- statsFiles = filterNonExistentFiles(c.Storage.Client, statsFiles)
+ statsFiles, err = getCompressedFilesAndRemoveNonExistent(c.Storage.Client, statsFiles)
+ if err != nil {
+ c.returnError(w, fmt.Errorf("unable to get files for reading: %s", err), http.StatusBadRequest)
+ return
+ }
fileReaders, err := c.Storage.Client.OpenFilesFromPos(statsFiles, pos)
if err != nil {
c.returnError(w, fmt.Errorf("error while reading files: %s", err), http.StatusBadRequest)
@@ -353,14 +361,42 @@ func (c *Context) packetLogsHandler(w http.ResponseWriter, r *http.Request) {
c.write(w, out)
}
-func filterNonExistentFiles(storage storage.Iface, files []string) []string {
+func getCompressedFilesAndRemoveNonExistent(storage storage.Iface, files []string) ([]string, error) {
res := []string{}
for _, file := range files {
+ tmpFile := path.Join(wireguard.VPN_PACKETLOGGER_TMP_DIR, path.Base(file))
if storage.FileExists(file) {
res = append(res, file)
+ } else if storage.FileExists(tmpFile) { // temporary file exists
+ res = append(res, tmpFile)
+ } else if storage.FileExists(file + ".gz") { // uncompress log file for random access
+ err := storage.EnsurePath(wireguard.VPN_PACKETLOGGER_TMP_DIR)
+ if err != nil {
+ return res, fmt.Errorf("ensure path error: %s", err)
+ }
+ compressedFile, err := storage.OpenFile(file + ".gz")
+ if err != nil {
+ return res, fmt.Errorf("unable to open compress filed (%s): %s", file+".gz", err)
+ }
+ gzipReader, err := gzip.NewReader(compressedFile)
+ if err != nil {
+ return res, fmt.Errorf("unable to open gzip reader (%s): %s", file+".gz", err)
+ }
+ fileWriter, err := storage.OpenFileForWriting(tmpFile)
+ if err != nil {
+ return res, fmt.Errorf("unable to open tmp file writer (%s): %s", tmpFile, err)
+ }
+ _, err = io.Copy(fileWriter, gzipReader)
+ if err != nil {
+ return res, fmt.Errorf("unable to uncompress to file (%s): %s", tmpFile, err)
+ }
+ fileWriter.Close()
+ gzipReader.Close()
+ compressedFile.Close()
+ res = append(res, tmpFile)
}
}
- return res
+ return res, nil
}
func getColor(i int) string {
diff --git a/pkg/rest/stats_test.go b/pkg/rest/stats_test.go
index b1da1fc..b484606 100644
--- a/pkg/rest/stats_test.go
+++ b/pkg/rest/stats_test.go
@@ -1,7 +1,10 @@
package rest
import (
+ "bytes"
+ "compress/gzip"
"encoding/json"
+ "io"
"net/http/httptest"
"path"
"strings"
@@ -78,3 +81,43 @@ func TestFilterLogRecord(t *testing.T) {
}
}
}
+
+func TestGetCompressedFilesAndRemoveNonExistent(t *testing.T) {
+ now := time.Now()
+ storage := &memorystorage.MockMemoryStorage{}
+ testData := now.Format(wireguard.TIMESTAMP_FORMAT) + ",3df97301-5f73-407a-a26b-91829f1e7f48,1,12729136,24348520,2024-08-23T18:30:42\n"
+ files := []string{
+ path.Join(wireguard.VPN_STATS_DIR, wireguard.VPN_PACKETLOGGER_DIR, "1-2-3-4-"+now.AddDate(0, 0, -1).Format("2006-01-02")+".log"),
+ path.Join(wireguard.VPN_STATS_DIR, wireguard.VPN_PACKETLOGGER_DIR, "1-2-3-4-"+now.Format("2006-01-02")+".log"),
+ }
+ for k, file := range files {
+ if k != len(files)-1 { // only the last file is not compressed
+ fileWriter, err := storage.OpenFileForWriting(file + ".gz")
+ if err != nil {
+ t.Fatalf("open file for wring error: %s", err)
+ }
+ writer := gzip.NewWriter(fileWriter)
+ io.Copy(writer, bytes.NewReader([]byte(testData)))
+ writer.Close()
+ fileWriter.Close()
+ } else {
+ storage.WriteFile(file, []byte(testData))
+ }
+ }
+ outFiles, err := getCompressedFilesAndRemoveNonExistent(storage, files)
+ if err != nil {
+ t.Fatalf("get files error: %s", err)
+ }
+ if len(outFiles) != 2 {
+ t.Fatalf("expected 2 files, got: %d", len(outFiles))
+ }
+ for _, file := range outFiles {
+ body, err := storage.ReadFile(file)
+ if err != nil {
+ t.Fatalf("readfile error: %s", err)
+ }
+ if string(body) != string(testData) {
+ t.Fatalf("mismatch: got: %s vs expected: %s\n", string(body), string(testData))
+ }
+ }
+}
diff --git a/pkg/storage/iface.go b/pkg/storage/iface.go
index 3bb3461..f412fff 100644
--- a/pkg/storage/iface.go
+++ b/pkg/storage/iface.go
@@ -14,6 +14,7 @@ type Iface interface {
Rename(oldName, newName string) error
AppendFile(name string, data []byte) error
EnsurePermissions(name string, mode fs.FileMode) error
+ FileInfo(name string) (fs.FileInfo, error)
ReadWriter
Seeker
}
diff --git a/pkg/storage/local/path.go b/pkg/storage/local/path.go
index de52e4f..214b341 100644
--- a/pkg/storage/local/path.go
+++ b/pkg/storage/local/path.go
@@ -90,3 +90,7 @@ func (l *LocalStorage) Rename(oldName, newName string) error {
func (l *LocalStorage) EnsurePermissions(name string, mode fs.FileMode) error {
return os.Chmod(path.Join(l.path, name), mode)
}
+
+func (l *LocalStorage) FileInfo(name string) (fs.FileInfo, error) {
+ return os.Stat(name)
+}
diff --git a/pkg/storage/memory/fileinfo.go b/pkg/storage/memory/fileinfo.go
new file mode 100644
index 0000000..b9fc5dd
--- /dev/null
+++ b/pkg/storage/memory/fileinfo.go
@@ -0,0 +1,34 @@
+package memorystorage
+
+import (
+ "io/fs"
+ "time"
+)
+
+type FileInfo struct {
+ NameOut string // base name of the file
+ SizeOut int64 // length in bytes for regular files; system-dependent for others
+ ModeOut fs.FileMode // file mode bits
+ ModTimeOut time.Time // modification time
+ IsDirOut bool // abbreviation for Mode().IsDir()
+ SysOut any // underlying data source (can return nil)
+}
+
+func (f FileInfo) Name() string {
+ return f.NameOut
+}
+func (f FileInfo) Size() int64 {
+ return f.SizeOut
+}
+func (f FileInfo) Mode() fs.FileMode {
+ return f.ModeOut
+}
+func (f FileInfo) ModTime() time.Time {
+ return f.ModTimeOut
+}
+func (f FileInfo) IsDir() bool {
+ return f.IsDirOut
+}
+func (f FileInfo) Sys() any {
+ return nil
+}
diff --git a/pkg/storage/memory/storage.go b/pkg/storage/memory/storage.go
index 3114181..ef5734e 100644
--- a/pkg/storage/memory/storage.go
+++ b/pkg/storage/memory/storage.go
@@ -1,7 +1,6 @@
package memorystorage
import (
- "bufio"
"bytes"
"fmt"
"io"
@@ -11,14 +10,6 @@ import (
"strings"
)
-type MyWriteCloser struct {
- *bufio.Writer
-}
-
-func (mwc *MyWriteCloser) Close() error {
- return nil
-}
-
type MockReadWriterData []byte
func (m *MockReadWriterData) Close() error {
@@ -30,7 +21,8 @@ func (m *MockReadWriterData) Write(p []byte) (nn int, err error) {
}
type MockMemoryStorage struct {
- Data map[string]*MockReadWriterData
+ FileInfoData map[string]*FileInfo
+ Data map[string]*MockReadWriterData
}
func (m *MockMemoryStorage) ConfigPath(filename string) string {
@@ -160,3 +152,10 @@ func (m *MockMemoryStorage) OpenFileForAppending(name string) (io.WriteCloser, e
func (m *MockMemoryStorage) EnsurePermissions(name string, mode fs.FileMode) error {
return nil
}
+func (m *MockMemoryStorage) FileInfo(name string) (fs.FileInfo, error) {
+ val, ok := m.FileInfoData[name]
+ if !ok {
+ return FileInfo{}, fmt.Errorf("couldn't get file info for: %s", name)
+ }
+ return val, nil
+}
diff --git a/pkg/wireguard/constants.go b/pkg/wireguard/constants.go
index 26beba2..46fea88 100644
--- a/pkg/wireguard/constants.go
+++ b/pkg/wireguard/constants.go
@@ -9,6 +9,7 @@ const IP_LIST_PATH = "config/iplist.json"
const VPN_CLIENTS_DIR = "clients"
const VPN_STATS_DIR = "stats"
const VPN_PACKETLOGGER_DIR = "packetlogs"
+const VPN_PACKETLOGGER_TMP_DIR = "tmp"
const VPN_SERVER_SECRETS_PATH = "secrets"
const VPN_PRIVATE_KEY_FILENAME = "priv.key"
const PRESHARED_KEY_FILENAME = "preshared.key"
diff --git a/pkg/wireguard/packetlogger.go b/pkg/wireguard/packetlogger.go
index 5415a8e..978acfd 100644
--- a/pkg/wireguard/packetlogger.go
+++ b/pkg/wireguard/packetlogger.go
@@ -78,7 +78,7 @@ func RunPacketLogger(storage storage.Iface, clientCache *ClientCache, vpnConfig
i := 0
openFiles := make(PacketLoggerOpenFiles)
for {
- err := readPacket(storage, handle, clientCache, openFiles)
+ err := readPacket(storage, handle, clientCache, openFiles, vpnConfig.PacketLogsTypes)
if err != nil {
logging.DebugLog(fmt.Errorf("readPacket error: %s", err))
}
@@ -102,14 +102,14 @@ func RunPacketLogger(storage storage.Iface, clientCache *ClientCache, vpnConfig
i++
}
}
-func readPacket(storage storage.Iface, handle *pcap.Handle, clientCache *ClientCache, openFiles PacketLoggerOpenFiles) error {
+func readPacket(storage storage.Iface, handle *pcap.Handle, clientCache *ClientCache, openFiles PacketLoggerOpenFiles, packetLogsTypes map[string]bool) error {
data, _, err := handle.ReadPacketData()
if err != nil {
return fmt.Errorf("read packet error: %s", err)
}
- return parsePacket(storage, data, clientCache, openFiles, time.Now())
+ return parsePacket(storage, data, clientCache, openFiles, packetLogsTypes, time.Now())
}
-func parsePacket(storage storage.Iface, data []byte, clientCache *ClientCache, openFiles PacketLoggerOpenFiles, now time.Time) error {
+func parsePacket(storage storage.Iface, data []byte, clientCache *ClientCache, openFiles PacketLoggerOpenFiles, packetLogsTypes map[string]bool, now time.Time) error {
packet := gopacket.NewPacket(data, layers.IPProtocolIPv4, gopacket.DecodeOptions{Lazy: true, DecodeStreamsAsDatagrams: true})
var (
ip4 *layers.IPv4
@@ -180,89 +180,108 @@ func parsePacket(storage storage.Iface, data []byte, clientCache *ClientCache, o
openFiles[clientID+"-"+now.Format("2006-01-02")] = logWriter
}
- if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
- tcpPacket, _ := tcpLayer.(*layers.TCP)
- if tcpPacket.SYN {
- logWriter.Write([]byte(strings.Join([]string{
- now.Format(TIMESTAMP_FORMAT),
- "tcp",
- srcIP.String(),
- dstIP.String(),
- strconv.FormatUint(uint64(tcpPacket.SrcPort), 10),
- strconv.FormatUint(uint64(tcpPacket.DstPort), 10)},
- ",") + "\n",
- ))
- }
- switch tcpPacket.DstPort {
- case 80:
- if tcpPacket.DstPort == 80 {
- appLayer := packet.ApplicationLayer()
- if appLayer != nil {
- req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(appLayer.Payload())))
- if err != nil {
- fmt.Printf("debug: can't parse http packet: %s", err)
- } else {
- logWriter.Write([]byte(strings.Join([]string{
- now.Format(TIMESTAMP_FORMAT),
- "http",
- srcIP.String(),
- dstIP.String(),
- strconv.FormatUint(uint64(tcpPacket.SrcPort), 10),
- strconv.FormatUint(uint64(tcpPacket.DstPort), 10),
- "http://" + req.Host + req.URL.RequestURI()},
- ",") + "\n",
- ))
- }
- }
+ logTcpVal, logTCP := packetLogsTypes["tcp"]
+ logHttpVal, logHttp := packetLogsTypes["http+https"]
+ logDnsVal, logDns := packetLogsTypes["dns"]
+ if logTCP && !logTcpVal {
+ logTCP = false
+ }
+ if logHttp && !logHttpVal {
+ logHttp = false
+ }
+ if logDns && !logDnsVal {
+ logDns = false
+ }
+
+ if logTCP || logHttp {
+ if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
+ tcpPacket, _ := tcpLayer.(*layers.TCP)
+ if tcpPacket.SYN && logTCP {
+ logWriter.Write([]byte(strings.Join([]string{
+ now.Format(TIMESTAMP_FORMAT),
+ "tcp",
+ srcIP.String(),
+ dstIP.String(),
+ strconv.FormatUint(uint64(tcpPacket.SrcPort), 10),
+ strconv.FormatUint(uint64(tcpPacket.DstPort), 10)},
+ ",") + "\n",
+ ))
}
- case 443:
- if tls, ok := packet.Layer(layers.LayerTypeTLS).(*layers.TLS); ok {
- for _, handshake := range tls.Handshake {
- if sni := parseTLSExtensionSNI([]byte(handshake.ClientHello.Extensions)); sni != nil {
- logWriter.Write([]byte(strings.Join([]string{
- now.Format(TIMESTAMP_FORMAT),
- "https",
- srcIP.String(),
- dstIP.String(),
- strconv.FormatUint(uint64(tcpPacket.SrcPort), 10),
- strconv.FormatUint(uint64(tcpPacket.DstPort), 10),
- string(sni)},
- ",") + "\n",
- ))
+ if logHttp {
+ switch tcpPacket.DstPort {
+ case 80:
+ if tcpPacket.DstPort == 80 {
+ appLayer := packet.ApplicationLayer()
+ if appLayer != nil {
+ req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(appLayer.Payload())))
+ if err != nil {
+ fmt.Printf("debug: can't parse http packet: %s", err)
+ } else {
+ logWriter.Write([]byte(strings.Join([]string{
+ now.Format(TIMESTAMP_FORMAT),
+ "http",
+ srcIP.String(),
+ dstIP.String(),
+ strconv.FormatUint(uint64(tcpPacket.SrcPort), 10),
+ strconv.FormatUint(uint64(tcpPacket.DstPort), 10),
+ "http://" + req.Host + req.URL.RequestURI()},
+ ",") + "\n",
+ ))
+ }
+ }
+ }
+ case 443:
+ if tls, ok := packet.Layer(layers.LayerTypeTLS).(*layers.TLS); ok {
+ for _, handshake := range tls.Handshake {
+ if sni := parseTLSExtensionSNI([]byte(handshake.ClientHello.Extensions)); sni != nil {
+ logWriter.Write([]byte(strings.Join([]string{
+ now.Format(TIMESTAMP_FORMAT),
+ "https",
+ srcIP.String(),
+ dstIP.String(),
+ strconv.FormatUint(uint64(tcpPacket.SrcPort), 10),
+ strconv.FormatUint(uint64(tcpPacket.DstPort), 10),
+ string(sni)},
+ ",") + "\n",
+ ))
+ }
+ }
}
}
}
}
}
- if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil {
- udp, _ := udpLayer.(*layers.UDP)
+ if logDns {
+ if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil {
+ udp, _ := udpLayer.(*layers.UDP)
- if udp.NextLayerType().Contains(layers.LayerTypeDNS) {
- dnsPacket := packet.Layer(layers.LayerTypeDNS)
- if dnsPacket != nil {
- udpDNS := dnsPacket.(*layers.DNS)
- questions := []string{}
- for k := range udpDNS.Questions {
- found := false
- for _, question := range questions {
- if question == string(udpDNS.Questions[k].Name) {
- found = true
+ if udp.NextLayerType().Contains(layers.LayerTypeDNS) {
+ dnsPacket := packet.Layer(layers.LayerTypeDNS)
+ if dnsPacket != nil {
+ udpDNS := dnsPacket.(*layers.DNS)
+ questions := []string{}
+ for k := range udpDNS.Questions {
+ found := false
+ for _, question := range questions {
+ if question == string(udpDNS.Questions[k].Name) {
+ found = true
+ }
+ }
+ if !found {
+ questions = append(questions, string(udpDNS.Questions[k].Name))
}
- }
- if !found {
- questions = append(questions, string(udpDNS.Questions[k].Name))
- }
+ }
+ logWriter.Write([]byte(strings.Join([]string{
+ now.Format(TIMESTAMP_FORMAT),
+ "udp",
+ srcIP.String(),
+ dstIP.String(),
+ strconv.FormatUint(uint64(udp.SrcPort), 10),
+ strconv.FormatUint(uint64(udp.DstPort), 10),
+ strings.Join(questions, "#")},
+ ",") + "\n"))
}
- logWriter.Write([]byte(strings.Join([]string{
- now.Format(TIMESTAMP_FORMAT),
- "udp",
- srcIP.String(),
- dstIP.String(),
- strconv.FormatUint(uint64(udp.SrcPort), 10),
- strconv.FormatUint(uint64(udp.DstPort), 10),
- strings.Join(questions, "#")},
- ",") + "\n"))
}
}
}
@@ -351,11 +370,15 @@ func checkDiskSpace() error {
// Packet log rotation
func PacketLoggerLogRotation(storage storage.Iface) {
for {
+ time.Sleep(getTimeUntilTomorrowStartOfDay()) // sleep until tomorrow
err := packetLoggerLogRotation(storage)
if err != nil {
logging.ErrorLog(fmt.Errorf("packet logger log rotation error: %s", err))
}
- time.Sleep(getTimeUntilTomorrowStartOfDay()) // sleep until tomorrow
+ err = packetLoggerRemoveTmpFiles(storage)
+ if err != nil {
+ logging.ErrorLog(fmt.Errorf("packet logger remove tmp files error: %s", err))
+ }
}
}
@@ -412,6 +435,28 @@ func packetLoggerLogRotation(storage storage.Iface) error {
return nil
}
+func packetLoggerRemoveTmpFiles(storage storage.Iface) error {
+ files, err := storage.ReadDir(VPN_PACKETLOGGER_TMP_DIR)
+ if err != nil {
+ return fmt.Errorf("readDir error: %s", err)
+ }
+ for _, filename := range files {
+ if strings.HasSuffix(filename, ".log") {
+ fileInfo, err := storage.FileInfo(path.Join(VPN_PACKETLOGGER_TMP_DIR, filename))
+ if err != nil {
+ return fmt.Errorf("file info error (%s): %s", filename, err)
+ }
+ if time.Since(fileInfo.ModTime()) > (24 * time.Hour) {
+ err = storage.Remove(path.Join(VPN_PACKETLOGGER_TMP_DIR, filename))
+ if err != nil {
+ return fmt.Errorf("file remove error (%s): %s", filename, err)
+ }
+ }
+ }
+ }
+ return nil
+}
+
func packetLoggerCompressLog(storage storage.Iface, filename string) error {
reader, err := storage.OpenFile(path.Join(VPN_STATS_DIR, VPN_PACKETLOGGER_DIR, filename))
if err != nil {
diff --git a/pkg/wireguard/packetlogger_test.go b/pkg/wireguard/packetlogger_test.go
index dbd3cab..a89c047 100644
--- a/pkg/wireguard/packetlogger_test.go
+++ b/pkg/wireguard/packetlogger_test.go
@@ -38,6 +38,11 @@ func TestParsePacket(t *testing.T) {
},
},
}
+ packetLogsTypes := map[string]bool{
+ "tcp": true,
+ "dns": true,
+ "http+https": true,
+ }
input := []string{
// DNS reqs
"45000037e04900004011cdab0abdb8020a000002e60d00350023d6861e1501000001000000000000056170706c6503636f6d0000010001",
@@ -64,7 +69,7 @@ func TestParsePacket(t *testing.T) {
if err != nil {
t.Fatalf("hex decode error: %s", err)
}
- err = parsePacket(storage, data, clientCache, openFiles, now)
+ err = parsePacket(storage, data, clientCache, openFiles, packetLogsTypes, now)
if err != nil {
t.Fatalf("parse error: %s", err)
}
@@ -102,6 +107,11 @@ func TestParsePacketSNI(t *testing.T) {
},
},
}
+ packetLogsTypes := map[string]bool{
+ "tcp": true,
+ "dns": true,
+ "http+https": true,
+ }
input := []string{
`450000d100004000400682160abdb80240e9b468ec5001bb4f71ed891a93673d8018080468f400000101080a1329f7772c5410131603010098010000940301f1d62f57f05cc00fc8fb984e7fc381a26adc301ec143b9bab6d36f3f1b15c97200002ec014c00a0039ff850088008100350084c013c00900330045002f0041c011c00700050004c012c0080016000a00ff0100003d00000013001100000e7777772e676f6f676c652e636f6d000b00020100000a000a0008001d0017001800190010000e000c02683208687474702f312e31`,
}
@@ -113,7 +123,7 @@ func TestParsePacketSNI(t *testing.T) {
if err != nil {
t.Fatalf("hex decode error: %s", err)
}
- err = parsePacket(storage, data, clientCache, openFiles, now)
+ err = parsePacket(storage, data, clientCache, openFiles, packetLogsTypes, now)
if err != nil {
t.Fatalf("parse error: %s", err)
}
@@ -148,6 +158,11 @@ func TestParsePacketOpenFiles(t *testing.T) {
},
},
}
+ packetLogsTypes := map[string]bool{
+ "tcp": true,
+ "dns": true,
+ "http+https": true,
+ }
input := []string{
// DNS reqs
"45000037e04900004011cdab0abdb8030a000002e60d00350023d6861e1501000001000000000000056170706c6503636f6d0000010001",
@@ -172,7 +187,7 @@ func TestParsePacketOpenFiles(t *testing.T) {
if err != nil {
t.Fatalf("hex decode error: %s", err)
}
- err = parsePacket(storage, data, clientCache, openFiles, nowMinusOneDay)
+ err = parsePacket(storage, data, clientCache, openFiles, packetLogsTypes, nowMinusOneDay)
if err != nil {
t.Fatalf("parse error: %s", err)
}
@@ -200,7 +215,7 @@ func TestParsePacketOpenFiles(t *testing.T) {
if err != nil {
t.Fatalf("hex decode error: %s", err)
}
- err = parsePacket(storage, data, clientCache, openFiles, now)
+ err = parsePacket(storage, data, clientCache, openFiles, packetLogsTypes, now)
if err != nil {
t.Fatalf("parse error: %s", err)
}
@@ -416,3 +431,128 @@ func TestPacketLoggerLogRotationDeletion(t *testing.T) {
t.Fatalf("only expected 7 days of retention. Got: %d", len(after))
}
}
+
+func TestPacketLoggerRemoveTmpFiles(t *testing.T) {
+ storage := &memorystorage.MockMemoryStorage{
+ Data: map[string]*memorystorage.MockReadWriterData{},
+ FileInfoData: map[string]*memorystorage.FileInfo{},
+ }
+ for i := 0; i < 20; i++ {
+ timestamp := time.Now().AddDate(0, 0, -1*i)
+ suffix := ".log"
+ key1 := path.Join(VPN_PACKETLOGGER_TMP_DIR, fmt.Sprintf("1-2-3-4-%s%s", timestamp.Format("2006-01-02"), suffix))
+ value1 := []byte(timestamp.Format(TIMESTAMP_FORMAT) + `,https,10.189.184.2,64.233.180.104,60496,443,www.google.com`)
+ err := storage.WriteFile(key1, value1)
+ if err != nil {
+ t.Fatalf("write file error: %s", err)
+ }
+ storage.FileInfoData[key1] = &memorystorage.FileInfo{
+ ModTimeOut: timestamp,
+ }
+ }
+
+ before, err := storage.ReadDir(VPN_PACKETLOGGER_TMP_DIR)
+ if err != nil {
+ t.Fatalf("readdir error: %s", err)
+ }
+
+ err = packetLoggerRemoveTmpFiles(storage)
+ if err != nil {
+ t.Fatalf("packetLoggerRotation error: %s", err)
+ }
+
+ after, err := storage.ReadDir(VPN_PACKETLOGGER_TMP_DIR)
+ if err != nil {
+ t.Fatalf("readdir error: %s", err)
+ }
+ if len(before) != 20 {
+ t.Fatalf("expected to have written 20 files. Got: %d", len(before))
+ }
+ if len(after) != 1 {
+ t.Fatalf("only expected 1 tmp files. Got: %d", len(after))
+ }
+}
+
+func TestParsePacketSwitchPacketLogTypes(t *testing.T) {
+ storage := &memorystorage.MockMemoryStorage{}
+ clientCache := &ClientCache{
+ Addresses: []ClientCacheAddresses{
+ {
+ Address: net.IPNet{
+ IP: net.ParseIP("10.189.184.2"),
+ Mask: net.IPMask(net.ParseIP("255.255.255.255").To4()),
+ },
+ ClientID: "1-2-3-4",
+ },
+ {
+ Address: net.IPNet{
+ IP: net.ParseIP("10.189.184.3"),
+ Mask: net.IPMask(net.ParseIP("255.255.255.255").To4()),
+ },
+ ClientID: "1-2-3-5",
+ },
+ },
+ }
+ packetLogsTypes := map[string]bool{
+ "tcp": true,
+ "dns": true,
+ "http+https": true,
+ }
+ input := []string{
+ // DNS reqs
+ "45000037e04900004011cdab0abdb8020a000002e60d00350023d6861e1501000001000000000000056170706c6503636f6d0000010001",
+ "4500004092d1000040111b1b0abdb8020a000002c73b0035002c4223b28e01000001000000000000037777770a676f6f676c656170697303636f6d0000410001",
+ "450000e300004000fe11af480a0000020abdb8020035dbb500cffccbad65818000010000000100000975732d656173742d310470726f6402707209616e616c797469637307636f6e736f6c65036177730361327a03636f6d00001c00010975732d656173742d310470726f6402707209616e616c797469637307636f6e736f6c65036177730361327a03636f6d00000600010000014b004b076e732d3136333709617773646e732d313202636f02756b0011617773646e732d686f73746d617374657206616d617a6f6e03636f6d000000000100001c20000003840012750000015180",
+ "450000a100004000fe11af8a0a0000020abdb8020035e136008db8bd155f81830001000000010000026462075f646e732d7364045f756470086174746c6f63616c036e657400000c0001c01c00060001000003c0004b046f726375026f72026272026e7007656c732d676d7303617474c0250d726d2d686f73746d617374657203656d730361747403636f6d0000000001000151800000271000093a8000015180",
+ // http req (SYN + Data)
+ "450000400000400040066ced0abdb8020a00010cc7b000507216cbdd00000000b0c2ffff008f000002040564010303060101080a69fbf8410000000004020000",
+ "450200810000400040066caa0abdb8020a00010cc7b000507216cbde4845afad80180804449900000101080a69fbf873eddf46d7474554202f6c6f67696e20485454502f312e310d0a486f73743a2031302e302e312e31320d0a557365722d4167656e743a206375726c2f382e372e310d0a4163636570743a202a2f2a0d0a0d0a",
+ }
+ now := time.Now()
+ nowMinusOneDay := now.AddDate(0, 0, -1)
+ openFiles := make(PacketLoggerOpenFiles)
+ for _, s := range input {
+ data, err := hex.DecodeString(s)
+ if err != nil {
+ t.Fatalf("hex decode error: %s", err)
+ }
+ err = parsePacket(storage, data, clientCache, openFiles, packetLogsTypes, nowMinusOneDay)
+ if err != nil {
+ t.Fatalf("parse error: %s", err)
+ }
+ }
+
+ out1, err := storage.ReadFile(path.Join(VPN_STATS_DIR, VPN_PACKETLOGGER_DIR, "1-2-3-4-"+nowMinusOneDay.Format("2006-01-02")+".log"))
+ if err != nil {
+ t.Fatalf("read file error: %s", err)
+ }
+
+ if !strings.Contains(string(out1), `,udp,10.189.184.2,10.0.0.2,58893,53,apple.com`) {
+ t.Fatalf("unexpected output. Expected udp record")
+ }
+
+ packetLogsTypes2 := map[string]bool{
+ "tcp": false,
+ "dns": false,
+ "http+https": true,
+ }
+ for _, s := range input {
+ data, err := hex.DecodeString(s)
+ if err != nil {
+ t.Fatalf("hex decode error: %s", err)
+ }
+ err = parsePacket(storage, data, clientCache, openFiles, packetLogsTypes2, now)
+ if err != nil {
+ t.Fatalf("parse error: %s", err)
+ }
+ }
+
+ out2, err := storage.ReadFile(path.Join(VPN_STATS_DIR, VPN_PACKETLOGGER_DIR, "1-2-3-4-"+now.Format("2006-01-02")+".log"))
+ if err != nil {
+ t.Fatalf("read file error: %s", err)
+ }
+
+ if strings.Contains(string(out2), `,udp,10.189.184.2,10.0.0.2,58893,53,apple.com`) {
+ t.Fatalf("unexpected output. Expected no udp record. Out: %s\n", out2)
+ }
+}
diff --git a/pkg/wireguard/vpnconfig.go b/pkg/wireguard/vpnconfig.go
index e56fe3b..8b11516 100644
--- a/pkg/wireguard/vpnconfig.go
+++ b/pkg/wireguard/vpnconfig.go
@@ -145,6 +145,10 @@ func WriteVPNConfig(storage storage.Iface, vpnConfig VPNConfig) error {
}
}
+ return nil
+}
+
+func ReloadVPNServerConfig() error {
// notify configmanager
client := http.Client{
Timeout: 10 * time.Second,
@@ -157,7 +161,6 @@ func WriteVPNConfig(storage storage.Iface, vpnConfig VPNConfig) error {
if resp.StatusCode != http.StatusAccepted {
return fmt.Errorf("configmanager post error: received status code %d", resp.StatusCode)
}
-
return nil
}
diff --git a/webapp/src/Routes/Setup/VPNSetup.tsx b/webapp/src/Routes/Setup/VPNSetup.tsx
index ca26665..ff2a55b 100644
--- a/webapp/src/Routes/Setup/VPNSetup.tsx
+++ b/webapp/src/Routes/Setup/VPNSetup.tsx
@@ -239,7 +239,7 @@ export function VPNSetup() {
Enable IP Packet logging
- Metadata of IP packets passing the VPN can be logged and displayed in this admin portal. Useful if you want to see TCP connection requests, DNS requests, or http/https requests passing the VPN.
+ Metadata of IP packets passing the VPN can be logged and displayed. Useful if you want to see TCP connection requests, DNS requests, or http/https requests passing the VPN. Can generate a lot of logging data when all traffic is routed over the VPN (0.0.0.0/0 route), or when DNS requests are being logged.
@@ -248,10 +248,11 @@ export function VPNSetup() {