Add NIST SP 800-171 r3 (CUI) profiles for RHEL 8, 9, and 10 - #15062
Open
ggbecker wants to merge 9 commits into
Open
Add NIST SP 800-171 r3 (CUI) profiles for RHEL 8, 9, and 10#15062ggbecker wants to merge 9 commits into
ggbecker wants to merge 9 commits into
Conversation
ggbecker
requested review from
a team,
Mab879,
jan-cerny,
marcusburghardt,
matusmarhefka and
vojtapolasek
as code owners
August 26, 2026 14:14
jan-cerny
reviewed
Aug 27, 2026
| CONTROL_RE = re.compile(r"([A-Z]{2})-(\d{2})(?:\((\d{2})\))?") | ||
|
|
||
|
|
||
| def strip_tags(line): |
Member
There was a problem hiding this comment.
According to docs/manual/developer/04_style_guide.md, new Python methods and scripts should have type hints.
Member
Author
There was a problem hiding this comment.
I believe this scrip will be a one time thing to this repository and I will remove it from this pull request before we merge it. Sorry for the noise
| @@ -0,0 +1,33 @@ | |||
| --- | |||
| documentation_complete: true | |||
Member
There was a problem hiding this comment.
Are FMF metadata in Contest updated so that Contest tests will test the new profile?
Member
Author
There was a problem hiding this comment.
Yes, this needs to land in sync with: RHSecurityCompliance/contest#664
ggbecker
marked this pull request as draft
August 27, 2026 10:43
ggbecker
force-pushed
the
nist-800-171
branch
4 times, most recently
from
September 2, 2026 17:09
8c28987 to
f83cd70
Compare
A control's 'controls:' list can reference controls from another policy (e.g. 'nist_800_53:ac-2'). Such references resolve against the current product's policies at build time. Derived frameworks like NIST 800-171 reference NIST 800-53 controls that only exist for some products (rhel8/9/10), so building any other product would previously fail with 'policy doesn't exist'. Cross-policy references now degrade gracefully: if the target policy or control isn't loaded for the product being built, the reference contributes no rules instead of aborting the build. Same-policy references are still resolved strictly so typos remain hard errors.
Add a single, product-agnostic controls/nist_800_171.yml in which each 800-171 requirement references (via cross-policy 'controls:' entries) the NIST 800-53 controls it was tailored from. At build time these resolve against the current product's nist_800_53 policy, so each requirement inherits the union of rules mapped to its source controls for that product; products without nist_800_53 resolve to no rules. Register the nist-800-171 reference type so rules selected under a requirement automatically receive that requirement id as a reference, mirroring the STIG stigid auto-reference behavior. The control file is derived from the official NIST SP 800-171 r3 publication.
Redefine the cui profile on each RHEL product to select all rules from the generic nist_800_171 control file (nist_800_171:all). Each 800-171 requirement resolves against the product's own nist_800_53 policy, so the profile pulls in the union of rules mapped to the source 800-53 controls for that product. RHEL 8 and RHEL 9 previously defined cui by inheriting ospp with a couple of variable overrides; that inheritance is dropped in favor of the control-file-driven selection. RHEL 10 gains a cui profile for the first time.
Consolidate the two NIST 800-171 reference types into one. The repo already had a 'cui' reference (Controlled Unclassified Information) pointing at NIST 800-171 Revision 2. Rather than maintain a separate 'nist-800-171' reference type for Revision 3, repurpose 'cui' as the single, current NIST 800-171 reference: - Point the cui reference URL at the r3 publication and remove the separate nist-800-171 reference type from constants. - Set reference_type: cui on the generic nist_800_171 control file (and the generator), so rules selected under a requirement receive that requirement id as a 'cui' reference automatically. The cui profile now maps to the cui reference end to end: cui profile -> nist_800_171 control file -> product nist_800_53 -> rules, with each rule stamped with its r3 requirement id.
Remove the 304 superseded manual r2 'cui:' references from rule files now that r2 is superseded by r3 (see previous commit repurposing the cui reference type). Drop now-empty references blocks left behind by the removal.
These rules are selected by the CIS profile but were missing from the CUI (NIST 800-171 r3) profile, because they were listed under NIST 800-53 controls that 800-171 does not reference, even though each rule's own nist reference points to a control that 800-171 does reference: - selinux_state -> ac-3 (Access Enforcement), alongside its already-mapped companions selinux_not_disabled and selinux_policytype - package_firewalld_installed -> sc-7 (Boundary Protection), alongside service_firewalld_enabled - package_xorg-x11-server-common_removed, xwindows_runlevel_target -> cm-7 (Least Functionality) on RHEL 9, alongside the other package removals This closes the CUI-vs-CIS parity gaps that were genuine 800-53 mapping inconsistencies; the remaining CIS-only rules map to controls outside the 800-171 subset and are correctly excluded.
products/rhel9/profiles/cis.profile (and cis_server_l1/cis_workstation_l1/l2) explicitly unselect file_owner_at_allow, file_ownership_home_directories, and group_unique_name via '!rule_id', with the comment: 'Following rules once had a prodtype incompatible with the rhel9 product'. That exclusion lives at the profile level, not in cis_rhel9.yml's control structure, so these 3 rules were still listed under RHEL9's nist_800_53 ac-3/cm-1/cm-6 controls and got pulled into the cui profile via nist_800_171:all, making cui select 3 rules CIS RHEL9 does not. RHEL8 and RHEL10's CIS profiles have no such exclusion and legitimately select all three rules, so this change is scoped to RHEL9 only, matching RHEL9 CIS's actual current selections whether or not the underlying prodtype-era exclusion is itself still warranted (a separate, existing CIS RHEL9 profile question, out of scope here). Also add a PRODUCT_RULE_EXCEPTIONS mechanism to utils/nist_sync/sync_nist_split.py so a future re-sync of the RHEL9 NIST 800-53 mapping does not silently reintroduce these 3 rules, since load_all_cis_items_from_control_files() reads cis_rhel9.yml's control structure (which still lists them) rather than the profile's '!rule_id' exclusions.
Rebasing onto upstream master picked up f6c42ec ('Add ensure_journald_and_rsyslog_not_active_together to NIST 800-53 AU-2 (rhel9)'), an independent, correctly-scoped mapping fix (au-2 is referenced by 800-171 requirement 03.03.01) that legitimately adds this rule to the cui profile. Update the stability reference to match.
ggbecker
force-pushed
the
nist-800-171
branch
from
September 3, 2026 21:10
f83cd70 to
2f81522
Compare
Rather than pinning extra selections in the cui profile to reach CIS parity, map the settings cui genuinely needs onto the NIST 800-53 controls it derives from, so they are inherited rather than hardcoded: - Add package_xorg-x11-server-Xwayland_removed to cm-7 (least functionality), alongside xwayland_disabled, for RHEL 10. - Add var_selinux_state=enforcing (and var_selinux_policy_name=targeted on RHEL 9) to ac-3, next to selinux_state, the control that brings SELinux into cui. These were previously present only via profile pins; the sc.yml control that also carries them is not pulled by 800-171. The remaining CIS-only hardening (firewalld zone/loopback rules, journald_compress, package_nftables_installed, sysctl_kernel_dmesg_restrict) is dropped: it does not trace to an 800-53 control referenced by 800-171 and cui does not need to match CIS. var_authselect_profile=local is also dropped as a no-op, since its only consumer, enable_authselect, is not part of cui on RHEL 10.
ggbecker
force-pushed
the
nist-800-171
branch
from
September 7, 2026 21:53
23ac77c to
3f95884
Compare
ggbecker
marked this pull request as ready for review
September 7, 2026 23:32
Member
Author
|
This should be now ready for review. |
Mab879
approved these changes
Sep 8, 2026
dodys
approved these changes
Sep 9, 2026
dodys
left a comment
Contributor
There was a problem hiding this comment.
from the ubuntu side, the change looks good, thanks!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR adds NIST SP 800-171 Revision 3 (Controlled Unclassified Information, CUI) profiles for RHEL 8, 9, and 10, built on top of the existing NIST 800-53 control mappings.
What's included
controls/resolution now tolerates foreign control references that don't resolve for a given product, so a product-agnostic control file can reference another policy's controls without hard-failing.controls/nist_800_171.yml— derived from NIST 800-53. Each 800-171 requirement references the correspondingnist_800_53control(s), which resolve at build time against the product's 800-53 policy. Generated viautils/nist_sync/generate_nist_800_171.py.cuireference type consolidation — thecuireference now points at the r3 document (https://nvlpubs.nist.gov/nistpubs/SpecialPublications/800-171r3/NIST.SP.800-171r3.html). Rules selected by the control file are auto-stamped with the requirement id as acuireference. Legacy r2cuireferences were dropped from rule files to avoid conflicting/duplicate references.800-53 mapping refinements
The CUI profile derives its selection entirely from the NIST 800-53 controls (
- nist_800_171:all), with no hardcoded profile selections. To make that derivation correct and complete, the underlying mappings were refined:package_xorg-x11-server-Xwayland_removedis mapped under cm-7 (least functionality), alongsidexwayland_disabled;var_selinux_state=enforcing(andvar_selinux_policy_name=targetedon RHEL 9) is mapped under ac-3, next toselinux_state, the control that actually brings SELinux into the profile. These were previously reachable only via profile-level pins.Testing
Known limitations
On the Image Mode (bootc) Day-2 upgrade path, two rules fail on RHEL 9 only under
/hardening/container/old-new/cuiand are waived in the contest test suite:aide_build_database— onbootc switchthe guest/varis not re-provisioned, so the AIDE database built into the new image never lands on disk. Transitional: resolves once a released CUI baseline already includes the rule.ensure_pam_wheel_group_empty— the su/wheel group is added imperatively to/etc/groupat build time, but the/etc3-way merge onbootc switchcan drop the entry (confirmed flaky).Only RHEL 9 is affected: RHEL 8 does not run the Image Mode test, and on RHEL 10 the released (old) datastream has no CUI profile so the old-new comparison is skipped.