Chapter 12. Blocking and allowing applications by using fapolicyd Setting and enforcing a policy that either allows or denies application execution based on a rule set efficiently prevents the execution of unknown and potentially malicious software. 12.1. Introduction to fapolicyd The fapolicyd software framework controls the execution of applications based on a user-defined policy. This is one of the most efficient ways to prevent running untrusted and possibly malicious applications on the system. The fapolicyd framework provides the following components: fapolicyd service fapolicyd command-line utilities fapolicyd RPM plugin fapolicyd rule language fagenrules script The administrator can define the allow and deny execution rules for any application with the possibility of auditing based on a path, hash, MIME type, or trust. The fapolicyd framework introduces the concept of trust. An application is trusted when it is properly installed by the system package manager, and therefore it is registered in the system RPM database. The fapolicyd daemon uses the RPM database as a list of trusted binaries and scripts. The fapolicyd RPM plugin registers any system update that is handled by either the DNF package manager or the RPM Package Manager. The plugin notifies the fapolicyd daemon about changes in this database. Other ways of adding applications require the creation of custom rules and restarting the fapolicyd service. The fapolicyd service configuration is located in the /etc/fapolicyd/ directory with the following structure: The /etc/fapolicyd/fapolicyd.trust file contains a list of trusted files. You can also use multiple trust files in the /etc/fapolicyd/trust.d/ directory. The /etc/fapolicyd/rules.d/ directory for files containing allow and deny execution rules. The fagenrules script merges these component rules files to the /etc/fapolicyd/compiled.rules file. The fapolicyd.conf file contains the daemon's configuration options. This file is useful primarily for performance-tuning purposes. Rules in /etc/fapolicyd/rules.d/ are organized in several files, each representing a different policy goal. The numbers at the beginning of the corresponding file names determine the order in /etc/fapolicyd/compiled.rules : 10 Language rules. 20 Dracut-related Rules. 21 rules for updaters. 30 Patterns. 40 ELF rules. 41 Shared objects rules. 42 Trusted ELF rules. 70 Trusted language rules. 72 Shell rules. 90 Deny execute rules. 95 Allow open rules. You can use one of the following ways for fapolicyd integrity checking: File-size checking Comparing SHA-256 hashes Integrity Measurement Architecture (IMA) subsystem By default, fapolicyd does no integrity checking. Integrity checking based on the file size is fast, but an attacker can replace the content of the file and preserve its byte size. Computing and checking SHA-256 checksums is more secure, but it affects the performance of the system. The integrity = ima option in fapolicyd.conf requires support for files extended attributes (also known as xattr ) on all file systems containing executable files. Additional resources fapolicyd(8) , fapolicyd.rules(5) , fapolicyd.conf(5) , fapolicyd.trust(13) , fagenrules(8) , and fapolicyd-cli(1) man pages. The Enhancing security with the kernel integrity subsystem chapter in the Managing, monitoring, and updating the kernel document. The documentation installed with the fapolicyd package in the /usr/share/doc/fapolicyd/ directory and the /usr/share/fapolicyd/sample-rules/README-rules file. 12.2. Deploying fapolicyd When deploying the fapolicyd application allowlisting framework, you can either try your configuration in permissive mode first or directly enable the service in the default configuration. Procedure Install the fapolicyd package: Optional: To try your configuration first, change mode to permissive. Open the /etc/fapolicyd/fapolicyd.conf file in a text editor of your choice, for example: Change the value of the permissive option from 0 to 1 , save the file, and exit the editor: Alternatively, you can debug your configuration by using the fapolicyd --debug-deny --permissive command before you start the service. See the Troubleshooting problems related to fapolicyd section for more information. Enable and start the fapolicyd service: If you enabled permissive mode through /etc/fapolicyd/fapolicyd.conf : Set the Audit service for recording fapolicyd events: Use your applications. Check Audit logs for fanotify denials, for example: When debugged, disable permissive mode by changing the corresponding value back to permissive = 0 , and restart the service: Verification Verify that the fapolicyd service is running correctly: Log in as a user without root privileges, and check that fapolicyd is working, for example: 12.3. Marking files as trusted using an additional source of trust The fapolicyd framework trusts files contained in the RPM database. You can mark additional files as trusted by adding the corresponding entries to the /etc/fapolicyd/fapolicyd.trust plain-text file or the /etc/fapolicyd/trust.d/ directory, which supports separating a list of trusted files into more files. You can modify fapolicyd.trust or the files in /etc/fapolicyd/trust.d either directly using a text editor or through fapolicyd-cli commands. Note Marking files as trusted using fapolicyd.trust or trust.d/ is better than writing custom fapolicyd rules due to performance reasons. Prerequisites The fapolicyd framework is deployed on your system. Procedure Copy your custom binary to the required directory, for example: Mark your custom binary as trusted, and store the corresponding entry to the myapp file in /etc/fapolicyd/trust.d/ : If you skip the --trust-file option, then the command adds the corresponding line to /etc/fapolicyd/fapolicyd.trust . To mark all existing files in a directory as trusted, provide the directory path as an argument of the --file option, for example: fapolicyd-cli --file add /tmp/my_bin_dir/ --trust-file myapp . Update the fapolicyd database: Note Changing the content of a trusted file or directory changes their checksum, and therefore fapolicyd no longer considers them trusted. To make the new content trusted again, refresh the file trust database by using the fapolicyd-cli --file update command. If you do not provide any argument, the entire database refreshes. Alternatively, you can specify a path to a specific file or directory. Then, update the database by using fapolicyd-cli --update . Verification Check that your custom binary can be now executed, for example: Additional resources fapolicyd.trust(13) man page on your system 12.4. Adding custom allow and deny rules for fapolicyd The default set of rules in the fapolicyd package does not affect system functions. For custom scenarios, such as storing binaries and scripts in a non-standard directory or adding applications without the dnf or rpm installers, you must either mark additional files as trusted or add new custom rules. For basic scenarios, prefer Marking files as trusted using an additional source of trust . In more advanced scenarios such as allowing to execute a custom binary only for specific user and group identifiers, add new custom rules to the /etc/fapolicyd/rules.d/ directory. The following steps demonstrate adding a new rule to allow a custom binary. Prerequisites The fapolicyd framework is deployed on your system. Procedure Copy your custom binary to the required directory, for example: Stop the fapolicyd service: Use debug mode to identify a corresponding rule. Because the output of the fapolicyd --debug command is verbose and you can stop it only by pressing Ctrl + C or killing the corresponding process, redirect the error output to a file. In this case, you can limit the output only to access denials by using the --debug-deny option instead of --debug : Alternatively, you can run fapolicyd debug mode in another terminal. Repeat the command that fapolicyd denied: Stop debug mode by resuming it in the foreground and pressing Ctrl + C : Alternatively, kill the process of fapolicyd debug mode: Find a rule that denies the execution of your application: Locate the file that contains a rule that prevented the execution of your custom binary. In this case, the deny_audit perm=execute rule belongs to the 90-deny-execute.rules file: Add a new allow rule to the file that lexically precedes the rule file that contains the rule that denied the execution of your custom binary in the /etc/fapolicyd/rules.d/ directory: Insert the following rule to the 80-myapps.rules file: Alternatively, you can allow executions of all binaries in the /tmp directory by adding the following rule to the rule file in /etc/fapolicyd/rules.d/ : Important To make a rule effective recursively on all directories under the specified directory, add a trailing slash to the value of the dir= parameter in the rule ( /tmp/ in the example). To prevent changes in the content of your custom binary, define the required rule using an SHA-256 checksum: Change the rule to the following definition: Check that the list of compiled differs from the rule set in /etc/fapolicyd/rules.d/ , and update the list, which is stored in the /etc/fapolicyd/compiled.rules file: Check that your custom rule is in the list of fapolicyd rules before the rule that prevented the execution: Start the fapolicyd service: Verification Check that your custom binary can be now executed, for example: Additional resources fapolicyd.rules(5) and fapolicyd-cli(1) man pages on your system The documentation installed with the fapolicyd package in the /usr/share/fapolicyd/sample-rules/README-rules file. 12.5. Enabling fapolicyd integrity checks By default, fapolicyd does not perform integrity checking. You can configure fapolicyd to perform integrity checks by comparing either file sizes or SHA-256 hashes. You can also set integrity checks by using the Integrity Measurement Architecture (IMA) subsystem. Prerequisites The fapolicyd framework is deployed on your system. Procedure Open the /etc/fapolicyd/fapolicyd.conf file in a text editor of your choice, for example: Change the value of the integrity option from none to sha256 , save the file, and exit the editor: Restart the fapolicyd service: Verification Back up the file used for the verification: Change the content of the /bin/more binary: Use the changed binary as a regular user: Revert the changes: 12.6. Troubleshooting problems related to fapolicyd The fapolicyd application framework provides tools for troubleshooting the most common problems and you can also add applications installed with the rpm command to the trust database. Installing applications by using rpm If you install an application by using the rpm command, you have to perform a manual refresh of the fapolicyd RPM database: Install your application : Refresh the database: If you skip this step, the system can freeze and must be restarted. Service status If fapolicyd does not work correctly, check the service status: fapolicyd-cli checks and listings The --check-config , --check-watch_fs , and --check-trustdb options help you find syntax errors, not-yet-watched file systems, and file mismatches, for example: Use the --list option to check the current list of rules and their order: Debug mode Debug mode provides detailed information about matched rules, database status, and more. To switch fapolicyd to debug mode: Stop the fapolicyd service: Use debug mode to identify a corresponding rule: Because the output of the fapolicyd --debug command is verbose, you can redirect the error output to a file: Alternatively, to limit the output only to entries when fapolicyd denies access, use the --debug-deny option: You can also use permissive mode, which does not prevent running your application but only records the matched fapolicyd rule: Removing the fapolicyd database To solve problems related to the fapolicyd database, try to remove the database file: Warning Do not remove the /var/lib/fapolicyd/ directory. The fapolicyd framework automatically restores only the database file in this directory. Dumping the fapolicyd database The fapolicyd contains entries from all enabled trust sources. You can check the entries after dumping the database: Application pipe In rare cases, removing the fapolicyd pipe file can solve a lockup: Additional resources fapolicyd-cli(1) man page on your system 12.7. Preventing users from executing untrustworthy code by using the fapolicyd RHEL system role You can automate the installation and configuration of the fapolicyd service by using the fapolicyd RHEL system role. With this role, you can remotely configure the service to allow users to execute only trusted applications, for example, the ones which are listed in the RPM database and in an allow list. Additionally, the service can perform integrity checks before it executes an allowed application. Prerequisites You have prepared the control node and the managed nodes You are logged in to the control node as a user who can run playbooks on the managed nodes. The account you use to connect to the managed nodes has sudo permissions on them. Procedure Create a playbook file, for example ~/playbook.yml , with the following content: --- - name: Configuring fapolicyd hosts: managed-node-01.example.com tasks: - name: Allow only executables installed from RPM database and specific files ansible.builtin.include_role: name: rhel-system-roles.fapolicyd vars: fapolicyd_setup_permissive: false fapolicyd_setup_integrity: sha256 fapolicyd_setup_trust: rpmdb,file fapolicyd_add_trusted_file: - <path_to_allowed_command> - <path_to_allowed_service> The settings specified in the example playbook include the following: fapolicyd_setup_permissive: <true|false> Enables or disables sending policy decisions to the kernel for enforcement. Set this variable for debugging and testing purposes to false . fapolicyd_setup_integrity: <type_type> Defines the integrity checking method. You can set one of the following values: none (default): Disables integrity checking. size : The service compares only the file sizes of allowed applications. ima : The service checks the SHA-256 hash that the kernel's Integrity Measurement Architecture (IMA) stored in a file's extended attribute. Additionally, the service performs a size check. Note that the role does not configure the IMA kernel subsystem. To use this option, you must manually configure the IMA subsystem. sha256 : The service compares the SHA-256 hash of allowed applications. fapolicyd_setup_trust: <trust_backends> Defines the list of trust backends. If you include the file backend, specify the allowed executable files in the fapolicyd_add_trusted_file list. For details about all variables used in the playbook, see the /usr/share/ansible/roles/rhel-system-roles.fapolicyd.README.md file on the control node. Validate the playbook syntax: Note that this command only validates the syntax and does not protect against a wrong but valid configuration. Run the playbook: Verification Execute a binary application that is not on the allow list as a user: Additional resources /usr/share/ansible/roles/rhel-system-roles.fapolicyd/README.md file /usr/share/doc/rhel-system-roles/fapolicyd/ directory 12.8. Additional resources fapolicyd -related man pages listed by using the man -k fapolicyd command on your system FOSDEM 2020 fapolicyd presentation

