github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/seccheck/metadata.go (about) 1 // Copyright 2022 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package seccheck 16 17 import ( 18 "fmt" 19 "os" 20 "path" 21 22 "github.com/nicocha30/gvisor-ligolo/pkg/fd" 23 "github.com/nicocha30/gvisor-ligolo/pkg/sync" 24 ) 25 26 // PointX represents the checkpoint X. 27 const ( 28 PointClone Point = iota 29 PointContainerStart 30 PointExecve 31 PointExitNotifyParent 32 PointTaskExit 33 34 // Add new Points above this line. 35 pointLengthBeforeSyscalls 36 ) 37 38 // FieldCtxtX represents a data field that comes from the Context. 39 const ( 40 FieldCtxtContainerID Field = iota 41 FieldCtxtCredentials 42 FieldCtxtCwd 43 FieldCtxtProcessName 44 FieldCtxtThreadGroupID 45 FieldCtxtThreadGroupStartTime 46 FieldCtxtThreadID 47 FieldCtxtThreadStartTime 48 FieldCtxtTime 49 ) 50 51 // Fields for container/start point. 52 const ( 53 // FieldContainerStartEnv is an optional field to collect list of environment 54 // variables set for the container start process. 55 FieldContainerStartEnv Field = iota 56 ) 57 58 // Fields for sentry/execve point. 59 const ( 60 // FieldSentryExecveBinaryInfo is an optional field to collect information 61 // about the binary being executed. 62 FieldSentryExecveBinaryInfo Field = iota 63 ) 64 65 // Points is a map with all the trace points registered in the system. 66 var Points = map[string]PointDesc{} 67 68 // Sinks is a map with all the sinks registered in the system. 69 var Sinks = map[string]SinkDesc{} 70 71 // defaultContextFields are the fields present in most trace points. 72 var defaultContextFields = []FieldDesc{ 73 { 74 ID: FieldCtxtTime, 75 Name: "time", 76 }, 77 { 78 ID: FieldCtxtThreadID, 79 Name: "thread_id", 80 }, 81 { 82 ID: FieldCtxtThreadStartTime, 83 Name: "task_start_time", 84 }, 85 { 86 ID: FieldCtxtThreadGroupID, 87 Name: "group_id", 88 }, 89 { 90 ID: FieldCtxtThreadGroupStartTime, 91 Name: "thread_group_start_time", 92 }, 93 { 94 ID: FieldCtxtContainerID, 95 Name: "container_id", 96 }, 97 { 98 ID: FieldCtxtCredentials, 99 Name: "credentials", 100 }, 101 { 102 ID: FieldCtxtCwd, 103 Name: "cwd", 104 }, 105 { 106 ID: FieldCtxtProcessName, 107 Name: "process_name", 108 }, 109 } 110 111 // SinkDesc describes a sink that is available to be configured. 112 type SinkDesc struct { 113 // Name is a unique identifier for the sink. 114 Name string 115 // Setup is called outside the protection of the sandbox. This is done to 116 // allow the sink to do whatever is necessary to set it up. If it returns a 117 // file, this file is donated to the sandbox and passed to the sink when New 118 // is called. config is an opaque json object passed to the sink. 119 Setup func(config map[string]any) (*os.File, error) 120 // New creates a new sink. config is an opaque json object passed to the sink. 121 // endpoing is a file descriptor to the file returned in Setup. It's set to -1 122 // if Setup returned nil. 123 New func(config map[string]any, endpoint *fd.FD) (Sink, error) 124 } 125 126 // RegisterSink registers a new sink to make it discoverable. 127 func RegisterSink(sink SinkDesc) { 128 if _, ok := Sinks[sink.Name]; ok { 129 panic(fmt.Sprintf("Sink %q already registered", sink.Name)) 130 } 131 Sinks[sink.Name] = sink 132 } 133 134 // PointDesc describes a Point that is available to be configured. 135 // Schema for these points are defined in pkg/sentry/seccheck/points/. 136 type PointDesc struct { 137 // ID is the point unique identifier. 138 ID Point 139 // Name is the point unique name. Convention is to use the following format: 140 // namespace/name 141 // Examples: container/start, sentry/clone, etc. 142 Name string 143 // OptionalFields is a list of fields that are available in the point, but not 144 // collected unless specified when the Point is configured. 145 // Examples: fd_path, data for read/write Points, etc. 146 OptionalFields []FieldDesc 147 // ContextFields is a list of fields that can be collected from the context, 148 // but are not collected unless specified when the Point is configured. 149 // Examples: container_id, PID, etc. 150 ContextFields []FieldDesc 151 } 152 153 // FieldDesc describes an optional/context field that is available to be 154 // configured. 155 type FieldDesc struct { 156 // ID is the numeric identifier of the field. 157 ID Field 158 // Name is the unique field name. 159 Name string 160 } 161 162 func registerPoint(pt PointDesc) { 163 if _, ok := Points[pt.Name]; ok { 164 panic(fmt.Sprintf("Point %q already registered", pt.Name)) 165 } 166 if err := validateFields(pt.OptionalFields); err != nil { 167 panic(err) 168 } 169 if err := validateFields(pt.ContextFields); err != nil { 170 panic(err) 171 } 172 Points[pt.Name] = pt 173 } 174 175 func validateFields(fields []FieldDesc) error { 176 ids := make(map[Field]FieldDesc) 177 names := make(map[string]FieldDesc) 178 for _, f := range fields { 179 if other, ok := names[f.Name]; ok { 180 return fmt.Errorf("field %q has repeated name with field %q", f.Name, other.Name) 181 } 182 if other, ok := ids[f.ID]; ok { 183 return fmt.Errorf("field %q has repeated ID (%d) with field %q", f.Name, f.ID, other.Name) 184 } 185 names[f.Name] = f 186 ids[f.ID] = f 187 } 188 return nil 189 } 190 191 func addRawSyscallPoint(sysno uintptr) { 192 addSyscallPointHelper(SyscallRawEnter, sysno, fmt.Sprintf("sysno/%d", sysno), nil) 193 } 194 195 func addSyscallPoint(sysno uintptr, name string, optionalFields []FieldDesc) { 196 addSyscallPointHelper(SyscallEnter, sysno, name, optionalFields) 197 } 198 199 func addSyscallPointHelper(typ SyscallType, sysno uintptr, name string, optionalFields []FieldDesc) { 200 registerPoint(PointDesc{ 201 ID: GetPointForSyscall(typ, sysno), 202 Name: path.Join("syscall", name, "enter"), 203 OptionalFields: optionalFields, 204 ContextFields: defaultContextFields, 205 }) 206 registerPoint(PointDesc{ 207 ID: GetPointForSyscall(typ+1, sysno), 208 Name: path.Join("syscall", name, "exit"), 209 OptionalFields: optionalFields, 210 ContextFields: defaultContextFields, 211 }) 212 } 213 214 // genericInit initializes non-architecture-specific Points available in the system. 215 func genericInit() { 216 // Points from the container namespace. 217 registerPoint(PointDesc{ 218 ID: PointContainerStart, 219 Name: "container/start", 220 OptionalFields: []FieldDesc{ 221 { 222 ID: FieldContainerStartEnv, 223 Name: "env", 224 }, 225 }, 226 ContextFields: defaultContextFields, 227 }) 228 229 // Points from the sentry namespace. 230 registerPoint(PointDesc{ 231 ID: PointClone, 232 Name: "sentry/clone", 233 ContextFields: defaultContextFields, 234 }) 235 registerPoint(PointDesc{ 236 ID: PointExecve, 237 Name: "sentry/execve", 238 OptionalFields: []FieldDesc{ 239 { 240 ID: FieldSentryExecveBinaryInfo, 241 Name: "binary_info", 242 }, 243 }, 244 ContextFields: defaultContextFields, 245 }) 246 registerPoint(PointDesc{ 247 ID: PointExitNotifyParent, 248 Name: "sentry/exit_notify_parent", 249 ContextFields: []FieldDesc{ 250 { 251 ID: FieldCtxtTime, 252 Name: "time", 253 }, 254 { 255 ID: FieldCtxtThreadID, 256 Name: "thread_id", 257 }, 258 { 259 ID: FieldCtxtThreadStartTime, 260 Name: "task_start_time", 261 }, 262 { 263 ID: FieldCtxtThreadGroupID, 264 Name: "group_id", 265 }, 266 { 267 ID: FieldCtxtThreadGroupStartTime, 268 Name: "thread_group_start_time", 269 }, 270 { 271 ID: FieldCtxtContainerID, 272 Name: "container_id", 273 }, 274 { 275 ID: FieldCtxtCredentials, 276 Name: "credentials", 277 }, 278 { 279 ID: FieldCtxtProcessName, 280 Name: "process_name", 281 }, 282 }, 283 }) 284 registerPoint(PointDesc{ 285 ID: PointTaskExit, 286 Name: "sentry/task_exit", 287 ContextFields: defaultContextFields, 288 }) 289 } 290 291 var initOnce sync.Once 292 293 // Initialize initializes the Points available in the system. 294 // Must be called prior to using any of them. 295 func Initialize() { 296 initOnce.Do(func() { 297 genericInit() 298 archInit() 299 }) 300 }