Skip to content

Commit 84fd91e

Browse files
committed
capabilities: do not drop caps by default
- do not drop capabilities by default (allows bypass) - return reference instead of new instance (fix) - add "get instance" logic to singleton - allow dropping capabilities for testing purposes example to enable capabilities drop: ... --capabilities bypass=false \ --capabilities add=cap_xxx,cap_yyy \ --capabilities drop=cap_aaa,cap_bbb
1 parent 29b89f8 commit 84fd91e

File tree

9 files changed

+81
-72
lines changed

9 files changed

+81
-72
lines changed

cmd/tracee-ebpf/flags/capabilities.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,15 @@ Available capabilities:
2626

2727
func PrepareCapabilities(capsSlice []string) (tracee.CapabilitiesConfig, error) {
2828
capsConfig := tracee.CapabilitiesConfig{
29-
BypassCaps: false,
29+
BypassCaps: true, // bypass capabilities by default
3030
}
3131

3232
for _, slice := range capsSlice {
3333
if strings.Contains(slice, "bypass=") {
3434
b := strings.TrimPrefix(slice, "bypass=")
35-
if b == "1" || b == "true" {
36-
capsConfig.BypassCaps = true
37-
capsConfig.AddCaps = nil
38-
capsConfig.DropCaps = nil
39-
return capsConfig, nil
40-
}
41-
if b != "0" && b != "false" {
35+
if b == "0" || b == "false" {
36+
capsConfig.BypassCaps = false
37+
} else if b != "1" && b != "true" {
4238
return capsConfig, fmt.Errorf("bypass should either be true or false")
4339
}
4440
}

cmd/tracee-rules/main.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ func main() {
2929
Name: "tracee-rules",
3030
Usage: "A rule engine for Runtime Security",
3131
Action: func(c *cli.Context) error {
32+
33+
// Capabilities command line flags
34+
35+
err := capabilities.Initialize(c.Bool("allcaps"))
36+
if err != nil {
37+
return err
38+
}
39+
3240
if c.NumFlags() == 0 {
3341
cli.ShowAppHelp(c)
3442
return errors.New("no flags specified")
@@ -49,13 +57,6 @@ func main() {
4957
)
5058
}
5159

52-
// Capabilities command line flags
53-
54-
err := capabilities.NewCapabilities(c.Bool("allcaps"))
55-
if err != nil {
56-
return err
57-
}
58-
5960
var target string
6061
switch strings.ToLower(c.String("rego-runtime-target")) {
6162
case "wasm":

pkg/capabilities/capabilities.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import (
1111
"kernel.org/pub/linux/libs/security/libcap/cap"
1212
)
1313

14-
var Caps Capabilities // singleton for all packages
14+
var once sync.Once
15+
var caps *Capabilities // singleton for all packages
1516

1617
const pkgName = "capabilities"
1718

@@ -31,26 +32,42 @@ const (
3132
)
3233

3334
type Capabilities struct {
34-
have *cap.Set
35-
all map[cap.Value]map[ringType]bool
36-
bypass bool
37-
initialized bool
38-
lock *sync.Mutex // big lock to guarantee all threads are on the same ring
35+
have *cap.Set
36+
all map[cap.Value]map[ringType]bool
37+
bypass bool
38+
lock *sync.Mutex // big lock to guarantee all threads are on the same ring
3939
}
4040

41-
func NewCapabilities(bypass bool) error {
42-
Caps = Capabilities{}
43-
return Caps.initialize(bypass)
41+
// Initialize initializes the "caps" instance (singleton).
42+
func Initialize(bypass bool) error {
43+
var err error
44+
45+
once.Do(func() {
46+
caps = &Capabilities{}
47+
err = caps.initialize(bypass)
48+
})
49+
50+
return err
51+
}
52+
53+
// GetInstance returns current "caps" instance. It initializes capabilities if
54+
// needed, bypassing the privilege dropping by default.
55+
func GetInstance() *Capabilities {
56+
if caps == nil {
57+
err := Initialize(true)
58+
if err != nil {
59+
return nil
60+
}
61+
62+
}
63+
return caps
4464
}
4565

4666
func (c *Capabilities) initialize(bypass bool) error {
4767
if bypass {
4868
c.bypass = true
4969
return nil
5070
}
51-
if c.initialized {
52-
return alreadyInitialized()
53-
}
5471

5572
c.lock = new(sync.Mutex)
5673
c.all = make(map[cap.Value]map[ringType]bool)
@@ -323,10 +340,6 @@ func couldNotGetProc(e error) error {
323340
return fmt.Errorf("could not get capabilities: %v", e)
324341
}
325342

326-
func alreadyInitialized() error {
327-
return fmt.Errorf("capabilities were already initialized")
328-
}
329-
330343
//
331344
// Standalone Functions
332345
//

pkg/containers/path_resolver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func (cPathRes PathResolver) ResolveAbsolutePath(mountNSAbsolutePath string, mou
3838
for _, pid := range pids {
3939
procRootPath := fmt.Sprintf("/proc/%d/root", int(pid))
4040
// fs.FS interface requires relative paths, so the '/' prefix should be trimmed.
41-
err := capabilities.Caps.Required(func() error {
41+
err := capabilities.GetInstance().Required(func() error {
4242
_, err := fs.Stat(cPathRes.fs, strings.TrimPrefix(procRootPath, "/"))
4343
return err
4444
})

pkg/containers/path_resolver_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ func TestPathResolver_ResolveAbsolutePath(t *testing.T) {
6464
testMntNS := 1
6565
testFilePath := "/tmp/tmp.so"
6666

67-
capabilities.NewCapabilities(true)
67+
err := capabilities.Initialize(true) // initialize capabilities
68+
assert.NoError(t, err)
6869

6970
for _, testCase := range testCases {
7071
t.Run(testCase.Name, func(t *testing.T) {

pkg/ebpf/events_processor.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const (
5252

5353
func (t *Tracee) processEvent(event *trace.Event) error {
5454
eventId := events.ID(event.EventID)
55+
5556
switch eventId {
5657

5758
case events.VfsWrite, events.VfsWritev, events.KernelWrite:
@@ -148,7 +149,7 @@ func (t *Tracee) processEvent(event *trace.Event) error {
148149
if !ok || lastCtime != castedSourceFileCtime {
149150

150151
// capture (ring1)
151-
err = capabilities.Caps.Required(func() error {
152+
err = capabilities.GetInstance().Required(func() error {
152153
return utils.CopyRegularFileByRelativePath(
153154
sourceFilePath,
154155
t.outDir,
@@ -177,7 +178,7 @@ func (t *Tracee) processEvent(event *trace.Event) error {
177178
} else {
178179

179180
// ring1
180-
capabilities.Caps.Required(func() error {
181+
capabilities.GetInstance().Required(func() error {
181182
currentHash, err = computeFileHashAtPath(sourceFilePath)
182183
if err == nil {
183184
hashInfoObj = fileExecInfo{castedSourceFileCtime, currentHash}

pkg/ebpf/probes/probes.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ type probes struct {
5252

5353
// Init initializes a Probes interface
5454
func Init(module *bpf.Module, netEnabled bool) (Probes, error) {
55-
5655
binaryPath := "/proc/self/exe"
5756

5857
allProbes := map[Handle]Probe{
@@ -147,7 +146,7 @@ func Init(module *bpf.Module, netEnabled bool) (Probes, error) {
147146
}
148147
}
149148
} else {
150-
capabilities.Caps.Require(cap.NET_ADMIN) // add to required
149+
capabilities.GetInstance().Require(cap.NET_ADMIN) // add to required
151150
}
152151

153152
return &probes{

pkg/ebpf/tracee.go

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -266,20 +266,20 @@ func New(cfg Config) (*Tracee, error) {
266266
return nil, fmt.Errorf("validation error: %v", err)
267267
}
268268

269-
// create tracee
269+
// Create Tracee
270270
t := &Tracee{
271271
config: cfg,
272272
writtenFiles: make(map[string]string),
273273
capturedFiles: make(map[string]int64),
274274
events: GetEssentialEventsList(),
275275
}
276276

277-
// Start capabilities rings
278-
// err = capabilities.NewCapabilities(t.config.Capabilities.BypassCaps)
279-
err = capabilities.NewCapabilities(true) // TODO: force until drop priv is finished
277+
// Initialize capabilities rings soon
278+
err = capabilities.Initialize(t.config.Capabilities.BypassCaps)
280279
if err != nil {
281280
return t, err
282281
}
282+
caps := capabilities.GetInstance()
283283

284284
// Pseudo events added by capture
285285
for eventID, eCfg := range GetCaptureEventsList(cfg) {
@@ -304,7 +304,7 @@ func New(cfg Config) (*Tracee, error) {
304304
return t, fmt.Errorf("could not get event")
305305
}
306306
for _, capArray := range evt.Dependencies.Capabilities {
307-
capabilities.Caps.Require(capArray)
307+
caps.Require(capArray)
308308
}
309309
}
310310

@@ -314,7 +314,7 @@ func New(cfg Config) (*Tracee, error) {
314314
if err != nil {
315315
return t, err
316316
}
317-
err = capabilities.Caps.Require(capsToAdd...)
317+
err = caps.Require(capsToAdd...)
318318
if err != nil {
319319
return t, err
320320
}
@@ -323,7 +323,7 @@ func New(cfg Config) (*Tracee, error) {
323323
if err != nil {
324324
return t, err
325325
}
326-
err = capabilities.Caps.Unrequire(capsToDrop...)
326+
err = caps.Unrequire(capsToDrop...)
327327
if err != nil {
328328
return t, err
329329
}
@@ -377,7 +377,7 @@ func (t *Tracee) Init() error {
377377
// Init kernel symbols map
378378

379379
if initReq.kallsyms {
380-
err = capabilities.Caps.Requested(func() error { // ring2
380+
err = capabilities.GetInstance().Requested(func() error { // ring2
381381

382382
t.kernelSymbols, err = helpers.NewKernelSymbolsMap()
383383
if err != nil {
@@ -405,7 +405,7 @@ func (t *Tracee) Init() error {
405405

406406
// Initialize containers enrichment logic
407407

408-
capabilities.Caps.Requested(func() error { // TODO: workaround until PR: #2233 is in place
408+
capabilities.GetInstance().Requested(func() error { // TODO: workaround until PR: #2233 is in place
409409

410410
t.containers, err = containers.New(t.config.Sockets, "containers_map", t.config.Debug)
411411
if err != nil {
@@ -1134,32 +1134,32 @@ func (t *Tracee) initBPF() error {
11341134
isCaptureNetSet := t.config.Capture.NetIfaces != nil
11351135
isFilterNetSet := len(t.config.Filter.NetFilter.Interfaces()) != 0
11361136

1137-
newModuleArgs := bpf.NewModuleArgs{
1138-
KConfigFilePath: t.config.KernelConfig.GetKernelConfigFilePath(),
1139-
BTFObjPath: t.config.BTFObjPath,
1140-
BPFObjBuff: t.config.BPFObjBytes,
1141-
BPFObjName: t.config.BPFObjPath,
1142-
}
1137+
// Execute code with higher privileges: ring1 (required)
11431138

1144-
// Open the eBPF object file (create a new module)
1139+
err = capabilities.GetInstance().Required(func() error {
11451140

1146-
t.bpfModule, err = bpf.NewModuleFromBufferArgs(newModuleArgs)
1147-
if err != nil {
1148-
return err
1149-
}
1141+
newModuleArgs := bpf.NewModuleArgs{
1142+
KConfigFilePath: t.config.KernelConfig.GetKernelConfigFilePath(),
1143+
BTFObjPath: t.config.BTFObjPath,
1144+
BPFObjBuff: t.config.BPFObjBytes,
1145+
BPFObjName: t.config.BPFObjPath,
1146+
}
11501147

1151-
// Initialize probes
1148+
// Open the eBPF object file (create a new module)
11521149

1153-
netEnabled := isCaptureNetSet || isFilterNetSet
1150+
t.bpfModule, err = bpf.NewModuleFromBufferArgs(newModuleArgs)
1151+
if err != nil {
1152+
return err
1153+
}
11541154

1155-
t.probes, err = probes.Init(t.bpfModule, netEnabled)
1156-
if err != nil {
1157-
return err
1158-
}
1155+
// Initialize probes
11591156

1160-
// Execute code with higher privileges: ring1 (required)
1157+
netEnabled := isCaptureNetSet || isFilterNetSet
11611158

1162-
err = capabilities.Caps.Required(func() error {
1159+
t.probes, err = probes.Init(t.bpfModule, netEnabled)
1160+
if err != nil {
1161+
return err
1162+
}
11631163

11641164
// Load the eBPF object into kernel
11651165

@@ -1326,7 +1326,7 @@ func (t *Tracee) Run(ctx gocontext.Context) error {
13261326

13271327
// Close cleans up created resources
13281328
func (t *Tracee) Close() {
1329-
err := capabilities.Caps.Required(func() error { // ring1
1329+
err := capabilities.GetInstance().Required(func() error { // ring1
13301330

13311331
if t.probes != nil {
13321332
err := t.probes.DetachAll()
@@ -1399,7 +1399,7 @@ func (t *Tracee) updateFileSHA() {
13991399

14001400
func (t *Tracee) invokeInitEvents() {
14011401
if t.events[events.InitNamespaces].emit {
1402-
capabilities.Caps.Requested(func() error { // ring2
1402+
capabilities.GetInstance().Requested(func() error { // ring2
14031403
systemInfoEvent := events.InitNamespacesEvent()
14041404
t.config.ChanEvents <- systemInfoEvent
14051405
return nil
@@ -1483,7 +1483,7 @@ func (t *Tracee) triggerSeqOpsIntegrityCheckCall(
14831483
}
14841484

14851485
func (t *Tracee) updateKallsyms() error {
1486-
return capabilities.Caps.Requested(func() error { // ring2
1486+
return capabilities.GetInstance().Requested(func() error { // ring2
14871487

14881488
kernelSymbols, err := helpers.NewKernelSymbolsMap()
14891489
if err != nil {

pkg/utils/sharedobjs/host_symbols_loader.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,12 @@ func (soCache *dynamicSymbolsLRUCache) Add(obj ObjInfo, dynamicSymbols *dynamicS
101101
// loadSharedObjectDynamicSymbols load all dynamic symbols of a shared object file in given path.
102102
func loadSharedObjectDynamicSymbols(path string) (*dynamicSymbols, error) {
103103
var loadedObject *elf.File
104-
err := capabilities.Caps.Required(func() error {
104+
105+
capabilities.GetInstance().Required(func() error {
105106
var e error
106107
loadedObject, e = elf.Open(path)
107108
return e
108109
})
109-
if err != nil {
110-
return nil, err
111-
}
112110
defer loadedObject.Close()
113111

114112
dynamicSymbols, err := loadedObject.DynamicSymbols()

0 commit comments

Comments
 (0)