Commands:
dnf install fapolicyd
vi /etc/fapolicyd/fapolicyd.conf
permissive = 1
systemctl enable --now fapolicyd
auditctl -w /etc/fapolicyd/ -p wa -k fapolicyd_changes service try-restart auditd
ausearch -ts recent -m fanotify
systemctl restart fapolicyd
systemctl status fapolicyd ● fapolicyd.service - File Access Policy Daemon Loaded: loaded (/usr/lib/systemd/system/fapolicyd.service; enabled; preset: disabled) Active: active (running) since Tue 2024-10-08 05:53:50 EDT; 11s ago ... Oct 08 05:53:51 machine1.example.com fapolicyd[4974]: Loading trust data from rpmdb backend Oct 08 05:53:51 machine1.example.com fapolicyd[4974]: Loading trust data from file backend Oct 08 05:53:51 machine1.example.com fapolicyd[4974]: Starting to listen for events
cp /bin/ls /tmp /tmp/ls bash: /tmp/ls: Operation not permitted
cp /bin/ls /tmp /tmp/ls bash: /tmp/ls: Operation not permitted
fapolicyd-cli --file add /tmp/ls --trust-file myapp
fapolicyd-cli --update
/tmp/ls ls
cp /bin/ls /tmp /tmp/ls bash: /tmp/ls: Operation not permitted
systemctl stop fapolicyd
fapolicyd --debug-deny 2> fapolicy.output & [1] 51341
/tmp/ls bash: /tmp/ls: Operation not permitted
fg fapolicyd --debug 2> fapolicy.output ^C
kill 51341
cat fapolicy.output | grep 'deny_audit' rule=13 dec=deny_audit perm=execute auid=0 pid=6855 exe=/usr/bin/bash : path=/tmp/ls ftype=application/x-executable trust=0
ls /etc/fapolicyd/rules.d/ 10-languages.rules 40-bad-elf.rules 72-shell.rules 20-dracut.rules 41-shared-obj.rules 90-deny-execute.rules 21-updaters.rules 42-trusted-elf.rules 95-allow-open.rules 30-patterns.rules 70-trusted-lang.rules cat /etc/fapolicyd/rules.d/90-deny-execute.rules Deny execution for anything untrusted deny_audit perm=execute all : all
touch /etc/fapolicyd/rules.d/80-myapps.rules vi /etc/fapolicyd/rules.d/80-myapps.rules
allow perm=execute exe=/usr/bin/bash trust=1 : path=/tmp/ls ftype=application/x-executable trust=0
allow perm=execute exe=/usr/bin/bash trust=1 : dir=/tmp/ trust=0
sha256sum /tmp/ls 780b75c90b2d41ea41679fcb358c892b1251b68d1927c80fbc0d9d148b25e836 ls
allow perm=execute exe=/usr/bin/bash trust=1 : sha256hash= 780b75c90b2d41ea41679fcb358c892b1251b68d1927c80fbc0d9d148b25e836
fagenrules --check /usr/sbin/fagenrules: Rules have changed and should be updated fagenrules --load
fapolicyd-cli --list 13. allow perm=execute exe=/usr/bin/bash trust=1 : path=/tmp/ls ftype=application/x-executable trust=0 14. deny_audit perm=execute all : all
systemctl start fapolicyd
/tmp/ls ls
vi /etc/fapolicyd/fapolicyd.conf
integrity = sha256
systemctl restart fapolicyd
cp /bin/more /bin/more.bak
cat /bin/less > /bin/more
su example.user /bin/more /etc/redhat-release bash: /bin/more: Operation not permitted
mv -f /bin/more.bak /bin/more
rpm -i application .rpm
fapolicyd-cli --update
systemctl status fapolicyd
fapolicyd-cli --check-config Daemon config is OK fapolicyd-cli --check-trustdb /etc/selinux/targeted/contexts/files/file_contexts miscompares: size sha256 /etc/selinux/targeted/policy/policy.31 miscompares: size sha256
fapolicyd-cli --list 9. allow perm=execute all : trust=1 10. allow perm=open all : ftype=%languages trust=1 11. deny_audit perm=any all : ftype=%languages 12. allow perm=any all : ftype=text/x-shellscript 13. deny_audit perm=execute all : all
systemctl stop fapolicyd
fapolicyd --debug
fapolicyd --debug 2> fapolicy.output
fapolicyd --debug-deny
fapolicyd --debug-deny --permissive
systemctl stop fapolicyd fapolicyd-cli --delete-db
fapolicyd-cli --dump-db
rm -f /var/run/fapolicyd/fapolicyd.fifo
--- - name: Configuring fapolicyd hosts: managed-node-01.example.com tasks: - name: Allow only executables installed from RPM database and specific files ansible.builtin.include_role: name: rhel-system-roles.fapolicyd vars: fapolicyd_setup_permissive: false fapolicyd_setup_integrity: sha256 fapolicyd_setup_trust: rpmdb,file fapolicyd_add_trusted_file: - <path_to_allowed_command> - <path_to_allowed_service>
ansible-playbook ~/playbook.yml --syntax-check
ansible-playbook ~/playbook.yml
ansible managed-node-01.example.com -m command -a 'su -c "/bin/not_authorized_application " <user_name> ' bash: line 1: /bin/not_authorized_application: Operation not permitted non-zero return code