-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsyscallset_linux.go
More file actions
158 lines (138 loc) · 4.36 KB
/
Copy pathsyscallset_linux.go
File metadata and controls
158 lines (138 loc) · 4.36 KB
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
// SPDX-FileCopyrightText: Alvar Penning
//
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux
package syscallset
import (
"fmt"
"runtime"
"strings"
seccomp "github.com/elastic/go-seccomp-bpf"
"github.com/elastic/go-seccomp-bpf/arch"
)
// filterUnsupportedSyscalls from other architectures as the current one.
func filterUnsupportedSyscalls(in []string) (out []string, err error) {
info, infoErr := arch.GetInfo(runtime.GOARCH)
if infoErr != nil {
err = infoErr
return
}
for _, syscall := range in {
if _, exist := info.SyscallNames[syscall]; !exist {
continue
}
out = append(out, syscall)
}
return
}
// unwrapSyscalls from the given syscall filter string to an array of syscalls.
func unwrapSyscalls(syscallFilter string) (syscalls []string, err error) {
syscallMap := map[string]struct{}{}
for _, obj := range strings.Split(strings.TrimSpace(syscallFilter), " ") {
// If an object is prefixed by "~", it will be removed from the previously
// allowed syscalls. By default, new syscalls are added to the set.
isAdditive := true
if obj[0] == '~' {
isAdditive = false
obj = obj[1:]
}
var tmpSyscalls []string
if obj[0] == '@' {
var exists bool
tmpSyscalls, exists = syscallSets[obj[1:]]
if !exists {
return nil, fmt.Errorf("syscall set %q does not exist", obj)
}
} else {
tmpSyscalls = []string{obj}
}
// Filter out unsupported syscalls on this architecture.
if tmpSyscalls, err = filterUnsupportedSyscalls(tmpSyscalls); err != nil {
return
}
for _, syscall := range tmpSyscalls {
if isAdditive {
syscallMap[syscall] = struct{}{}
} else {
delete(syscallMap, syscall)
}
}
}
for k := range syscallMap {
syscalls = append(syscalls, k)
}
return
}
// limit the usable syscalls by applying a seccomp-bpf filter. The given action
// will be performed for unspecified syscalls.
func limit(syscallFilter string, action seccomp.Action) error {
syscalls, err := unwrapSyscalls("@default " + syscallFilter)
if err != nil {
return err
}
return seccomp.LoadFilter(seccomp.Filter{
NoNewPrivs: true,
Flag: seccomp.FilterFlagTSync,
Policy: seccomp.Policy{
DefaultAction: action,
Syscalls: []seccomp.SyscallGroup{
{
Action: seccomp.ActionAllow,
Names: syscalls,
},
},
},
})
}
// IsSupported returns true if filtering syscalls through seccomp-bpf is
// possible on this platform.
func IsSupported() bool {
if !seccomp.Supported() {
return false
}
_, infoErr := arch.GetInfo(runtime.GOARCH)
return infoErr == nil
}
// LimitTo a subset of the available Linux syscalls using a systemd system call
// filter string.
//
// A filter string might contain both syscall sets, prefixed by an at sign (@),
// as well as single syscalls by their name. The list of syscall sets is either
// available in this package's main documentation or can be fetched from
// systemd's exec documentation:
//
// https://www.freedesktop.org/software/systemd/man/systemd.exec.html#System%20Call%20Filtering
//
// The filter acts as an allow list. Thus, every other syscall results in the
// termination of the process and its children. One can remove single syscalls
// or smaller sets again by prefixing them with a tilde (~). As the set of
// allowed syscalls is created by parsing the words from left to right, one
// should start with building the allow list and reducing it afterwards.
//
// A small subset of syscalls (@default) is always allowed. Thus, when calling
// with an empty string, a very strict filter is applied, not even allowing
// using stdin or stdout.
//
// Unknown syscalls are being ignored, as they might only be supported on other
// architectures. However, unknown syscall sets will result in an error.
//
// A simple example with systemd's wide @system-service might be:
//
// @system-service
//
// Allowing some IO and file system access might be achieved through:
//
// @basic-io @file-system @io-event
//
// To restrict a wider set might be used like the following:
//
// @system-service ~@process ~@setuid
func LimitTo(syscallFilter string) error {
return limit(syscallFilter, seccomp.ActionKillProcess)
}
// LimitAndLog acts like LimitTo; however, non allowed syscalls are being logged
// instead of resulting in aborting the process. This might be useful for
// testing the application.
func LimitAndLog(syscallFilter string) error {
return limit(syscallFilter, seccomp.ActionLog)
}