FAPOLICYD.RULES:(5)

πŸ“¦
version 2.0.1 Β· 4 versions
, in centos-stream 10, centos-stream 9, fedora 43, fedora 44, ubuntu stonking

πŸ’– NAME πŸ’–

ok so bestie, welcome to fapolicyd.rules β€” that's section 5 of the manual, the file-format girlies. ✨

  • compiled.rules β€” the compiled fapolicyd rules that decide who gets access rights around here πŸ‘‘
  • fapolicyd.rules β€” the deprecated version of those rules, also for deciding access rights πŸ’€

🌸 DESCRIPTION 🌸

compiled.rules is a file that gets compiled by fagenrules, and inside it lives all the rules that fapolicyd uses to make its little decisions about access rights. no cap, this is the whole personality of the daemon. 🫢

the rules follow a super simple format, like:

decision perm subject : object

they get evaluated from top to bottom, and the first rule to match is the one that gets used for the access control decision. periodt. πŸ’…

the colon is mandatory to separate subject and object, bestie β€” because they literally share keywords. do not skip the colon. πŸ™ˆ

✨ Decision ✨

the decision is one of: allow, deny, allow_audit, deny_audit, allow_syslog, deny_syslog, allow_log, or deny_log.

if the rule triggers, that's the access decision fapolicyd tells the kernel. πŸ“£

  • if it's one of the audit girlies, the decision triggers a FANOTIFY audit event with all the relevant info. and listen β€” you must have at least one audit rule loaded to generate an audit event. like, at all. 😭
  • if it's one of the syslog girlies, the decision triggers writing an event into syslog. πŸ“
  • if it's one of the log girlies, it does both β€” an audit event and a syslog event. overachiever behavior honestly. πŸ’…

and regardless of whatever notification is happening: any rule with a deny in the keyword denies access, and any with an allow in the keyword allows access. that part is not negotiable. πŸŽ€


πŸ¦‹ Perm πŸ¦‹

perm describes what kind of permission is being asked for. it's either open, execute, or any.

if none are given, then open is assumed. so if you don't say anything, it's giving open energy. 😌


πŸ’œ Subject πŸ’œ

the subject is the process that is out here performing actions on system resources. the subject fields are written in name=value format, and there can be one or more of them. each field gets and'ed with the others to decide if a rule triggers.

the name values can be any of the following:

  • all β€” matches against any subject. when you use it, it must be the only subject in the rule. no friends allowed. πŸ™…
  • auid β€” this is the login uid that the audit system assigns users when they log in. when the audit login uid is unset (which is lowkey common for system daemons and kernel threads), the kernel value is -1 and fapolicyd reports it as 4294967295. the auid attribute is unsigned, so rules must use auid=4294967295; auid=-1 is invalid. other than that, the given value may be numeric or the account name. ⭐
  • uid β€” the user id that the program is running under. value can be numeric or the account name.
  • gid β€” the group id that the program is running under. value can be numeric or the group name.
  • sessionid β€” the numeric session id that the audit system assigns to users when they log in. daemons have a value of -1. 🫠
  • pid β€” the numeric process id that a program has.
  • ppid β€” the numeric process id of the program's parent. quick lore drop: programs that are orphaned or started directly from systemd have a ppid value of 1, and kernel threads have a ppid value of 2. ✨
  • trust β€” a boolean describing whether it's required for the subject to be in the trust database or not. 1 means it's required, 0 means it's not. trust checking is extended by the integrity setting in fapolicyd.conf. when trust is used on the subject, it could be a daemon. and here's the tea 🍡 β€” if that daemon gets updated on disk, the trustdb gets updated to the new SHA256 hash. if the integrity setting is not none, the running daemon is not likely to be trusted unless it gets restarted. the default rules are not written in a way that this would happen, but this needs to be highlighted because it may not be obvious when writing a new rule. πŸ‘€
  • comm β€” the shortened command name. when an interpreter starts a program, it usually renames the program to the script rather than the interpreter. sneaky but that's just how it is. 🧚
  • exe β€” the full path to the executable. values are exact paths unless they use the explicit glob: pattern prefix. see PATH GLOBBING below. you may also use the special keyword untrusted to match on the subject not being listed in the rpm database. πŸ’…
  • dir β€” if you wanna match a directory, use this by giving the full path to the directory. it's recommended to end with the / to make sure it matches a directory. there are 3 keywords that dir supports: execdirs, systemdirs, untrusted.
    • execdirs β€” matches against the following list of directories:
