AppArmor and SELinux Comparison

  AppArmor SELinux
Type of Security
  • Pathname based system does not require labeling or relabeling filesystem
  • When developing profiles incrementally, there is much less reason to modify other profiles, because all profiles simply refer to the pathnames they use
  • Pathnames are easy to understand and audit
  • Attaches labels to all files, processes
  • Labels identify the channels of communication, so adding new profiles may require modifying existing profiles to split channels of communication, making incremental policy development difficult
  • Not all applications preserve labels
Consequences
  • Automated tools in place
  • Easier integration with Novell platforms
  • Hard to maintain
  • Low adoption rate
Ease of Use
  • Auditable policies
  • Integrated GUI/Console toolset
  • Proficiency with 1-2 days training
  • Usability is primary goal
  • Complex policy language
  • Hard to manage rules
  • Lack of integrated tools
  • Substantial training investment
More Automated
 
  1. Open YaST Control Center
  2. Run Server Analyzer to determine which programs to profile
  3. Run the Profile Wizard to generate a profile template
  4. Run the application through normal operation
  5. Run the interactive optimizer to synthesize log events into a profile

SELinux audit2allow

  1. Create a file at $SELINUX_SRC/domains/program/foo.te
  2. Put the daemon domain macro call in the file
  3. Create the file contexts file
  4. Put the first list of file contexts in file.fc
  5. Load the new policy with make load
  6. Label the foo files
  7. Start the daemon, service foo start
  8. Examine your audit log for denial messages
  9. Familiarize yourself with the errors the daemon is generating
  10. Use audit2allow to start the first round of policy rules
  11. Look to see if the foo_t domain tries to create a network socket
  12. Continue to iterate through the basic steps to generate all the rules you need
  13. If the domain tries to access port_t, which relates to tclass=tcp_socket or tclass=udp_socket in the AVC log message, you need to determine what port number foo_t needs to use
  14. Iterate through the remaining AVC denials. When they are resolved with new policy, you can configure the unique port requirements for the foo_t domain
  15. With the daemon started, determine which port foo is using
  16. Remove the generic port_t rule, replacing it with a specific rule for a new port type based on the foo_t domain
More Efficient
 

usr/sbin/in.ftpd {
#include <immunix-standard/base>
#include <immunix-standard/nameservice>
#include <immunix-standard/authentication>
#include <user-custom/ftpd>
/   r,
/dev/urandom    r,
/etc/fstab    r,
/etc/ftpaccess    r,
/etc/ftpconversions    r,
/etc/ftphosts   r,
/etc/ftpusers    r,
/etc/shells    r,
/usr/sbin/in.ftpd    r,
/usr/share/ssl/certs/ca-bundle.crt   r,
/usr/share/ssl/certs/ftpd-rsa.pem   r,
/usr/share/ssl/private/ftpd-rsa-key.pem   r,
/usr/share/ssl/.rnd    w,
/var/log/xferlog    w,
/var/run   wr,
/var/run/ftp.{pids,rips}-all   wr,
}

AppArmor profile for the same program is about 4x smaller

