Skip to content
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

add security allow list lints #439

Closed
wants to merge 20 commits into from

Conversation

maltek
Copy link

@maltek maltek commented Jun 24, 2020

This took a lot longer than I'd hoped. This PR adds the allow lists from openSUSE/rpmlint-checks developed in parallel to this rewrite.

I'm not sure if the links to the openSUSE wiki in the description texts are acceptable here. If not, we'll have to find some way to change these texts in openSUSE. We're doing something pretty rude here (telling a packager they can't do what they want to) - the least we have to do is tell the packager the exact steps they can take to get the allowed lists to be adapted.

@lgtm-com
Copy link

lgtm-com bot commented Jun 24, 2020

This pull request introduces 3 alerts when merging 94f99cf into ad7aedd - view on LGTM.com

new alerts:

  • 2 for Variable defined multiple times
  • 1 for Unreachable code

@scarabeusiv
Copy link
Contributor

Seems there are bunch of flake8 and lgtm complaints.

Generally okay. Only thing we will have to figure out how to override those descriptions pointing to suse, so it could be reused by the other distributions too. (alternatively we would have to keep it in opensuse branch but it seems okay for master).

@maltek
Copy link
Author

maltek commented Jun 25, 2020

Yes, the original author's C++ background really showed through in that code. I've added a commit that should address your comments.

@maltek
Copy link
Author

maltek commented Jun 25, 2020

Only thing we will have to figure out how to override those descriptions pointing to suse, so it could be reused by the other distributions too.

So, how about:

I adapt Filter.get_description to append the text of reason + "-suffix" if such a key exists. That'd allow us to drop in additional TOML files with openSUSE-specific information, without having to merge changes in the generic text.

@scarabeusiv
Copy link
Contributor

Only thing we will have to figure out how to override those descriptions pointing to suse, so it could be reused by the other distributions too.

So, how about:

I adapt Filter.get_description to append the text of reason + "-suffix" if such a key exists. That'd allow us to drop in additional TOML files with openSUSE-specific information, without having to merge changes in the generic text.

We have it already in progress I think @kstreitova #432 it just needs a bit of polishing.

Also whats weird is that one test failing on filtering in travis right now, it should not be failing :)

#############################################################################
# Author : Matthias Gerstner
# Purpose : reusable code for dealing with security allow lists
#############################################################################
Copy link
Contributor

Choose a reason for hiding this comment

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

The whole comments are moot, just convert them to module docstring if it makes sense.

@@ -0,0 +1,15 @@
from .Allowlisting import AbstractSimpleAllowlistCheck
Copy link
Contributor

Choose a reason for hiding this comment

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

The imports should be by default absolute unless the path grows crazy complex.

'texlive',
'texlive.texlive',
'otrs', # bsc#1118049
)
Copy link
Contributor

Choose a reason for hiding this comment

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

I bet this should be in a config file as a toml list, also any insight where this default came from?

self.output.add_info('E', pkg, 'permissions-fscaps', f'{f} has fscaps "{pkgfile.filecaps}"')

mode = pkgfile.mode
owner = pkgfile.user + ':' + pkgfile.group
Copy link
Contributor

Choose a reason for hiding this comment

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

f'{pkgfile.user}:{pkgfile.group}' ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer to stay with pkgfile.user + ':' + pkgfile.group.

if need_set_permissions:
if 'permissions' not in (x[0] for x in pkg.prereq):
self.output.add_info('E', pkg, 'permissions-missing-requires',
"missing 'permissions' in PreReq")
Copy link
Contributor

Choose a reason for hiding this comment

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

This sounds wrong, PreReq is obsolete var, now we use Requires(phase): to state the deps.

if found_suseconfig:
# TODO: nothing in tumbleweed uses this anymore. can probable be dropped.
self.output.add_info('I', pkg, 'permissions-suseconfig-obsolete',
'%run_permissions is obsolete')
Copy link
Contributor

Choose a reason for hiding this comment

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

I would probably drop this one. Also it is auto-removed by spec-cleaner too.



