-
Notifications
You must be signed in to change notification settings - Fork 171
node scanning wip #19376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
node scanning wip #19376
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,7 +2,9 @@ package compliance | |||||||||||||||||||
|
|
||||||||||||||||||||
| import ( | ||||||||||||||||||||
| "context" | ||||||||||||||||||||
| "fmt" | ||||||||||||||||||||
| "strconv" | ||||||||||||||||||||
| "strings" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| "github.com/pkg/errors" | ||||||||||||||||||||
| "github.com/quay/claircore/indexer/controller" | ||||||||||||||||||||
|
|
@@ -31,7 +33,11 @@ var ( | |||||||||||||||||||
| const ( | ||||||||||||||||||||
| rhcosFullName = "Red Hat Enterprise Linux CoreOS" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Golden image repository for RHCOS < 4.19 (OCP-style versions like 418.94.xxx) | ||||||||||||||||||||
| goldenKey = rhcc.RepositoryKey | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // RHEL CPE repository for RHCOS 4.19+ (RHEL-style versions like 9.6.xxx) | ||||||||||||||||||||
| rhelCPEKey = "rhel-cpe-repository" | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| var ( | ||||||||||||||||||||
|
|
@@ -419,12 +425,12 @@ func (c *nodeInventoryHandlerImpl) sendNodeIndex(toC chan<- *message.ExpiringMes | |||||||||||||||||||
| return | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| isRHCOS, version, err := c.nodeRHCOSMatcher.GetRHCOSVersion(indexWrap.NodeName) | ||||||||||||||||||||
| isRHCOS, ocpVersion, version, err := c.nodeRHCOSMatcher.GetRHCOSVersion(indexWrap.NodeName) | ||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||
| log.Warnf("Unable to determine RHCOS version for node %q: %v", indexWrap.NodeName, err) | ||||||||||||||||||||
| isRHCOS = false | ||||||||||||||||||||
| } | ||||||||||||||||||||
| log.Debugf("Node=%q discovered RHCOS=%t rhcos-version=%q", indexWrap.NodeName, isRHCOS, version) | ||||||||||||||||||||
| log.Debugf("Node=%q discovered RHCOS=%t ocp-version=%q rhcos-version=%q", indexWrap.NodeName, isRHCOS, ocpVersion, version) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| select { | ||||||||||||||||||||
| case <-c.stopper.Flow().StopRequested(): | ||||||||||||||||||||
|
|
@@ -441,7 +447,7 @@ func (c *nodeInventoryHandlerImpl) sendNodeIndex(toC chan<- *message.ExpiringMes | |||||||||||||||||||
| arch = extractArch(indexWrap.IndexReport) | ||||||||||||||||||||
| c.archCache[indexWrap.NodeName] = arch | ||||||||||||||||||||
| } | ||||||||||||||||||||
| log.Debugf("Attaching OCI entry for 'rhcos' to index-report for node %s: version=%s, arch=%s", indexWrap.NodeName, version, arch) | ||||||||||||||||||||
| log.Debugf("Attaching OCI entry for 'rhcos' to index-report for node %s: ocp-version=%s, version=%s, arch=%s", indexWrap.NodeName, ocpVersion, version, arch) | ||||||||||||||||||||
| irWrapperFunc = attachRPMtoRHCOS | ||||||||||||||||||||
| } | ||||||||||||||||||||
| toC <- message.New(¢ral.MsgFromSensor{ | ||||||||||||||||||||
|
|
@@ -452,7 +458,7 @@ func (c *nodeInventoryHandlerImpl) sendNodeIndex(toC chan<- *message.ExpiringMes | |||||||||||||||||||
| // This can be changed to CREATE or UPDATE for Sensor 4.8 or when Central 4.6 is out of support. | ||||||||||||||||||||
| Action: central.ResourceAction_UNSET_ACTION_RESOURCE, | ||||||||||||||||||||
| Resource: ¢ral.SensorEvent_IndexReport{ | ||||||||||||||||||||
| IndexReport: irWrapperFunc(version, arch, indexWrap.IndexReport), | ||||||||||||||||||||
| IndexReport: irWrapperFunc(ocpVersion, version, arch, indexWrap.IndexReport), | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
|
|
@@ -472,7 +478,37 @@ func normalizeVersion(version string) []int32 { | |||||||||||||||||||
| return []int32{v[0], v[1], 0, 0, 0, 0, 0, 0, 0, 0} | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| func noop(_, _ string, rpm *v4.IndexReport) *v4.IndexReport { | ||||||||||||||||||||
| // extractRHELMajorVersion extracts the major version from a RHEL-style version string. | ||||||||||||||||||||
| // For example, "9.6.20260217-1" returns "9". | ||||||||||||||||||||
| func extractRHELMajorVersion(version string) string { | ||||||||||||||||||||
| parts := strings.SplitN(version, ".", 2) | ||||||||||||||||||||
| if len(parts) >= 1 { | ||||||||||||||||||||
| return parts[0] | ||||||||||||||||||||
| } | ||||||||||||||||||||
| return "9" // Default to RHEL 9 if parsing fails | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // isNewRHCOSVersionSchema returns true if the RHCOS version follows the RHEL-style | ||||||||||||||||||||
| // (9.x, 10.x) rather than the OCP-style (4xx.x). | ||||||||||||||||||||
| // RHCOS 4.19+ reports versions like "9.6.20260217-1" instead of "418.94.202501011408". | ||||||||||||||||||||
| func isNewRHCOSVersionSchema(version string) bool { | ||||||||||||||||||||
|
Comment on lines
+491
to
+494
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Align The logic currently classifies any version with
Suggested change
|
||||||||||||||||||||
| if len(version) == 0 { | ||||||||||||||||||||
| return false | ||||||||||||||||||||
| } | ||||||||||||||||||||
| parts := strings.SplitN(version, ".", 2) | ||||||||||||||||||||
| if len(parts) < 2 { | ||||||||||||||||||||
| return false | ||||||||||||||||||||
| } | ||||||||||||||||||||
| major, err := strconv.Atoi(parts[0]) | ||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||
| return false | ||||||||||||||||||||
| } | ||||||||||||||||||||
| // RHEL versions are 8.x, 9.x, 10.x (single/double digit major) | ||||||||||||||||||||
| // OCP-derived versions are 412.x, 413.x, 418.x (triple digit major) | ||||||||||||||||||||
| return major < 100 | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| func noop(_, _, _ string, rpm *v4.IndexReport) *v4.IndexReport { | ||||||||||||||||||||
| return rpm | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -506,7 +542,7 @@ func extractArch(rpm *v4.IndexReport) string { | |||||||||||||||||||
| return "" | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| func attachRPMtoRHCOS(version, arch string, rpm *v4.IndexReport) *v4.IndexReport { | ||||||||||||||||||||
| func attachRPMtoRHCOS(ocpVersion, version, arch string, rpm *v4.IndexReport) *v4.IndexReport { | ||||||||||||||||||||
| idCandidate := 600 // Arbitrary selected. RHCOS has usually 520-560 rpm packages. | ||||||||||||||||||||
| envs := rpm.GetContents().GetEnvironments() | ||||||||||||||||||||
| if len(envs) == 0 { | ||||||||||||||||||||
|
|
@@ -516,7 +552,7 @@ func attachRPMtoRHCOS(version, arch string, rpm *v4.IndexReport) *v4.IndexReport | |||||||||||||||||||
| idCandidate++ | ||||||||||||||||||||
| } | ||||||||||||||||||||
| strID := strconv.Itoa(idCandidate) | ||||||||||||||||||||
| oci := buildRHCOSIndexReport(strID, version, arch) | ||||||||||||||||||||
| oci := buildRHCOSIndexReport(strID, ocpVersion, version, arch) | ||||||||||||||||||||
| for pkgID, pkg := range rpm.GetContents().GetPackages() { | ||||||||||||||||||||
| oci.Contents.Packages[pkgID] = pkg | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
@@ -536,7 +572,28 @@ func attachRPMtoRHCOS(version, arch string, rpm *v4.IndexReport) *v4.IndexReport | |||||||||||||||||||
| return oci | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| func buildRHCOSIndexReport(Id, version, arch string) *v4.IndexReport { | ||||||||||||||||||||
| // buildRHCOSIndexReport creates an IndexReport for the synthetic "rhcos" package. | ||||||||||||||||||||
| // For RHCOS 4.19+ (new schema with RHEL-style versions like "9.6.xxx"), it uses | ||||||||||||||||||||
| // the RHEL CPE repository instead of the golden image to enable proper vulnerability | ||||||||||||||||||||
| // matching against RHEL-based CVE data. | ||||||||||||||||||||
| func buildRHCOSIndexReport(Id, ocpVersion, version, arch string) *v4.IndexReport { | ||||||||||||||||||||
| // Determine repository configuration based on version schema | ||||||||||||||||||||
| repoKey := goldenKey | ||||||||||||||||||||
| repoName := goldenName | ||||||||||||||||||||
| repoURI := goldenURI | ||||||||||||||||||||
| repoCPE := "cpe:2.3:*" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if isNewRHCOSVersionSchema(version) { | ||||||||||||||||||||
| // RHCOS 4.19+ uses RHEL-style versions (e.g., "9.6.xxx") | ||||||||||||||||||||
| // Use RHEL CPE repository for proper vulnerability matching | ||||||||||||||||||||
| rhelMajor := extractRHELMajorVersion(version) | ||||||||||||||||||||
| repoKey = rhelCPEKey | ||||||||||||||||||||
| repoName = fmt.Sprintf("cpe:/o:redhat:enterprise_linux:%s::baseos", rhelMajor) | ||||||||||||||||||||
| repoURI = "" // RHEL CPE repos don't have a URI | ||||||||||||||||||||
| repoCPE = fmt.Sprintf("cpe:2.3:o:redhat:enterprise_linux:%s:*:baseos:*:*:*:*:*", rhelMajor) | ||||||||||||||||||||
| log.Debugf("RHCOS 4.19+ detected (version=%s): using RHEL %s CPE repository for vulnerability matching", version, rhelMajor) | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| return &v4.IndexReport{ | ||||||||||||||||||||
| // This hashId is arbitrary. The value doesn't play a role for matcher, but must be valid sha256. | ||||||||||||||||||||
| HashId: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | ||||||||||||||||||||
|
|
@@ -559,10 +616,10 @@ func buildRHCOSIndexReport(Id, version, arch string) *v4.IndexReport { | |||||||||||||||||||
| Name: "rhcos", | ||||||||||||||||||||
| Kind: "source", | ||||||||||||||||||||
| Version: version, | ||||||||||||||||||||
| Cpe: "cpe:2.3:*", // required to pass validation of scanner V4 API | ||||||||||||||||||||
| Cpe: repoCPE, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| Arch: arch, | ||||||||||||||||||||
| Cpe: "cpe:2.3:*", // required to pass validation of scanner V4 API | ||||||||||||||||||||
| Cpe: repoCPE, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| PackagesDEPRECATED: []*v4.Package{ | ||||||||||||||||||||
|
|
@@ -580,28 +637,28 @@ func buildRHCOSIndexReport(Id, version, arch string) *v4.IndexReport { | |||||||||||||||||||
| Name: "rhcos", | ||||||||||||||||||||
| Kind: "source", | ||||||||||||||||||||
| Version: version, | ||||||||||||||||||||
| Cpe: "cpe:2.3:*", // required to pass validation of scanner V4 API | ||||||||||||||||||||
| Cpe: repoCPE, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| Arch: arch, | ||||||||||||||||||||
| Cpe: "cpe:2.3:*", // required to pass validation of scanner V4 API | ||||||||||||||||||||
| Cpe: repoCPE, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| Repositories: map[string]*v4.Repository{ | ||||||||||||||||||||
| Id: { | ||||||||||||||||||||
| Id: Id, | ||||||||||||||||||||
| Name: goldenName, | ||||||||||||||||||||
| Key: goldenKey, | ||||||||||||||||||||
| Uri: goldenURI, | ||||||||||||||||||||
| Cpe: "cpe:2.3:*", // required to pass validation of scanner V4 API | ||||||||||||||||||||
| Name: repoName, | ||||||||||||||||||||
| Key: repoKey, | ||||||||||||||||||||
| Uri: repoURI, | ||||||||||||||||||||
| Cpe: repoCPE, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| RepositoriesDEPRECATED: []*v4.Repository{ | ||||||||||||||||||||
| { | ||||||||||||||||||||
| Id: Id, | ||||||||||||||||||||
| Name: goldenName, | ||||||||||||||||||||
| Key: goldenKey, | ||||||||||||||||||||
| Uri: goldenURI, | ||||||||||||||||||||
| Cpe: "cpe:2.3:*", // required to pass validation of scanner V4 API | ||||||||||||||||||||
| Name: repoName, | ||||||||||||||||||||
| Key: repoKey, | ||||||||||||||||||||
| Uri: repoURI, | ||||||||||||||||||||
| Cpe: repoCPE, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| // Environments must be present for the matcher to discover records | ||||||||||||||||||||
|
|
||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider simplifying the new RHCOS handling by narrowing the wrapper function signatures and centralizing version and repository selection logic into focused helpers.
You can preserve the new behavior while significantly reducing complexity by tightening the function signatures and consolidating the version/repo logic.
1. Drop
ocpVersionfrom the wrapper function typeocpVersionis only used for logging insendNodeIndexand is not consulted inbuildRHCOSIndexReport. Passing it through multiple layers just to satisfy a function type increases cognitive load.You can keep
GetRHCOSVersionas-is (it may be useful elsewhere) but narrow the wrapper function to exactly what’s needed:And adjust
attachRPMtoRHCOSaccordingly:This removes unused parameters from
noopandattachRPMtoRHCOSwhile keeping the same behavior (ocpVersion is still logged).2. Collapse the version-schema helpers into one utility
isNewRHCOSVersionSchemaandextractRHELMajorVersionboth parse the sameversionstring. You can do this once and return both the scheme and the major:Then
buildRHCOSIndexReportonly calls this once.3. Extract a repo config helper to remove duplication in
buildRHCOSIndexReportRight now
buildRHCOSIndexReportsetsrepoKey,repoName,repoURI,repoCPEwith a default and overrides them conditionally, then uses the same values multiple times. A small struct plus a helper makes the function shorter and clearer:Then
buildRHCOSIndexReportbecomes:This keeps all the new behavior (including the RHEL CPE selection and logging) but:
noopandattachRPMtoRHCOS.