Skip to content

Commit b3dc538

Browse files
Port sensors, loader, unloader and exec packages to windows
In order to port sensor package on Windows, adding exec parser and loader / unloader for Windows Tried to keep common functions between two OS in pkg/sensors/load.go. The windows implementation of some functions in load_windows.go looks like Linux implementation but is different because of unavailability of some features on windows like bpffs and btf. Also, the sensors.load() function in Windows is a work in progress and we might deprioritize code-reuse in favour of an ability to make future changes independently. Signed-off-by: Anadi Anadi <[email protected]>
1 parent b8a6b55 commit b3dc538

File tree

9 files changed

+647
-312
lines changed

9 files changed

+647
-312
lines changed

pkg/sensors/exec/exec_windows.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Authors of Tetragon
3+
4+
package exec
5+
6+
import (
7+
"bytes"
8+
"encoding/binary"
9+
"fmt"
10+
"strings"
11+
12+
"github.com/cilium/tetragon/pkg/api"
13+
"github.com/cilium/tetragon/pkg/api/ops"
14+
"github.com/cilium/tetragon/pkg/api/processapi"
15+
exec "github.com/cilium/tetragon/pkg/grpc/exec"
16+
"github.com/cilium/tetragon/pkg/logger"
17+
"github.com/cilium/tetragon/pkg/observer"
18+
"github.com/cilium/tetragon/pkg/sensors"
19+
"github.com/cilium/tetragon/pkg/sensors/exec/userinfo"
20+
"github.com/cilium/tetragon/pkg/sensors/program"
21+
"github.com/cilium/tetragon/pkg/strutils"
22+
"github.com/sirupsen/logrus"
23+
)
24+
25+
func msgToExecveUnix(m *processapi.MsgExecveEvent) *exec.MsgExecveEventUnix {
26+
unix := &exec.MsgExecveEventUnix{}
27+
unix.Unix = &processapi.MsgExecveEventUnix{}
28+
unix.Unix.Msg = m
29+
return unix
30+
}
31+
32+
func execParse(reader *bytes.Reader) (processapi.MsgProcess, bool, error) {
33+
proc := processapi.MsgProcess{}
34+
exec := processapi.MsgExec{}
35+
36+
if err := binary.Read(reader, binary.LittleEndian, &exec); err != nil {
37+
logger.GetLogger().WithError(err).Debug("Failed to read exec event")
38+
return proc, true, err
39+
}
40+
41+
proc.Size = exec.Size
42+
proc.PID = exec.PID
43+
proc.TID = exec.TID
44+
proc.NSPID = exec.NSPID
45+
proc.UID = exec.UID
46+
proc.Flags = exec.Flags
47+
proc.Ktime = exec.Ktime
48+
proc.AUID = exec.AUID
49+
proc.SecureExec = exec.SecureExec
50+
proc.Nlink = exec.Nlink
51+
proc.Ino = exec.Ino
52+
53+
size := exec.Size - processapi.MSG_SIZEOF_EXECVE
54+
if size > processapi.MSG_SIZEOF_BUFFER-processapi.MSG_SIZEOF_EXECVE {
55+
err := fmt.Errorf("msg exec size larger than argsbuffer")
56+
exec.Size = processapi.MSG_SIZEOF_EXECVE
57+
proc.Args = "enomem enomem"
58+
proc.Filename = "enomem"
59+
return proc, false, err
60+
}
61+
62+
args := make([]byte, size) //+2)
63+
if err := binary.Read(reader, binary.LittleEndian, &args); err != nil {
64+
proc.Size = processapi.MSG_SIZEOF_EXECVE
65+
proc.Args = "enomem enomem"
66+
proc.Filename = "enomem"
67+
return proc, false, err
68+
}
69+
70+
n := bytes.Index(args, []byte{0x00})
71+
if n != -1 {
72+
proc.Filename = strings.TrimPrefix(strutils.UTF8FromBPFBytes(args[:n]), "\\??\\")
73+
args = args[n+1:]
74+
}
75+
remFileName := proc.Filename
76+
if args[0] == '"' {
77+
remFileName = "\"" + proc.Filename + "\""
78+
}
79+
80+
proc.Args = strings.TrimPrefix(strutils.UTF8FromBPFBytes(args), remFileName)
81+
proc.Args = strings.TrimPrefix(proc.Args, " ")
82+
proc.Flags = api.EventNoCWDSupport
83+
return proc, false, nil
84+
}
85+
86+
func nopMsgProcess() processapi.MsgProcess {
87+
return processapi.MsgProcess{
88+
Filename: "<enomem>",
89+
Args: "<enomem>",
90+
}
91+
}
92+
93+
func handleExecve(r *bytes.Reader) ([]observer.Event, error) {
94+
var empty bool
95+
96+
m := processapi.MsgExecveEvent{}
97+
err := binary.Read(r, binary.LittleEndian, &m)
98+
if err != nil {
99+
return nil, err
100+
}
101+
//ToDo: Function Name
102+
msgUnix := msgToExecveUnix(&m)
103+
msgUnix.Unix.Process, empty, err = execParse(r)
104+
if err != nil && empty {
105+
msgUnix.Unix.Process = nopMsgProcess()
106+
}
107+
if err == nil && !empty {
108+
err = userinfo.MsgToExecveAccountUnix(msgUnix.Unix)
109+
if err != nil {
110+
logger.GetLogger().WithFields(logrus.Fields{
111+
"process.pid": msgUnix.Unix.Process.PID,
112+
"process.binary": msgUnix.Unix.Process.Filename,
113+
"process.uid": msgUnix.Unix.Process.UID,
114+
}).WithError(err).Trace("Resolving process uid to username record failed")
115+
}
116+
}
117+
return []observer.Event{msgUnix}, nil
118+
}
119+
120+
func msgToExitUnix(m *processapi.MsgExitEvent) *exec.MsgExitEventUnix {
121+
return &exec.MsgExitEventUnix{MsgExitEvent: *m}
122+
}
123+
124+
func handleExit(r *bytes.Reader) ([]observer.Event, error) {
125+
m := processapi.MsgExitEvent{}
126+
err := binary.Read(r, binary.LittleEndian, &m)
127+
if err != nil {
128+
return nil, err
129+
}
130+
msgUnix := msgToExitUnix(&m)
131+
return []observer.Event{msgUnix}, nil
132+
}
133+
134+
type execProbe struct{}
135+
136+
func (e *execProbe) LoadProbe(args sensors.LoadProbeArgs) error {
137+
return program.LoadTracepointProgram(args.BPFDir, args.Load, args.Maps, args.Verbose)
138+
}
139+
140+
func init() {
141+
AddExec()
142+
}
143+
144+
func AddExec() {
145+
sensors.RegisterProbeType("execve", &execProbe{})
146+
147+
observer.RegisterEventHandlerAtInit(ops.MSG_OP_EXECVE, handleExecve)
148+
observer.RegisterEventHandlerAtInit(ops.MSG_OP_EXIT, handleExit)
149+
}