permissions-missing-requires="""
Please add 'PreReq: permissions'
Copy link
Contributor

Choose a reason for hiding this comment

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

PreReq is obsolete variable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should be Requires used here?

Copy link
Contributor

Choose a reason for hiding this comment

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

No Requires(phase): one.

Copy link
Author

Choose a reason for hiding this comment

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

I think it should be a plain Requires, because the verifyscript also has that dependency.

@Conan-Kudo
Copy link
Member

Conan-Kudo commented Jun 25, 2020

I'm going to do a more in-depth review of this a bit later, but I've got some simple feedback:

  • I'm okay with the idea of having this capability in rpmlint master
  • I'm not okay with the suse- prefix, or openSUSE wiki/bug references leaking in
  • I'd like for someone from Fedora to also take a look at this (perhaps @spotrh or @sidhpurwala-huzaifa)
  • I don't want references to specific packages anywhere (e.g. permissions, etc.)
  • These need to default to off in rpmlint

@scarabeusiv scarabeusiv added this to the 2.0 milestone Jul 17, 2020
@marxin
Copy link
Contributor

marxin commented Oct 1, 2020

@maltek Can you please provide links to spec files from which you created the .rpm files for testing?

@maltek
Copy link
Author

maltek commented Oct 1, 2020

@maltek Can you please provide links to spec files from which you created the .rpm files for testing?

Should be these:
https://gist.github.com/maltek/a1da0cd5d3460c361c4c9149e4dd6f54

@scarabeusiv scarabeusiv modified the milestones: 2.0, 2.1 Oct 1, 2020
@maltek
Copy link
Author

maltek commented Oct 7, 2020

I've rebased the PR and updated it based on the comments and #469.

(From #469:)

Apparently, upstream reworked the patches significantly

Well, kind of. There's a few of new checks there, but they are much less important than the ones here for openSUSE. Waiting another few months won't help with the fork situation.

@marxin
Copy link
Contributor

marxin commented Oct 7, 2020

I've rebased the PR and updated it based on the comments and #469.

Thank you for the port!
One comment I have is if you can start using TOML configuration files for the added checks?

@maltek
Copy link
Author

maltek commented Oct 8, 2020

One comment I have is if you can start using TOML configuration files for the added checks?

Done.

@@ -0,0 +1,40 @@
import os
import os.path
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe use of pathlib will be better option

Copy link
Author

Choose a reason for hiding this comment

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

I don't personally see how it'd provide a benefit over a single call to os.path.join.

But I don't care much either way, so I'll use that if that's what you prefer.

@Conan-Kudo
Copy link
Member

@dcantrell Could you please take a look over this?

@dcantrell
Copy link

Looking over this PR now. I have some initial thoughts, but will have more to post on Monday. Currently on a long weekend and only have my phone available. Thanks.

@dcantrell
Copy link

dcantrell commented Oct 19, 2020

Various comments:

  • Allow lists contain a lot of vendor-specifics. As an example, allowlisting.py has _verify_bug_nr() and checks that the value can split on '#' and the first part is either 'bsc', 'boo', or 'bnc'. I don't think this information is necessary for a tool like rpmlint.

  • The files described at https://github.com/openSUSE/rpmlint-security-whitelistings/blob/master/README.md Contain a lot of unnecessary information. There's the bug number reference again as well as the package name that a file lives in. Since the ultimate test is to check what the installed file looks like, you don't really care what package delivers that file.

  • In that same README, the metadata based whitelist is essentially storing the fields from stat(2), but not in a great way. There's the "dev" key which contains both the major and minor number, requiring the code to always have to split that value. Just have two fields for major and minor. Likewise with the "owner" key containing "root:root". Just have separate user and group keys. The format of this file also feels clumsy. To make it more widely maintainable, it might be easier to just allow "ls -l" listings for entries and parse those lines.

  • At least CheckDBUSServices.py, CronJobsCheck.py, PolkitDefaultPrivsAllowlist.py, and PolkitRulesAllowedCheck.py contain hardcoded paths of where to find files in question. These sorts of things should go in runtime configuration files to avoid having to patch the code when paths change and to allow variations among vendors.

  • SUIDPermissionsCheck.py contains SLES-specifics in the code. It also looks for usage of /usr/bin/chkstat, which is not a program I am familiar with.

  • The PR contains built RPMs for the test suite. I recommend using the rpmfluff Python module and generating test RPMs in the test suite.

Overall this PR feels very SuSE-specific and not general enough to apply to a wide range of RPM-based distributions. The code itself contains a lot of vendor specifics and hardcoded paths and things unique to SuSE releases.

I am also not a fan of the data format described in the README. For https://github.com/rpminspect/rpminspect, I have fileinfo lists per product release defined in the vendor's rpminspect-data package. Here's what the rawhide one looks like:

https://github.com/rpminspect/rpminspect-data-fedora/blob/master/fileinfo/rawhide

Separate from that are the capabilities lists per product release:

https://github.com/rpminspect/rpminspect-data-fedora/blob/master/capabilities/rawhide

I have tried to keep these files minimal so they are easily maintained by all contributors. The files in question can move among packages in the distributions, but we can still enforce the same settings on the file that is ultimately put on the filesystem.

@marxin
Copy link
Contributor

marxin commented Oct 23, 2020

Various comments:

Thanks for them, they are useful.

  • Allow lists contain a lot of vendor-specifics. As an example, allowlisting.py has _verify_bug_nr() and checks that the value can split on '#' and the first part is either 'bsc', 'boo', or 'bnc'. I don't think this information is necessary for a tool like rpmlint.

I agree. That format should be verified in the project where you have the configuration file.

It seems to me logical to have it grouped on a package basis and having an optional comment (or a bug link) seems fine to.
What about the following TOML structure:

[mypackage]

bug_url = 'https://bugzilla.opensuse.org/show_bug.cgi?id=1177680'
comment = 'Chroot duplicates of some uncritical character devices. urandom was historically packaged non-world-writable.'

[mypackage.digests."/tmp/a"]

algorithm = 'sha256'
hash = 'd536dc68e198189149048a907ea6d56a7ee9fc732ae8fec5a4072ad06640e359'

[mypackage.digests."/tmp/b"]

algorithm = 'sha256'
hash = 'asdfasdfa'
  • In that same README, the metadata based whitelist is essentially storing the fields from stat(2), but not in a great way. There's the "dev" key which contains both the major and minor number, requiring the code to always have to split that value. Just have two fields for major and minor. Likewise with the "owner" key containing "root:root". Just have separate user and group keys. The format of this file also feels clumsy. To make it more widely maintainable, it might be easier to just allow "ls -l" listings for entries and parse those lines.

I like the idea of matching that against the ls -l format. It's simple.

  • At least CheckDBUSServices.py, CronJobsCheck.py, PolkitDefaultPrivsAllowlist.py, and PolkitRulesAllowedCheck.py contain hardcoded paths of where to find files in question. These sorts of things should go in runtime configuration files to avoid having to patch the code when paths change and to allow variations among vendors.

I guess a TOML configuration for that can be added.

  • SUIDPermissionsCheck.py contains SLES-specifics in the code. It also looks for usage of /usr/bin/chkstat, which is not a program I am familiar with.

I guess the paths that are searched can be also factored out into a TOML configuration?

  • The PR contains built RPMs for the test suite. I recommend using the rpmfluff Python module and generating test RPMs in the test suite.

This is a generic problem, so far we've added a bunch of RPM files that are built from:
https://build.opensuse.org/project/show/devel:openSUSE:Factory:rpmlint:tests

I can't find any documentation for the rpmfluff project. Is it capable of building an RPM from a SPEC file?

Overall this PR feels very SuSE-specific and not general enough to apply to a wide range of RPM-based distributions. The code itself contains a lot of vendor specifics and hardcoded paths and things unique to SuSE releases.

I am also not a fan of the data format described in the README. For https://github.com/rpminspect/rpminspect, I have fileinfo lists per product release defined in the vendor's rpminspect-data package. Here's what the rawhide one looks like:

https://github.com/rpminspect/rpminspect-data-fedora/blob/master/fileinfo/rawhide

Separate from that are the capabilities lists per product release:

https://github.com/rpminspect/rpminspect-data-fedora/blob/master/capabilities/rawhide

I have tried to keep these files minimal so they are easily maintained by all contributors. The files in question can move among packages in the distributions, but we can still enforce the same settings on the file that is ultimately put on the filesystem.

@dcantrell
Copy link

It seems to me logical to have it grouped on a package basis and having an optional comment (or a bug link) seems fine to.
What about the following TOML structure:

[mypackage]

bug_url = 'https://bugzilla.opensuse.org/show_bug.cgi?id=1177680'
comment = 'Chroot duplicates of some uncritical character devices. urandom was historically packaged non-world-writable.'

[mypackage.digests."/tmp/a"]

algorithm = 'sha256'
hash = 'd536dc68e198189149048a907ea6d56a7ee9fc732ae8fec5a4072ad06640e359'

[mypackage.digests."/tmp/b"]

algorithm = 'sha256'
hash = 'asdfasdfa'

This improves on it. I would suggest bug_url be a list because it is possible that you may want to reference both the distribution bug as well as one or more upstream bugs (or even a CVE).

algorithm feels unnecessary as that can be determined by the digest hash, but only if you require the full digest hash. My preference is towards requiring unabbreviated digest hashes and doing away with 'algorithm' and just inferring that in the code.

  • At least CheckDBUSServices.py, CronJobsCheck.py, PolkitDefaultPrivsAllowlist.py, and PolkitRulesAllowedCheck.py contain hardcoded paths of where to find files in question. These sorts of things should go in runtime configuration files to avoid having to patch the code when paths change and to allow variations among vendors.

I guess a TOML configuration for that can be added.

Anything that gets those sorts of lists out of code and in to runtime-maintainable files.

  • SUIDPermissionsCheck.py contains SLES-specifics in the code. It also looks for usage of /usr/bin/chkstat, which is not a program I am familiar with.

I guess the paths that are searched can be also factored out into a TOML configuration?

They should be, yes.

  • The PR contains built RPMs for the test suite. I recommend using the rpmfluff Python module and generating test RPMs in the test suite.

This is a generic problem, so far we've added a bunch of RPM files that are built from:
https://build.opensuse.org/project/show/devel:openSUSE:Factory:rpmlint:tests

I can't find any documentation for the rpmfluff project. Is it capable of building an RPM from a SPEC file?

rpmfluff began life inside the Red Hat rpmdiff project (internal tool) but is now a Python module project.

https://pypi.org/project/rpmfluff/

While there is value in running tests against known RPMs files, being able to construct them directly from the test suite is also useful. Especially for external contributors. rpmfluff lets you create RPM files for testing purposes for tools like this. It does not take a spec file.

rpmfluff is not perfect, but it is a timesaver for test suites. If you point me at an example spec file, I can show you how I would synthesize that with rpmfluff.

@marxin
Copy link
Contributor

marxin commented Jan 19, 2021

Based on the internal discussion with openSUSE security team (btw. @maltek left the company), we will add the 2 specific checks into opensuse branch. Let's follow #594 and #600.

@marxin marxin closed this Jan 19, 2021
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.

6 participants