Skip to content

ROX-13770: Introduce local Node Scanner #1164

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

Merged
merged 10 commits into from
May 17, 2023
Merged

Conversation

Maddosaurus
Copy link
Contributor

@Maddosaurus Maddosaurus commented Apr 19, 2023

To make local experimentation and debugging with Node Scanning code easier, this PR introduces a CLI binary that calls said code.
For the scan to work, the binary requires the path to a folder containing a filesystem.
As the Node Scanning code requires the rpmdb binary to be present in the PATH, additionally to the CLI binary, also a self-contained Docker image is introduced to make running the binary possible on common dev platforms.

After discussions in a Maple Demo Session, I have created a follow-up ticket with more feature ideas for this local node scanner:
ROX-16621.

@Maddosaurus Maddosaurus requested review from RTann and vikin91 April 19, 2023 13:11
@ghost
Copy link

ghost commented Apr 19, 2023

Images are ready for the commit at 1b12dd0.

To use the images, use the tag 2.29.x-30-g1b12dd0afe.

Copy link
Contributor

@vikin91 vikin91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but I think we should mention the content sets in the readme as it is an important element of this all


COPY ./bin/local-nodescanner /local-nodescanner

ENTRYPOINT [ "/local-nodescanner" ]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add that newline at the end of file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


## Requirements
The scanning code requires an `rpmdb` binary to be available in the executing systems `PATH`.
Be warned that RPM installed via `brew` on OSX *will not work correctly*.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, now I am curious why this would not work :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an explanation. I have not investigated why that is, but if you use rpmdb installed via Brew to rebuild the RPM DB, it will result in an empty DB.

Comment on lines 25 to 26
As of writing this readme, only RHCOS RPM components are supported by the Node Scan.
Therefore, not a full filesystem is needed. The minimal folder/file structure needed for a scan to succeed is:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sentences would say the same when we remove the first two:

Suggested change
As of writing this readme, only RHCOS RPM components are supported by the Node Scan.
Therefore, not a full filesystem is needed. The minimal folder/file structure needed for a scan to succeed is:
The minimal folder/file structure needed for a scan to succeed is:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 27 to 35
- `/etc/redhat-release` with contents denoting an RHCOS system (e.g. `Red Hat Enterprise Linux CoreOS release 4.12`)
- `/usr/share/rpm` containing the RPM database
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need the content_sets json file or the scanner won't be able to find CPEs for scanning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the os-release, I grabbed an RHCOS 4.9 version, which has hardcoded CPEs. Hence I didn't catch that. Thanks!
I have now corrected the os-release to 4.12 and added the content_sets.json to required files.

