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  }