go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/kernel.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package resources
     5  
     6  import (
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/cockroachdb/errors"
    11  	"github.com/rs/zerolog/log"
    12  	"go.mondoo.com/cnquery/llx"
    13  	"go.mondoo.com/cnquery/providers-sdk/v1/inventory"
    14  	"go.mondoo.com/cnquery/providers-sdk/v1/plugin"
    15  	"go.mondoo.com/cnquery/providers-sdk/v1/util/convert"
    16  	"go.mondoo.com/cnquery/providers/os/connection/shared"
    17  	"go.mondoo.com/cnquery/providers/os/resources/kernel"
    18  )
    19  
    20  func initKernel(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) {
    21  	// this resource is only supported on linux
    22  	conn := runtime.Connection.(shared.Connection)
    23  	platform := conn.Asset().Platform
    24  
    25  	supported := false
    26  	if platform.IsFamily("linux") || platform.IsFamily("darwin") || platform.Name == "freebsd" {
    27  		supported = true
    28  	}
    29  
    30  	if supported == false {
    31  		return nil, nil, errors.New("kernel resource is only supported for unix platforms")
    32  	}
    33  
    34  	return args, nil, nil
    35  }
    36  
    37  type mqlKernelInternal struct {
    38  	moduleByName map[string]*mqlKernelModule
    39  	lock         sync.Mutex
    40  }
    41  
    42  type KernelVersion struct {
    43  	Name    string `json:"name"`
    44  	Version string `json:"version"`
    45  	Running bool   `json:"running"`
    46  }
    47  
    48  func (k *mqlKernel) installed() ([]interface{}, error) {
    49  	res := []KernelVersion{}
    50  
    51  	conn := k.MqlRuntime.Connection.(shared.Connection)
    52  	platform := conn.Asset().Platform
    53  
    54  	if platform.IsFamily(inventory.FAMILY_LINUX) {
    55  
    56  		// 1. gather running kernel information
    57  		info := k.GetInfo()
    58  		if info.Error != nil {
    59  			return nil, errors.New("could not determine kernel version")
    60  		}
    61  
    62  		kernelInfo, ok := info.Data.(map[string]interface{})
    63  		if !ok {
    64  			return nil, errors.New("no structured kernel information found")
    65  		}
    66  
    67  		runningKernelVersion := kernelInfo["version"].(string)
    68  
    69  		// 2. get all packages
    70  		raw, err := CreateResource(k.MqlRuntime, "packages", map[string]*llx.RawData{})
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  		packages := raw.(*mqlPackages)
    75  
    76  		tlist := packages.GetList()
    77  		if tlist.Error != nil {
    78  			return nil, tlist.Error
    79  		}
    80  		mqlPkgs := tlist.Data
    81  
    82  		filterKernel := func(pkg *mqlPackage) {}
    83  
    84  		if platform.IsFamily("debian") {
    85  			// debian based systems
    86  			// kernel version is  "4.19.0-13-cloud-amd64"
    87  			// filter by packages named "linux-image-*"
    88  			//[{
    89  			//	name: "linux-image-4.19.0-12-cloud-amd64"
    90  			//	version: "4.19.152-1"
    91  			//}, {
    92  			//	name: "linux-image-4.19.0-13-cloud-amd64"
    93  			//	version: "4.19.160-2"
    94  			//}, {
    95  			//	name: "linux-image-cloud-amd64"
    96  			//	version: "4.19+105+deb10u8"
    97  			//}]
    98  			filterKernel = func(pkg *mqlPackage) {
    99  				if strings.HasPrefix(pkg.Name.Data, "linux-image") {
   100  					kernelName := strings.TrimPrefix(pkg.Name.Data, "linux-image-")
   101  					running := false
   102  					if kernelName == runningKernelVersion {
   103  						running = true
   104  					}
   105  
   106  					res = append(res, KernelVersion{
   107  						Name:    kernelName,
   108  						Version: pkg.Version.Data,
   109  						Running: running,
   110  					})
   111  				}
   112  			}
   113  		} else if platform.IsFamily("redhat") || platform.Name == "amazonlinux" {
   114  			// rpm based systems
   115  			// kernel version is  "3.10.0-1160.11.1.el7.x86_64"
   116  			// filter by packages named "kernel"
   117  			//[{
   118  			//	name: "kernel"
   119  			//	version: "3.10.0-1127.el7"
   120  			//}, {
   121  			//	name: "kernel"
   122  			//	version: "3.10.0-1160.11.1.el7"
   123  			//}, {
   124  			//	name: "kernel"
   125  			//	version: "3.10.0-1127.19.1.el7"
   126  			//}]
   127  			filterKernel = func(pkg *mqlPackage) {
   128  				if pkg.Name.Data == "kernel" {
   129  					version := pkg.Version.Data
   130  					arch := pkg.Arch.Data
   131  
   132  					kernelName := version + "." + arch
   133  					running := false
   134  					if kernelName == runningKernelVersion {
   135  						running = true
   136  					}
   137  
   138  					res = append(res, KernelVersion{
   139  						Name:    pkg.Name.Data,
   140  						Version: version,
   141  						Running: running,
   142  					})
   143  				}
   144  			}
   145  		} else if platform.Name == "photon" {
   146  			filterKernel = func(pkg *mqlPackage) {
   147  				name := pkg.Name.Data
   148  				if strings.HasPrefix(name, "linux") {
   149  					version := pkg.Version.Data
   150  
   151  					kernelName := version + strings.TrimPrefix(name, "linux")
   152  					running := false
   153  					if kernelName == runningKernelVersion {
   154  						running = true
   155  					}
   156  
   157  					res = append(res, KernelVersion{
   158  						Name:    name,
   159  						Version: version + strings.TrimPrefix(name, "linux"),
   160  						Running: running,
   161  					})
   162  				}
   163  			}
   164  		} else if platform.IsFamily("suse") {
   165  			// kernel.info[version] == "4.12.14-122.23-default"
   166  			// rpm -qa | grep -i kernel
   167  			// kernel-default-4.12.14-122.23.1.x86_64
   168  			// kernel-firmware-20190618-5.14.1.noarch
   169  			// kernel-default-4.12.14-122.60.1.x86_64
   170  			// cat /proc/version
   171  			// Linux version 4.12.14-122.23-default (geeko@buildhost)
   172  			filterKernel = func(pkg *mqlPackage) {
   173  				name := pkg.Name.Data
   174  				if strings.HasPrefix(name, "kernel-") {
   175  					version := pkg.Version.Data
   176  
   177  					kernelType := strings.TrimPrefix(name, "kernel")
   178  					running := false
   179  
   180  					// NOTE: pkg version is 4.12.14-122.23.1 while the kernel version is 4.12.14-122.23
   181  					if strings.HasSuffix(runningKernelVersion, kernelType) && strings.HasPrefix(version, strings.TrimSuffix(runningKernelVersion, kernelType)) {
   182  						running = true
   183  					}
   184  
   185  					res = append(res, KernelVersion{
   186  						Name:    name,
   187  						Version: version + strings.TrimPrefix(name, "kernel"),
   188  						Running: running,
   189  					})
   190  				}
   191  			}
   192  		}
   193  
   194  		for i := range mqlPkgs {
   195  			mqlPkg := mqlPkgs[i]
   196  			pkg := mqlPkg.(*mqlPackage)
   197  			filterKernel(pkg)
   198  		}
   199  	}
   200  
   201  	// empty when there is no kernel information found
   202  	return convert.JsonToDictSlice(res)
   203  }
   204  
   205  func (k *mqlKernel) info() (interface{}, error) {
   206  	// find suitable kernel module manager
   207  	conn := k.MqlRuntime.Connection.(shared.Connection)
   208  	mm, err := kernel.ResolveManager(conn)
   209  	if mm == nil || err != nil {
   210  		return nil, errors.Wrap(err, "could not detect suitable kernel module manager for platform")
   211  	}
   212  
   213  	// retrieve all kernel modules
   214  	kernelInfo, err := mm.Info()
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	return convert.JsonToDict(kernelInfo)
   220  }
   221  
   222  func (k *mqlKernel) parameters() (map[string]interface{}, error) {
   223  	// find suitable kernel module manager
   224  	conn := k.MqlRuntime.Connection.(shared.Connection)
   225  	mm, err := kernel.ResolveManager(conn)
   226  	if mm == nil || err != nil {
   227  		return nil, errors.Wrap(err, "could not detect suitable kernel module manager for platform")
   228  	}
   229  
   230  	// retrieve all kernel modules
   231  	kernelParameters, err := mm.Parameters()
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	// copy values to fulfill the interface
   237  	res := make(map[string]interface{})
   238  	for key, value := range kernelParameters {
   239  		res[key] = value
   240  	}
   241  
   242  	return res, nil
   243  }
   244  
   245  func (k *mqlKernel) modules() ([]interface{}, error) {
   246  	k.lock.Lock()
   247  	defer k.lock.Unlock()
   248  
   249  	// find suitable kernel module manager
   250  	conn := k.MqlRuntime.Connection.(shared.Connection)
   251  	mm, err := kernel.ResolveManager(conn)
   252  	if mm == nil || err != nil {
   253  		return nil, errors.Wrap(err, "could not detect suitable kernel module manager for platform")
   254  	}
   255  
   256  	// retrieve all kernel modules
   257  	kernelModules, err := mm.Modules()
   258  	if err != nil {
   259  		return nil, errors.Wrap(err, "could not retrieve kernel module list for platform")
   260  	}
   261  	log.Debug().Int("modules", len(kernelModules)).Msg("[kernel.modules]> modules")
   262  
   263  	// create MQL kernel module entry resources for each entry
   264  	moduleEntries := make([]interface{}, len(kernelModules))
   265  	for i, kernelModule := range kernelModules {
   266  
   267  		raw, err := CreateResource(k.MqlRuntime, "kernel.module", map[string]*llx.RawData{
   268  			"name":   llx.StringData(kernelModule.Name),
   269  			"size":   llx.StringData(kernelModule.Size),
   270  			"loaded": llx.BoolTrue,
   271  		})
   272  		if err != nil {
   273  			return nil, err
   274  		}
   275  
   276  		moduleEntries[i] = raw.(*mqlKernelModule)
   277  	}
   278  
   279  	return moduleEntries, k.refreshCache(moduleEntries)
   280  }
   281  
   282  func (x *mqlKernel) refreshCache(all []interface{}) error {
   283  	if all == nil {
   284  		raw := x.GetModules()
   285  		if raw.Error != nil {
   286  			return raw.Error
   287  		}
   288  		all = raw.Data
   289  	}
   290  
   291  	x.moduleByName = map[string]*mqlKernelModule{}
   292  
   293  	for i := range all {
   294  		u := all[i].(*mqlKernelModule)
   295  		x.moduleByName[u.Name.Data] = u
   296  	}
   297  
   298  	return nil
   299  }
   300  
   301  func initKernelModule(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) {
   302  	if len(args) > 2 {
   303  		return args, nil, nil
   304  	}
   305  
   306  	nameRaw := args["name"]
   307  	if nameRaw == nil {
   308  		return args, nil, nil
   309  	}
   310  	name := nameRaw.Value.(string)
   311  
   312  	obj, err := CreateResource(runtime, "kernel", map[string]*llx.RawData{})
   313  	if err != nil {
   314  		return nil, nil, err
   315  	}
   316  	kernel := obj.(*mqlKernel)
   317  
   318  	if err = kernel.refreshCache(nil); err != nil {
   319  		return nil, nil, err
   320  	}
   321  
   322  	if res, ok := kernel.moduleByName[name]; ok {
   323  		return nil, res, nil
   324  	}
   325  
   326  	res := &mqlKernelModule{}
   327  	res.Name = plugin.TValue[string]{Data: name, State: plugin.StateIsSet}
   328  	res.Size.State = plugin.StateIsSet | plugin.StateIsNull
   329  	res.Loaded = plugin.TValue[bool]{Data: false, State: plugin.StateIsSet}
   330  	return nil, res, nil
   331  }
   332  
   333  func (k *mqlKernelModule) id() (string, error) {
   334  	return k.Name.Data, nil
   335  }