pkg/sensors/load.go

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Authors of Tetragon
3+
4+
package sensors
5+
6+
import (
7+
"fmt"
8+
"os"
9+
"path"
10+
"path/filepath"
11+
"strings"
12+
13+
"github.com/cilium/tetragon/pkg/logger"
14+
"github.com/cilium/tetragon/pkg/option"
15+
"github.com/cilium/tetragon/pkg/sensors/program"
16+
"github.com/cilium/tetragon/pkg/tracingpolicy"
17+
"github.com/sirupsen/logrus"
18+
)
19+
20+
const (
21+
BPF_PROG_TYPE_UNSPEC = 0
22+
BPF_PROG_TYPE_SOCKET_FILTER = 1
23+
BPF_PROG_TYPE_KPROBE = 2
24+
BPF_PROG_TYPE_SCHED_CLS = 3
25+
BPF_PROG_TYPE_SCHED_ACT = 4
26+
BPF_PROG_TYPE_TRACEPOINT = 5
27+
BPF_PROG_TYPE_XDP = 6
28+
BPF_PROG_TYPE_PERF_EVENT = 7
29+
BPF_PROG_TYPE_CGROUP_SKB = 8
30+
BPF_PROG_TYPE_CGROUP_SOCK = 9
31+
BPF_PROG_TYPE_LWT_IN = 10
32+
BPF_PROG_TYPE_LWT_OUT = 11
33+
BPF_PROG_TYPE_LWT_XMIT = 12
34+
BPF_PROG_TYPE_SOCK_OPS = 13
35+
BPF_PROG_TYPE_SK_SKB = 14
36+
BPF_PROG_TYPE_CGROUP_DEVICE = 15
37+
BPF_PROG_TYPE_SK_MSG = 16
38+
BPF_PROG_TYPE_RAW_TRACEPOINT = 17
39+
BPF_PROG_TYPE_CGROUP_SOCK_ADDR = 18
40+
BPF_PROG_TYPE_LWT_SEG6LOCAL = 19
41+
BPF_PROG_TYPE_LIRC_MODE2 = 20
42+
BPF_PROG_TYPE_SK_REUSEPORT = 21
43+
BPF_PROG_TYPE_FLOW_DISSECTOR = 22
44+
BPF_PROG_TYPE_CGROUP_SYSCTL = 23
45+
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE = 24
46+
BPF_PROG_TYPE_CGROUP_SOCKOPT = 25
47+
BPF_PROG_TYPE_TRACING = 26
48+
BPF_PROG_TYPE_STRUCT_OPS = 27
49+
BPF_PROG_TYPE_EXT = 28
50+
BPF_PROG_TYPE_LSM = 29
51+
)
52+
53+
// LoadConfig loads the default sensor, including any from the configuration file.
54+
func LoadConfig(bpfDir string, sens []*Sensor) error {
55+
load := mergeSensors(sens)
56+
if err := load.Load(bpfDir); err != nil {
57+
return fmt.Errorf("tetragon, aborting could not load BPF programs: %w", err)
58+
}
59+
return nil
60+
}
61+
62+
func (s *Sensor) policyDir() string {
63+
return tracingpolicy.PolicyDir(s.Namespace, s.Policy)
64+
}
65+
66+
func (s *Sensor) createDirs(bpfDir string) {
67+
for _, p := range s.Progs {
68+
// setup sensor based program pin path
69+
p.PinPath = filepath.Join(s.policyDir(), s.Name, p.PinName)
70+
// and make the path
71+
if err := os.MkdirAll(filepath.Join(bpfDir, p.PinPath), os.ModeDir); err != nil {
72+
logger.GetLogger().WithError(err).
73+
WithField("prog", p.PinName).
74+
WithField("dir", p.PinPath).
75+
Warn("Failed to create program dir")
76+
}
77+
}
78+
s.BpfDir = bpfDir
79+
}
80+
81+
func (s *Sensor) removeDirs() {
82+
// Remove all the program dirs
83+
for _, p := range s.Progs {
84+
if err := os.Remove(filepath.Join(s.BpfDir, p.PinPath)); err != nil {
85+
logger.GetLogger().WithError(err).
86+
WithField("prog", p.PinName).
87+
WithField("dir", p.PinPath).
88+
Warn("Failed to remove program dir")
89+
}
90+
}
91+
// Remove sensor dir
92+
if err := os.Remove(filepath.Join(s.BpfDir, s.policyDir(), s.Name)); err != nil {
93+
logger.GetLogger().WithError(err).
94+
WithField("sensor", s.Name).
95+
WithField("dir", filepath.Join(s.policyDir(), s.Name)).
96+
Warn("Failed to remove sensor dir")
97+
}
98+
99+
// For policy dir the last one switches off the light.. there still
100+
// might be other sensors in the policy, so the last sensors removed
101+
// will succeed in removal policy dir.
102+
os.Remove(filepath.Join(s.BpfDir, s.policyDir()))
103+
}
104+
105+
func (s *Sensor) Unload(unpin bool) error {
106+
logger.GetLogger().Infof("Unloading sensor %s", s.Name)
107+
if !s.Loaded {
108+
return fmt.Errorf("unload of sensor %s failed: sensor not loaded", s.Name)
109+
}
110+
111+
if s.PreUnloadHook != nil {
112+
if err := s.PreUnloadHook(); err != nil {
113+
logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Pre unload hook failed")
114+
}
115+
}
116+
117+
var progs []string
118+
for _, p := range s.Progs {
119+
unloadProgram(p, unpin)
120+
progs = append(progs, p.String())
121+
}
122+
123+
var mapsOk, mapsErr []string
124+
for _, m := range s.Maps {
125+
if err := m.Unload(unpin); err != nil {
126+
logger.GetLogger().WithError(err).WithField("map", s.Name).Warn("Failed to unload map")
127+
mapsErr = append(mapsErr, m.String())
128+
} else {
129+
mapsOk = append(mapsOk, m.String())
130+
}
131+
}
132+
133+
if unpin {
134+
s.removeDirs()
135+
}
136+
137+
s.Loaded = false
138+
139+
if s.PostUnloadHook != nil {
140+
if err := s.PostUnloadHook(); err != nil {
141+
logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Post unload hook failed")
142+
}
143+
}
144+
145+
progsCleanup()
146+
logger.GetLogger().WithFields(logrus.Fields{
147+
"maps": mapsOk,
148+
"maps-error": mapsErr,
149+
"progs": progs,
150+
}).Infof("Sensor unloaded")
151+
return nil
152+
}
153+
154+
// Destroy will unload the hook and call DestroyHook, this hook is usually used
155+
// to clean up resources that were created during creation of the sensor.
156+
func (s *Sensor) Destroy(unpin bool) {
157+
err := s.Unload(unpin)
158+
if err != nil {
159+
// do not return on error but just log since Unload can only error on
160+
// sensor being already not loaded
161+
logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Unload failed during destroy")
162+
}
163+
164+
if s.DestroyHook != nil {
165+
err = s.DestroyHook()
166+
if err != nil {
167+
logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Destroy hook failed")
168+
}
169+
}
170+
s.Destroyed = true
171+
}
172+
173+
func (s *Sensor) findProgram(p *program.Program) error {
174+
logger.GetLogger().WithField("file", p.Name).Debug("Checking for bpf file")
175+
if _, err := os.Stat(p.Name); err == nil {
176+
logger.GetLogger().WithField("file", p.Name).Debug("Found bpf file")
177+
return nil
178+
}
179+
logger.GetLogger().WithField("file", p.Name).Debug("Candidate bpf file does not exist")
180+
last := strings.Split(p.Name, "/")
181+
filename := last[len(last)-1]
182+
183+
path := path.Join(option.Config.HubbleLib, filename)
184+
if _, err := os.Stat(path); err == nil {
185+
p.Name = path
186+
logger.GetLogger().WithField("file", path).Debug("Found bpf file")
187+
return nil
188+
}
189+
logger.GetLogger().WithField("file", path).Debug("Candidate bpf file does not exist")
190+
191+
return fmt.Errorf("sensor program %q can not be found", p.Name)
192+
}
193+
194+
// FindPrograms finds all the BPF programs in the sensor on the filesytem.
195+
func (s *Sensor) FindPrograms() error {
196+
for _, p := range s.Progs {
197+
if err := s.findProgram(p); err != nil {
198+
return err
199+
}
200+
}
201+
for _, m := range s.Maps {
202+
if err := s.findProgram(m.Prog); err != nil {
203+
return err
204+
}
205+
}
206+
return nil
207+
}
208+
209+
func mergeSensors(sensors []*Sensor) *Sensor {
210+
var progs []*program.Program
211+
var maps []*program.Map
212+
213+
for _, s := range sensors {
214+
progs = append(progs, s.Progs...)
215+
maps = append(maps, s.Maps...)
216+
}
217+
return &Sensor{
218+
Name: "__main__",
219+
Progs: progs,
220+
Maps: maps,
221+
}
222+
}
223+
224+
func unloadProgram(prog *program.Program, unpin bool) {
225+
log := logger.GetLogger().WithField("label", prog.Label).WithField("pin", prog.PinPath)
226+
227+
if !prog.LoadState.IsLoaded() {
228+
log.Debugf("Refusing to remove %s, program not loaded", prog.Label)
229+
return
230+
}
231+
if count := prog.LoadState.RefDec(); count > 0 {
232+
log.Debugf("Program reference count %d, not unloading yet", count)
233+
return
234+
}
235+
236+
if err := prog.Unload(unpin); err != nil {
237+
logger.GetLogger().WithField("name", prog.Name).WithError(err).Warn("Failed to unload program")
238+
}
239+
240+
log.Debug("BPF prog was unloaded")
241+
}
242+
243+
func UnloadSensors(sens []SensorIface) {
244+
for i := range sens {
245+
if err := sens[i].Unload(true); err != nil {
246+
logger.GetLogger().Warnf("Failed to unload sensor: %s", err)
247+
}
248+
}
249+
}

0 commit comments

Comments
 (0)