-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
185 lines (162 loc) · 5.71 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package main
import (
"flag"
"fmt"
"io/ioutil"
"regexp"
"time"
configMod "github.com/david-caro/netsnoop/internal/config"
"github.com/david-caro/netsnoop/internal/utils"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
log "github.com/sirupsen/logrus"
)
var iface = flag.String("iface", "wlp0s20f3", "Select interface where to capture")
var configPath = flag.String("configPath", "./netsnoop.yaml", "Path to the configuration yaml file")
var verbose = flag.Bool("verbose", false, "Enable verbose logging")
var promPath = flag.String("promPath", "./netsnoop.prom", "File to output prometheus stats")
var refreshSecs = flag.Uint("refreshSecs", 60, "How often to show the stats and write down the prom file")
func writePromFile(path *string, counter *map[string]map[string]int) error {
promData := "# HELP toolforge_internal_dependencies Number of times a tool has known to start a connection to the given known dependency\n"
promData += "# TYPE toolforge_internal_dependencies counter\n"
for toolName, sites := range *counter {
for site, count := range sites {
promData += fmt.Sprintf("toolforge_internal_dependencies{tool=\"%s\", dependency=\"%s\"} %d\n", toolName, site, count)
}
}
// needed as we use golang 1.11.6 and os.WriteFile is not supported yet
err := ioutil.WriteFile(*path, []byte(promData), 0644)
log.Debug("Wrote prometheus file ", path)
return err
}
func findSiteInPacket(packet gopacket.Packet, httpServicesRegexes []*regexp.Regexp) (string, bool) {
applicationLayer := packet.ApplicationLayer()
if applicationLayer != nil {
log.Debug("Application layer/Payload found.")
payload := string(applicationLayer.Payload())
return findSiteInString(payload, httpServicesRegexes)
}
return "", false
}
func findSiteInString(stringToCheck string, siteRegexes []*regexp.Regexp) (string, bool) {
for _, siteRegex := range siteRegexes {
match := siteRegex.FindString(stringToCheck)
if match != "" {
log.Debug("Found ", match, " in data:", stringToCheck)
return match, true
} else {
log.Debug("Nothing interesting found in:", stringToCheck, " with regex:", siteRegex)
}
}
return "", false
}
func main() {
flag.Parse()
log.SetFormatter(&log.JSONFormatter{})
log.Info(fmt.Sprintf("Starting up, verbose=%v, configPath='%s'", *verbose, *configPath))
if *verbose {
log.SetLevel(log.DebugLevel)
}
config, err := configMod.ReadConfig(*configPath)
if err != nil {
log.Error(err)
return
}
log.Debug("Got config: ", config)
// Opening Device
// for now capturing size 512, only interested in the http headers if any
// not interested in promiscuous listening either, only packets from this host
handle, err := pcap.OpenLive(*iface, int32(512), false, pcap.BlockForever)
if err != nil {
log.Fatal(err)
}
defer handle.Close()
ticker := time.NewTicker(5 * time.Second)
quit := make(chan struct{})
counters := make(chan map[string]map[string]int)
go func() {
for {
log.Debug("Waiting for ticker....")
select {
case <-ticker.C:
log.Debug("Waiting for counts....")
counts := <-counters
log.Info("Got counts ", counts)
writePromFile(promPath, &counts)
case <-quit:
ticker.Stop()
return
}
}
}()
defer ticker.Stop()
bpfFilter := configMod.ConfigToBPFFilter(config)
err = handle.SetBPFFilter(bpfFilter)
if err != nil {
log.Fatalf("error applying BPF Filter ", bpfFilter, " error:", err)
}
log.Info("Applying BPF filter: ", bpfFilter)
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
usersToServicesCount := make(map[string]map[string]int)
httpServicesRegexes := make([]*regexp.Regexp, 0, len(config.HttpServiceRegexes))
for _, serviceRegex := range config.HttpServiceRegexes {
regex, err := regexp.Compile(serviceRegex)
if err != nil {
log.Error("Unable to parse service regex: ", serviceRegex, " error:", err)
continue
}
httpServicesRegexes = append(httpServicesRegexes, regex)
}
last_stat_time := time.Now()
// we use the network traffic only to trigger a full scan, as what we are looking for is containers we can't really match per port
packetChannel := packetSource.Packets()
for {
select {
case packet := <-packetChannel:
if ip4Layer := packet.Layer(layers.LayerTypeIPv4); ip4Layer != nil {
layerData, _ := ip4Layer.(*layers.IPv4)
log.Debug("Got packet ", packet)
tcpLayer := packet.Layer(layers.LayerTypeTCP)
if tcpLayer == nil {
log.Debug("Skipping packet, not tcp")
continue
}
tcpLayerData, _ := tcpLayer.(*layers.TCP)
foundService, wasFound := findSiteInPacket(packet, httpServicesRegexes)
if wasFound {
log.Debug("Detected HTTP based site '", foundService, "'")
} else {
log.Debug("No site found, skipping packet")
continue
}
log.Debug("Extracting interesting IPs/port")
interestingIP := layerData.DstIP.String()
// the local port is the opposite (src<->dst) than the interesting ip
localPort := int(tcpLayerData.SrcPort)
log.Debug("Found interesting ip ", interestingIP, ", connected to local port ", localPort, " for service ", foundService)
err := utils.FindUsersForLocalPort(
packet,
interestingIP,
localPort,
foundService,
config.InterestingUsersPrefix,
&usersToServicesCount,
)
if err != nil {
log.Warn(" unable to get process for packet: ", err)
}
for userName, services := range usersToServicesCount {
log.Debug(" User:", userName, " services:", services)
}
continue
//log.Warn("Detected unknown ip ", packet.NetworkLayer().NetworkFlow().Dst().String())
}
default:
if time.Since(last_stat_time) > time.Duration(*refreshSecs)*time.Second {
log.Debug("Sending counts...")
counters <- usersToServicesCount
}
}
}
}