#################################
#
# Rules for the ftpd_t domain
#
type ftp_port_t, port_type;
type ftp_data_port_t, port_type;
daemon_domain(ftpd, `, auth_chkpwd')
type etc_ftpd_t, file_type, sysadmfile;

can_network(ftpd_t)
can_ypbind(ftpd_t)
allow ftpd_t self:unix_dgram_socket create_socket_perms;
allow ftpd_t self:unix_stream_socket create_socket_perms;
allow ftpd_t self:process {getcap setcap};
allow ftpd_t self:fifo_file rw_file_perms;

allow ftpd_t bin_t:dir search;
can_exec(ftpd_t, bin_t)
allow ftpd_t { sysctl_t sysctl_kernel_t }:dir search;
allow ftpd_t sysctl_kernel_t:file { getattr read };
allow ftpd_t urandom_device_t:chr_file { getattr read };

ifdef(`crond.te', `
system_crond_entry(ftpd_exec_t, ftpd_t)
can_exec(ftpd_t, { sbin_t shell_exec_t })
')

allow ftpd_t ftp_data_port_t:tcp_socket name_bind;

ifdef(`ftpd_daemon', `
define(`ftpd_is_daemon', `')
') dnl end ftpd_daemon
ifdef(`ftpd_is_daemon', `
rw_dir_create_file(ftpd_t, var_lock_t)
allow ftpd_t ftp_port_t:tcp_socket name_bind;
allow ftpd_t self:unix_dgram_socket { sendto };
can_tcp_connect(userdomain, ftpd_t)
', `
ifdef(`inetd.te', `
domain_auto_trans(inetd_t, ftpd_exec_t, ftpd_t)
ifdef(`tcpd.te', `domain_auto_trans(tcpd_t, ftpd_exec_t, ftpd_t)')

# Use sockets inherited from inetd.
allow ftpd_t inetd_t:fd use;
allow ftpd_t inetd_t:tcp_socket rw_stream_socket_perms;

# Send SIGCHLD to inetd on death.
allow ftpd_t inetd_t:process sigchld;
') dnl end inetd.te
')dnl end (else) ftp_is_daemon
ifdef(`ftp_shm', `
allow ftpd_t tmpfs_t:file { read write };
allow ftpd_t { tmpfs_t initrc_t }:shm { read write unix_read unix_write associate };
')

# Use capabilities.
allow ftpd_t ftpd_t:capability { net_bind_service setuid setgid fowner fsetid chown sys_resource sys_chroot };

# Append to /var/log/wtmp.
allow ftpd_t wtmp_t:file { getattr append };

# allow access to /home
allow ftpd_t home_root_t:dir { getattr search };v
# Create and modify /var/log/xferlog.
type xferlog_t, file_type, sysadmfile, logfile;
file_type_auto_trans(ftpd_t, var_log_t, xferlog_t, file)
# Execute /bin/ls (can comment this out for proftpd)
# also may need rules to allow tar etc...
can_exec(ftpd_t, ls_exec_t)

allow { ftpd_t initrc_t } etc_ftpd_t:file r_file_perms;
allow ftpd_t { etc_t resolv_conf_t etc_runtime_t }:file { getattr read };
allow ftpd_t proc_t:file { getattr read };

')dnl end if ftp_home_dir

More Auditable
 

/usr/sbin/in.ftpd {
#include <immunix-standard/base>
#include <immunix-standard/nameservice>
#include <immunix-standard/authentication>
#include <user-custom/ftpd>
/    r,
/dev/urandom    r,
/etc/fstab    r,
/etc/ftpaccess    r,
/etc/ftpconversions    r,
/etc/ftphosts    r,
/etc/ftpusers    r,
/etc/shells    r,
/usr/sbin/in.ftpd    r,
/usr/share/ssl/certs/ca-bundle.crt    r,
/usr/share/ssl/certs/ftpd-rsa.pem    r,
/usr/share/ssl/private/ftpd-rsa-key.pem    r,
/usr/share/ssl/.rnd    w,
/var/log/xferlog    w,
/var/run    wr,
/var/run/ftp.{pids,rips}-all    wr,
}

ifdef(`ftpd_daemon', `
define(`ftpd_is_daemon', `')
') dnl end ftpd_daemon
ifdef(`ftpd_is_daemon', `
rw_dir_create_file(ftpd_t, var_lock_t)
allow ftpd_t ftp_port_t:tcp_socket name_bind;
allow ftpd_t self:unix_dgram_socket { sendto };
can_tcp_connect(userdomain, ftpd_t)
', `
ifdef(`inetd.te', `
domain_auto_trans(inetd_t, ftpd_exec_t, ftpd_t)
ifdef(`tcpd.te', `domain_auto_trans(tcpd_t, ftpd_exec_t, ftpd_t)')

# Use sockets inherited from inetd.
allow ftpd_t inetd_t:fd use;
allow ftpd_t inetd_t:tcp_socket rw_stream_socket_perms;

# Send SIGCHLD to inetd on death.
allow ftpd_t inetd_t:process sigchld;
') dnl end inetd.te
')dnl end (else) ftp_is_daemon
ifdef(`ftp_shm', `
allow ftpd_t tmpfs_t:file { read write };

 
  • Easy to Use—pre-defined abstractions
  • Easy to Modify—regular expression & wildcard support
  • Easy to Audit—classic Unix syntax
  • Custom and complex programming language
  • Hard-to-manage rules