/usr/       /bin/ /sbin/ /lib/ /lib64/
            /usr/libexec/
  • systemdirs β€” matches against the same list as execdirs but also includes /etc/. πŸ’–
  • untrusted β€” looks up the current executable's full path in the rpm database to see if the executable is known to the system. the rule will trigger if the file in question is not in the trust database. this option is deprecated in favor of using object trust with execute permission when writing rules. rules that use dir=untrusted emit a deprecation warning when parsed, and this compatibility option will be removed in a future release. for compatibility with older policies, a subject dir=untrusted rule paired with a path object at the same rule field position will not match a trusted object. this subject/object cross-reference is obsolete β€” use explicit object trust rules for new policy. fr fr, don't build new stuff on this. 😭
  • ftype β€” this option takes the mime type of a file as an argument. mime is determined based on magic patterns files. fapolicyd uses two precompiled magic files: /usr/share/fapolicyd/fapolicyd-magic.mgc and /usr/share/misc/magic.mgc. to compile a magic file, run the command:
file -C -m <magic file>

and if you wanna check the mime type of a file while writing rules, run this:

fapolicyd-cli --ftype /path-to-file

so handy, honestly. πŸŽ€

  • pattern β€” there are various ways an attacker may try to execute code that may reveal itself in the pattern of file accesses made during program startup. this rule can take one of several options depending on which access patterns you wish to block. fapolicyd is able to detect these different access patterns and provide the access decision as soon as it identifies the pattern. the pattern type can be any of:
    • normal β€” matches against any ELF program that is dynamically linked.
    • ld_so β€” matches against access patterns that indicate the program is being started directly by the runtime linker.
    • ld_preload β€” matches against access patterns that indicate the program is being started with either LD_PRELOAD or LD_AUDIT present in the environment. note that even without this rule, you have protection against LD_PRELOAD of unknown binaries when the rules are written such that trust is used to determine if a library should be opened. in that case, the preloaded library would be denied but the application will still execute. this rule makes it so that even trusted libraries can be denied and the application will not execute. that's the difference, bestie. πŸ’…
    • static β€” matches against ELF files that are statically linked.

🌈 Object 🌈

the object is the file that the subject is interacting with. the object fields are written in name=value format, and there can be one or more of them. each field is and'ed with the others to decide if a rule triggers.

the name values can be any of the following:

  • all β€” matches against any object. when used, this must be the only object in the rule. again β€” no plus ones. πŸ™…
  • path β€” the full path to the file that will be accessed. values are exact paths unless they use the explicit glob: pattern prefix. see PATH GLOBBING below. you may also use the special keyword untrusted to match on the object not being listed in the rpm database. ⭐
  • dir β€” if you wish to match on access to any file in a directory, use this by giving the full path to the directory. it's recommended to end with the / to ensure it matches a directory. there are 3 keywords that dir supports: execdirs, systemdirs, untrusted. see the dir option under Subject for an explanation of these keywords. πŸ‘‘

ok that's part 1 bestie, and there's more coming β€” don't go anywhere, we're not done yet πŸ’–

  • device β€” matches against the device that the file being accessed actually lives on. To use it, start with /dev/ and then add the target device name. πŸ’…
  • ftype β€” matches against the mime type of the file being accessed. Go look at ftype under Subject for the whole how-do-I-even-figure-out-the-mime-type situation. πŸ¦‹
  • trust β€” this one is a boolean, and it's asking: does this object have to be in the trust database or not? 1 = yes it's required, 0 = nope it's not. And trust checking gets extended by the integrity setting over in fapolicyd.conf, iykyk.
  • FILE_HASH β€” (the legacy keyword SHA256HASH is still accepted, but πŸ’€) β€” this matches against the hash of the file being accessed. The accepted hash length follows the trust source, so RPM entries built with SHA512 store SHA512 digests while other sources may continue to provide SHA256. The hash in the rules should be all lowercase letters and do NOT start with 0x. Lowercase is literally the default output of sha256sum and sha512sum. The SHA256HASH keyword remains for compatibility but is deprecated; prefer FILE_HASH for new rules. periodt.

