To run your new application confined by SELinux on the system, prepare custom policy files that supplement rules enforced by the distribution SELinux policy.
Generating a policy template
The sepolicy generate tool provided by the policycoreutils-devel package helps you to perform the initial step. The tool generates a basic policy module, a Makefile, and a setup script. The script builds and installs the policy module, and relabels paths defined in the .fc file. You can use the command as a regular user, for example:
The setenforce 0 command sets SELinux to permissive mode on the whole system until the next restart. You can switch only the new domain to permissive mode and keep the rest of the SELinux policy in enforcing mode:
Note that the sepolicy generate command adds the permissive <myapp_t> rule to the policy module. Therefore, you can skip this step if you preserved the rule.
After you enable full auditing and permissive mode, run the application in its new SELinux domain. To see all access required by the application, test as many use cases as possible. The system logs all access requests that are not allowed in the current policy in the form of AVCs (SELinux access denial logs).
Processing AVCs
When SELinux denies an action, the system adds an Access Vector Cache (AVC) message to the /var/log/audit/audit.log and /var/log/messages files or the Journal daemon logs the denial.
You can display recent AVCs by using the ausearch command, for example:
The -ts recent parameter limits the search to last 10 minutes, but you can use a timestamp instead.
The audit2allow tool can process AVCs and generate corresponding allow rules for our policy.
Whenever your custom rules interact with resources defined in other policy packages, use interfaces instead simple allow rules if possible. The audit2allow -R command attempts to find an interface covering the use case instead. You must check each result of this command to ensure that the resulting policy is not too loose.
Policy macros
Policy macros, which are also called patterns or in more complex cases, interfaces, generate multiple rules that work together to allow certain use cases.
Examples:
interfaces:
mysql_read_config(<domain>) - gives the specified domain (httpd_t in this example) read access to MySQL config files
init_daemon_domain(<domain>, <file_type>) - promotes the type given as the first argument to a domain, while is used as an entry-point executable by the init daemon
patterns:
rw_files_pattern(<domain>, <dir_type>, <file_type>) - gives read and write access to files labeled located in directories labeled
domtrans_pattern(<source>, <entrypoint>, <target>) - sets up an automatic domain transition: when a process running in domain executes a binary labeled , the resulting process runs in domain
Interfaces available in Fedora, RHEL, and CentOS are defined in the modules section of the selinux-policy Github repository. Each policy module contains a set of interfaces as means for other modules to gain access to resources defined in the module.
Each macro has a fixed number of arguments you must specify. If you are not sure about the corresponding arguments, check the body of the macro for argument references in the form of the $ character followed by an index (for example, $1). Alternatively, check the interface index in the /usr/share/doc/selinux-policy/html/interfaces.html file provided by the selinux-policy-doc package.
Optional policy
Whenever you use an interface defined in one of the contribution modules, you must enclose it in an optional_policy block.
For example, Apache modules use kerberos_read_keytab interface defined in kerberos module:
The optional_policy block ensures you can use the policy module without the optional module. In this case, the apache module could be used on a system where the kerberos module was removed. If you omit the optional_policy block, installing your new policy module causes an error when any of the modules whose resources you granted access to are missing.
Expanding macros
The macro-expander tool provided by the selinux-policy-devel package displays what each macro contains if the name is not self-explanatory. Use macro-expander with the full macro name including all arguments to see all allow rules the macro generates, for example:
Note that macro-expander lists only allow rules by default. To see the full content of a macro, such as type_transition rules or attribute assignments, use macro-expander -M <> and ignore the module header and all require blocks:
Unlike other policy macros, permission sets do not use any arguments and only serve as a shorthand for a set of permissions that are commonly used together.
append_fifo_file_perms - { getattr open append lock ioctl }
All permission sets are defined in the obj_perm_sets.spt file. Because macro-expander by default displays only allow rules, you must adjust the command as follows to expand standalone permission sets:
In this case, macro-expander with the -M option generates a complete module containing the query, and you can ignore the module header and the require block.
Alternatively, supply a "dummy" allow rule, for example:
$ audit2allow -R
type=AVC msg=audit(1674838508.590:840): avc: denied { write } for pid=2383 comm="dhcpd" name="bluetooth.conf" dev="vda2" ino=2 scontext=system_u:system_r:dhcpd_t:s0 tcontext=system_u:object_r:bluetooth_conf_t:s0 tclass=file permissive=1
require {
type bluetooth_conf_t;
type dhcpd_t;
class file write;
}
#============= dhcpd_t ==============
allow dhcpd_t bluetooth_conf_t:file write;
No interface for writing into bluetooth_conf_t files exists, but a quick search in the bluetooth.if file on the selinux-policy Github repository reveals bluetooth_read_config, which you can modify for this use case:
optional_policy(`
gen_require(`
type bluetooth_conf_t;
')
allow dhcpd_t bluetooth_conf_t:file write_file_perms;
')
Process context (domain)
Types assigned to running processes are often referred to as domains. By default, a new process inherits the context of its parent. For example, a cat command entered by a user in the Bash shell running in the unconfined_t domain results in a new process running also as unconfined_t.
You can change this behavior by using a type_transition rule, which is a part of domtrans_pattern (domain transition pattern):
This pattern defines an automatic transition into the <target> domain using the type_transition rule and adds a set of rules allowing the transition. It is commonly used as a part of the init_daemon_domain(<target>, <entrypoint>) interface to facilitate the transition between init_t (the init daemon domain) and the newly created domain.
File context
New files inherit the type assigned to the directory where you created them. To assign a custom type without changing the application code, use the filetrans_pattern macro:
When a process running as httpd_t creates a file named apache_cache in a directory labeled var_t, the system sets the httpd_cache_t label on the resulting file.
You can use filetrans_pattern inside more specialized macros, where the <directory> argument is pre-filled based on a specific location, for example:
files_var_filetrans(httpd_t, httpd_cache_t, { file dir })
For more details, see the File Name Transition section in the RHEL 7 SELinux User's and Administrator's Guide.
Default file context definitions
All policy modules contain a file context definition file<module>.fc, which determines the default SELinux context of its resources. This context is used by labeling utilities, such as matchpathcon or restorecon.
When you load the compiled policy module onto the system, the compiler translates the module code into the Common Intermediate Language (CIL). This means that most errors encountered while loading the module are reported against the CIL version of your policy as demonstrated in the following example of a simple policy with a type definition that already exists in the system policy:
$ sepolicy generate --init /usr/bin/cat
$ echo "type abrt_t;" >> cat.te
$ sudo make -f /usr/share/selinux/devel/Makefile cat.pp
Compiling targeted cat module
Creating targeted cat.pp policy package
rm tmp/cat.mod.fc tmp/cat.mod
$ sudo semodule -i cat.pp
Re-declaration of type abrt_t
Previous declaration of type at /var/lib/selinux/targeted/tmp/modules/400/cat/cil:6
Bad type declaration at /var/lib/selinux/targeted/tmp/modules/400/cat/cil:6
Failed to build AST
semodule: Failed!
Because some CIL-related error messages are not clear, you might need to check the corresponding line in the CIL translation of the module source code. In this particular case, the error is related to line 6. Use the pp tool to translate the policy binary to CIL, and display the problematic line:
For more information about CIL and how its keywords differ from the source policy language, see the CIL Policy Language section on the SELinux Project wiki.
Example policy
By performing the following example steps, you create and test a new SELinux policy for the bootupd package. The package contains a service that is unconfined now.
Display all files installed by the bootupd package:
Among the listed files, you can see two unit files as well as two binaries, bootupd and bootupctl (remote command-line interface). You can ignore the rest of the files for the preparation of the new SELinux policy.
The bootupd.socket file indicates that the service uses the /var/run/bootupd.sock socket. Because the unit files of the bootupd service use the init system, use the --init parameter with the sepolicy generate command. Based on the content of the bootupd.service file, you know that /usr/libexec/bootupd is the service binary. You use the rest of the files (the unit and socket files) as values for the --writepath parameter so that the sepolicy generate command includes them in the policy:
$ sepolicy generate --init /usr/libexec/bootupd -w /usr/lib/systemd/system/bootupd.service /usr/lib/systemd/system/bootupd.socket /var/run/bootupd.sock
Created the following files:
/home/user/bootupd/bootupd.te # Type Enforcement file
/home/user/bootupd/bootupd.if # Interface file
/home/user/bootupd/bootupd.fc # File Contexts file
/home/user/bootupd/bootupd_selinux.spec # Spec file
/home/user/bootupd/bootupd.sh # Setup Script
The bootupd.te type enforcement file contains four new types:
* bootupd_t - domain for the bootupd process
* bootupd_exec_t - file type for the bootupd binary
* bootupd_var_run_t - type for the /var/run/bootupd.sock socket
* bootupd_unit_file_t - file type for the two unit files
The init_daemon_domain(bootupd_t, bootupd_exec_t) macro ensures that when the init daemon executes the bootupd binary, the resulting process runs in the bootupd_t domain.
The rest of the generated module is mostly an init daemon policy template, except for the group of macros granting access to bootupd_var_run_t:
* manage_dirs_pattern(bootupd_t, bootupd_var_run_t, bootupd_var_run_t) - manage directories labeled bootupd_var_run_t
* manage_files_pattern(bootupd_t, bootupd_var_run_t, bootupd_var_run_t) - manage files labeled bootupd_var_run_t
* manage_lnk_files_pattern(bootupd_t, bootupd_var_run_t, bootupd_var_run_t) - manage link files labeled bootupd_var_run_t
* files_pid_filetrans(bootupd_t, bootupd_var_run_t, { dir file lnk_file }) - if bootupd_t creates a file in a directory labeled var_run_t, the resulting file is labeled bootupd_var_run_t
The /var/run/bootupd.sock path does not exist, and therefore sepolicy generate generates access macros for directories, files, and link files. Because /var/run/bootupd.sock is a socket file, you can remove all three manage_* patterns. The file_patterns.spt file on the selinux-policy Github repository contains several socket patterns you can use instead, such as rw_sock_files_pattern.
Because the socket file is generated by systemd (not by bootupd) as a result of using systemctl start bootupd.socket, the files_pid_filetrans macro is also redundant. In a case your application binary generates the socket file, you must replace { dir file lnk_file }by{ sock_file }instead of removing the line withfiles_pid_filetrans`.
The bootupd.fc file-context configuration file also contains all the paths provided to the sepolicy generate command:
Again, sepolicy generate did not recognize the socket file correctly. Therefore, you must change the object class from -- (normal file) to -s (socket file):
$ sudo restorecon -v /usr/libexec/bootupd /usr/lib/systemd/system/bootupd.socket /usr/lib/systemd/system/bootupd.service
Relabeled /usr/libexec/bootupd from system_u:object_r:bin_t:s0 to system_u:object_r:bootupd_exec_t:s0
Relabeled /usr/lib/systemd/system/bootupd.socket from system_u:object_r:systemd_unit_file_t:s0 to system_u:object_r:bootupd_unit_file_t:s0
Relabeled /usr/lib/systemd/system/bootupd.service from system_u:object_r:systemd_unit_file_t:s0 to system_u:object_r:bootupd_unit_file_t:s0
Even though the service is running in the new domain and the socket file has the correct label, SELinux still reported three access vectors not allowed in the policy. For detailed explanation of AVC messages, see the SELinux denials in the Audit log section in the RHEL 9 Using SELinux document.
You can use the audit2allow tool for suggestions of allow rules missing in the new policy:
Because you cannot compile a policy module containing types defined in another policy module (fs_t and kernel_t), you must use audit2allow with the -R option for interfaces or macros containing the necessary rules:
After verifying that the suggested interfaces cover exactly the required use case using macro-expander, add them to the policy module. You also must enclose any interface originating from contribution modules in an optional_policy block. Deploy the new policy in permissive mode first, and test as many use scenarios on various system configurations as possible.
We appreciate your interest in having Red Hat content localized to your language. Please note that excessive use of this feature could cause delays in getting specific content you are interested in translated.
Generating Machine Translation
Loading…
We are generating a machine translation for this content. Depending on the length of the content, this process could take a while.
Comments