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 }