✨ QUOTED VALUES ✨

Okay so, rule fields are separated by spaces. That's the whole thing. So when a string value has a space in it, you wrap the complete value in double quotes. The surrounding quotes are syntax β€” they are not part of the value used for matching. And this applies consistently to subject exe, subject and object dir, and object path:

allow perm=execute all : dir="/tmp/first last/"
allow perm=execute exe="/tmp/first last/tool" : all
allow perm=execute all : path="/tmp/first last/tool"

Double quotes can also be used in named string-set definitions and in each comma-separated string value. Inside a quoted value, a backslash before a double quote or before another backslash represents that character. Other backslashes are preserved, so glob patterns retain their normal backslash behavior. 🌸

Percent escapes are not interpreted in rules, bestie. For example, dir="/tmp/first%20last/" matches a literal percent sign followed by 20, not a space. A backslash before a space and shell-style single quotes are not rule-file quoting forms; double quotes are the only supported way to include a literal space in a rule value. πŸ™ˆ


🌈 PATH GLOBBING 🌈

Rule values use exact matching by default. If you want pattern matching, prefix the value with glob:. This explicit marker is what distinguishes exact paths from patterns, so path names containing literal *, ?, or [ characters keep their historical exact-match meaning. For example, path=/tmp/name[1] matches only that literal path, while path=glob:/tmp/name[1] uses [1] as a bracket expression and matches /tmp/name1. ✨

The glob: prefix is supported only by the subject exe and object path attributes, and the pattern after the prefix must be an absolute path. The dir attribute remains a literal directory prefix β€” use it instead of a glob when a rule applies to a complete subtree rooted at a fixed directory. An unmarked dir value containing metacharacters remains literal. A glob: value assigned to dir, or to any attribute other than exe and path, is a rule error, including when the value comes from a named set. Use subject exe or object path when glob matching is required.

The pattern following glob: uses fnmatch(3) syntax. A * matches zero or more characters, ? matches one character, and bracket expressions such as [0-9] match one character from the expression. Matching is case-sensitive and must cover the complete path. Wildcards do not match a slash, so each variable path component must be written explicitly. A wildcard does not match a leading period in a path component unless the period appears explicitly in the pattern. Backslash quotes a metacharacter. Recursive ** globstar matching is not supported. πŸ’€

When a glob value contains a space, quote the complete value, including the glob: prefix. For example:

allow perm=execute all : path="glob:/tmp/first last/*"
allow perm=execute exe="glob:/tmp/first last/*" : all

The quotes are removed before glob validation and matching. The dir attribute remains a literal prefix and does not accept a quoted or unquoted glob: value. πŸŽ€

Bracket ranges such as [a-z] and named character classes such as [[:alpha:]] use the locale active in the daemon. Avoid ranges and named classes when policy must produce identical matches in every locale; use exact alternatives or enumerate the intended characters instead. 🌍

For example, glob:/home/*/bin/tool matches /home/alice/bin/tool but not /home/alice/project/bin/tool or /home/.admin/bin/tool. Each pattern in a comma-separated attribute value or named string set needs its own glob: prefix. Exact paths and patterns may be mixed in the same set. Rule order is unchanged: rules are evaluated from top to bottom, and the first complete rule match determines the decision. πŸ‘‘

The concrete object path is obtained from the event file descriptor through /proc/self/fd/, and the subject exe path is obtained through /proc/PID/exe. Symbolic links have therefore already been followed, so a rule normally matches the reported target path rather than the symbolic link name used by the application. Hard links and mount aliases can expose different concrete paths for the same inode; policy that must cover those aliases needs an exact value or pattern for each reported path.

Globbing selects paths; it does not expand the pattern, alter the path reported in logs, or make matching files trusted. Trust database entries continue to use concrete paths. An allow rule for a user-writable location should normally pair an object path glob with object trust=1 or an explicit FILE_HASH constraint. A subject exe glob in a user-writable location should normally require subject trust=1. Otherwise, any file whose name fits the pattern can receive the allow decision. Prefer exact paths, named sets of exact paths, or dir when they express the policy, because exact and prefix matching are less expensive than glob evaluation. πŸ’Έ

deny_audit perm=execute all : path=glob:/opt/vendor/product-*/bin/tool
allow perm=execute all : path=glob:/home/*/bin/tool trust=1

🧚 SETS 🧚

A set is a named group of values of the same type. Fapolicyd internally distinguishes between INT and STRING set types. You can define your own set and use it as a value for a specific rule attribute. The definition is in key=value syntax and starts with a set name. The set name has to start with % and the rest is alphanumeric or _. The value is a comma separated list. The set type is inherited from the first item in the list. If that can be turned into a number then the whole list is expected to carry numbers. You can use these sets as a value for subject and object attributes. It is also possible to use a plain list as an attribute value without previous definition. The assigned set has to match the attribute type. It is not possible to set groups for TRUST and PATTERN attributes. πŸ™…β€β™€οΈ

πŸŽ€ SETS EXAMPLES πŸŽ€

# definition
# string set
%python=/usr/bin/python2.7,/usr/bin/python3.6
allow exe=%python : all trust=1
#
# definition
# number set
%uuids=0,1000
allow uid=%uuids : all

🌸 LANGUAGE SET EXTENSIONS 🌸

The installed %languages set identifies MIME types that policy treats as language or interpreter content. A package may add a MIME type without modifying the distribution-owned definition by placing a naturally ordered macro fragment after 10-languages.rules and before policy rules. For example, 11-vendor-languages.rules can contain:

%languages+=application/x-vendor-script

The += form is available only for %languages. It requires the base set to have been defined already, does not replace it, and treats an existing MIME type as a no-op. Run fagenrules after adding or removing a fragment. The added value must also be a MIME type emitted by fapolicyd's file type detector or the system magic database; adding it to the set alone does not add file type detection. 🫢


πŸ’œ NOTES πŸ’œ

When writing rules, you should keep them focused to one goal and store them in one file. These rule files are kept in the /etc/fapolicyd/rules.d directory. During daemon startup, fagenrules will run and compile all these component files into one master file, compiled.rules. See the fagenrules man page for more information. πŸ“š

When you are writing a rule for the execute permission, remember that the file to be executed is an object. For example, you type ssh into the shell. The shell calls execve on /usr/bin/ssh. At that instant in time, ssh is the object that bash is working on. However, if you are blocking execution from a specific program, then you would normally state the program on the subject side and use all for the object side. 🐚

If you are writing rules that use patterns, just select any as the permission to be clear that this applies to anything. In reality, pattern matching ignores the permission, but the suggestion is for documentation purposes. ✍️

Some interpreters do not immediately read all lines of input. Rather, they read content as needed until they get to end of file. This means that if they do stuff like networking or sleeping or anything that takes time, someone with the privileges to modify the file can add to it after the file's integrity has been checked. This is not unique to fapolicyd, it's simply how things work. Make sure that trusted file permissions are not excessive so that no unexpected file content modifications can occur. ⚠️

Bestie. If a trusted file is world-writable, you have bigger problems than this man page. Lock those permissions down. πŸ”’

πŸ”₯ EXAMPLES πŸ”₯

The following rules illustrate the rule syntax.

deny_audit perm=open exe=/usr/bin/wget : dir=/tmp
allow perm=open exe=/usr/bin/python3.7 : ftype=text/x-python trust=1
deny_audit perm=any pattern ld_so : all
deny perm=any all : all

πŸ‘‘ SEE ALSO πŸ‘‘


🌟 AUTHOR 🌟

Steve Grubb


And that's the whole page, bestie!! πŸ’– This one shipped from Red Hat, dated June 2022, and it's FAPOLICYD.RULES:(5) β€” go forth and write rules that slay, not rules that get you owned. πŸŽ€βœ¨πŸ¦‹

πŸ“¦ where this page comes from