func main() {
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
fPath := flag.String("fspath", "/host", "Path to the root folder of a filesystem")
fRHCOSrequired := flag.Bool("rhcosRequired", true, "Fails scan if filesystem is not RHCOS. Node Scanning only works on RHCOS.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filesystem is not RHCOS it is actually the OS, not the filesystem. Maybe this could be rephrased?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rephrased it to make it more clear that the path needs to point to a filesystem resulting from running RHCOS

@Maddosaurus Maddosaurus requested review from vikin91 and jvdm April 20, 2023 10:45
@Maddosaurus
Copy link
Contributor Author

After discussions in a Maple Demo Session, I have created a follow-up ticket with more feature ideas for this local node scanner:
ROX-16621.

Copy link
Contributor

@vikin91 vikin91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Il looks good, but I found some minor issues. Giving it green, but please check them

Comment on lines 14 to 17



## Requirements
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown linter would scream here seeing so many empty lines ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Done

## Running the Docker image
The Docker image only requires the path to a target filesystem to be scanned.
As the default for `fspath` is set to `/host`, one can run the image without changes when mounting the target fs to the right path:
`docker run -it -v /path/to/rhcos/fs:/host local-nodescanner:2.29.x-5-g7a3b50ef72-dirty`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that work here?

Suggested change
`docker run -it -v /path/to/rhcos/fs:/host local-nodescanner:2.29.x-5-g7a3b50ef72-dirty`
`docker run -it -v /path/to/rhcos/fs:/host local-nodescanner:$(make tag)`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works, great idea!

The only required flag is the path to a filesystem.
This can be a RO-mount of a running system (e.g. `/host` in the Compliance or Node-Scanner images),
or an unpacked filesystem, e.g. from a Docker image or ISO.
Refer to `testdata/NodeScanning/rhcos4.12-minimal.tar.gz` for an archive containing a minimal example.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping that we get a bit more instructions how to handle that 17MB file - could we maybe get a command where we extract it to some tmp location and then do docker run with mounting it?

Copy link
Contributor Author

@Maddosaurus Maddosaurus Apr 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a complete working example that should get anyone up and running. See L25-L31 in the README

if components.CertifiedRHELComponents.ContentSets != nil {
log.Infof("Number of discovered ContentSets: %v", len(components.CertifiedRHELComponents.ContentSets))
for _, cs := range components.CertifiedRHELComponents.ContentSets {
log.Debugf("CPE: %v", cs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
log.Debugf("CPE: %v", cs)
log.Debugf("Content set: %v", cs)


# Local Node Scanner
.PHONY: local-nodescanner
local-nodescanner:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the idea that this should only be used inside a container? Otherwise, Mac users won't be able to use the binary directly :(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to have maximum flexibility. I have added a UBI 9 container target in this PR to make it runnable anywhere, but we could add more, e.g. UBI 8 or even Fedora latest for a bleeding edge RPM version.
The binary can either be used for tests and/or CI, or as a debug entry point for local debugging. That local debugging would not work on bare metal OSX w.r.t. RPM packages, but still might be useful, or could be done in the container.

flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
fPath := flag.String("fspath", "/host", "Path to the root folder of a filesystem")
fRHCOSrequired := flag.Bool("rhcosRequired", true, "Fails scan if path does not contain a filesystem generated by RHCOS. Node Scanning only works on RHCOS.")
fUncertifiedRHEL := flag.Bool("uncertifiedRhel", false, "Set true for CentOS and false for RHEL.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: uncertifiedRHEL (not sure if that breaks flag name conventions)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would make it harder to use in the command line, assuming it's one of the intended use-cases of the tool? I would use rhcos-required or rchosrequired, honestly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have renamed both flags, see 07e3382

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feeling a bit uneasy about pushing a 17MB file to the repo. How was this generated? Can we just recreate it on-demand? Maybe add some make targets to pull and save the image or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the 17MB is actually the RPM package DB of an RHCOS 4.12 installation.
AFAIK there is no Docker image for RHCOS available, so I generated this by spinning a cluster and then manually pulling the RPM DB plus the additional text/json files that Node Scanning relies on.

I don't know whether we can automate that process or where to get a pre-populated RPM DB.
I remember that we found the download link of an RHCOS live image, though we would need to check whether that comes with a populated DB at rest or if it needs to be started.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RTann I have checked and there is one way I can think of:

  1. Download the raw live rootfs from OpenShift (e.g. here)
  2. Unpack the img with cpio
  3. Unpack the contained squashfs with unsquashfs
  4. Navigate the resulting raw ostree to find the root volume
  5. Copy all required files into our /testdata folder

If I can figure out a reliable way of navigating the ostree layers (all hashes), I could build this as a makefile target and parametrize the script to download a specific RHCOS version.
I know that 17MB is quite a bit, but I don't know whether we can build automation robust enough to not break with the next RHCOS update.
What do you think?

log.Errorf("Encountered error while scanning: %v", err)
}

printResultsToTTY(components)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: You are simply writing to stdout, which might not be a TTY and can be some other file type. I would replace TTY with Stdout or just printResults().

flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
fPath := flag.String("fspath", "/host", "Path to the root folder of a filesystem")
fRHCOSrequired := flag.Bool("rhcosRequired", true, "Fails scan if path does not contain a filesystem generated by RHCOS. Node Scanning only works on RHCOS.")
fUncertifiedRHEL := flag.Bool("uncertifiedRhel", false, "Set true for CentOS and false for RHEL.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would make it harder to use in the command line, assuming it's one of the intended use-cases of the tool? I would use rhcos-required or rchosrequired, honestly.

Makefile Outdated
.PHONY: local-nodescanner
local-nodescanner:
@echo "+ $@"
GOOS=linux GOARCH=amd64 go build -trimpath -o ./tools/bin/local-nodescanner ./tools/local-nodescanner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we build this inside our $(BUILD_IMAGE)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to keep the tool rather isolated, but if you think we should use the build image, I'm happy to move that over into it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By isolation, do you mean minimizing dependencies? If yes, I think it's a very good goal.

If the primary goal of the tool is to aid development and local testing, I think it is OK not to use the build image to build it. Plan to have this evolve into something we can leverage in CI. Using the builder image might be necessary to ensure the built environment doesn't affect the final binary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! I've introduced a new dockerized build that is based on the builder image. Let me know what you think!
See 07e3382

@Maddosaurus Maddosaurus requested review from jvdm and RTann May 10, 2023 14:40
@Maddosaurus Maddosaurus force-pushed the mm/rox-13770-rpmdb branch from 07e3382 to 2e4511d Compare May 15, 2023 08:38
Copy link
Contributor

@jvdm jvdm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious to know what will be the outcome of #1164 (comment), but otherwise, LGTM.

@Maddosaurus Maddosaurus force-pushed the mm/rox-13770-rpmdb branch from db691c6 to 1b12dd0 Compare May 17, 2023 09:21
@Maddosaurus
Copy link
Contributor Author

Maddosaurus commented May 17, 2023

@jvdm / @RTann Regarding a makefile target: I have hit my timebox trying to make this work. It works on Linux, but fails on OSX when trying to use unsquashfs (Fails with File system corrupted - Bad xattr_ids count in super block). You can see the state here: db691c6.
I am merging this as is, but have noted this in a follow-up ticket, ROX-16621.

@Maddosaurus Maddosaurus merged commit 8c54419 into master May 17, 2023
@Maddosaurus Maddosaurus deleted the mm/rox-13770-rpmdb branch May 17, 2023 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants