Psst! Your Ubuntu system has been secretly hardened with AppArmor!
Three years ago, I devoted a couple columns (in the April and August 2006 issues of LJ) to Novell AppArmor, a partial implementation of Mandatory Access Controls (MACs) that Novell had integrated into SUSE Linux as part of its acquisition of Immunix. Novell also had released AppArmor's source code under the GPL. I expressed hope that other distributions soon would offer AppArmor as an easier-to-configure alternative to SELinux.
The good news is, during the three years since I wrote those articles, both Ubuntu and Mandriva have incorporated AppArmor into their respective distributions. Although until recently Ubuntu hasn't provided very much documentation on its AppArmor port—one might even characterize Ubuntu's AppArmor adoption as stealthful—AppArmor actually has been in Ubuntu since Ubuntu 7.10 (Gutsy Gibbon). In fact, I mentioned this inclusion in these very pages in the April 2008 issue, in my article “Security Features in Ubuntu Server”.
At the time, I commented that due to its lack of AppArmor GUI tools or documentation, AppArmor in Ubuntu 7.10 appeared to be targeted at expert users. With Ubuntu 9.04, I'm happy to report that although AppArmor in Ubuntu still is configured strictly via the command line, it's now amply documented and comes with a useful set of default profiles.
The bad news is, in late 2007, Novell laid off all four of its full-time AppArmor developers, raising serious questions about the future of AppArmor (see The Future of AppArmor sidebar).
Being a security goon, I'm not optimistic by nature. However, I do believe in making hay while the sun shines. If a compelling tool is available to you in Ubuntu 9.04, you should take advantage of it and not worry about whether that tool will be available in Ubuntu 11.04—unless, of course, that tool requires an enormous investment in your time, attention and thought.
But AppArmor, unlike most other MAC mechanism, is not such a tool. As I explain in this month's overview of AppArmor in Ubuntu, for many applications, you don't need to do anything to enable or configure AppArmor protection. For others, AppArmor essentially can train itself in protecting them.
So, let's take a look at AppArmor in Ubuntu.
In case you missed my earlier articles on this topic, AppArmor is based on the Linux Security Modules (LSMs), as is SELinux. AppArmor, however, provides only a subset of the controls SELinux provides. Whereas SELinux has methods for Type Enforcement (TE), Role-Based Access Controls (RBACs), and Multi-Level Security (MLS), AppArmor provides only a form of Type Enforcement.
Type Enforcement involves confining a given application to a specific set of actions, such as writing to Internet network sockets, reading a specific file and so forth. RBAC involves restricting user activity based on the defined role, and MLS involves limiting access to a given resource based on its data classification (or label).
By focusing on Type Enforcement, AppArmor provides protection against, arguably, the most common Linux attack scenario—the possibility of an attacker exploiting vulnerabilities in a given application that allows the attacker to perform activities not intended by the application's developer or administrator. By creating a baseline of expected application behavior and blocking all activity that falls outside that baseline, AppArmor (potentially) can mitigate even zero-day (unpatched) software vulnerabilities.
What AppArmor cannot do, however, is prevent abuse of an application's intended functionality. For example, the Secure Shell dæmon, SSHD, is designed to grant shell access to remote users. If an attacker figures out how to break SSHD's authentication by, for example, entering just the right sort of gibberish in the user name field of an SSH login session, resulting in SSHD giving the attacker a remote shell as some authorized user, AppArmor may very well allow the attack to proceed, as the attack's outcome is perfectly consistent with what SSHD would be expected to do after successful login.
If, on the other hand, an attacker figured out how to make the CUPS print services dæmon add a line to /etc/passwd that effectively creates a new user account, AppArmor could prevent that attack from succeeding, because CUPS has no reason to be able to write to the file /etc/passwd.
In SUSE's and Ubuntu's AppArmor implementations, AppArmor comes with an assortment of pretested profiles for popular server and client applications and with simple tools for creating your own AppArmor profiles. On Ubuntu systems, most of the pretested profiles are enabled by default. There's nothing you need to do to install or enable them.
Other Ubuntu AppArmor profiles are installed, but set to run in complain mode, in which AppArmor only logs unexpected application behavior to /var/log/messages rather than both blocking and logging it. You either can leave them that way, if you're satisfied with just using AppArmor as a watchdog for those applications (in which case, you should keep an eye on /var/log/messages), or you can switch them to enforce mode yourself, although, of course, you should test thoroughly first.
Still other profiles are provided by Ubuntu's optional apparmor-profiles package. Whereas ideally a given AppArmor profile should be incorporated into its target application's package, for now at least, apparmor-profiles is sort of a catchall for emerging and not-quite-stable profiles that, for whatever reason, aren't appropriate to bundle with their corresponding packages.
Active AppArmor profiles reside in /etc/apparmor.d. The files at the root of this directory are parsed and loaded at boot time automatically. The apparmor-profiles package installs some of its profiles there, but puts experimental profiles in /usr/share/doc/apparmor-profiles/extras.
The Ubuntu 9.04 packages shown in Table 1 put corresponding profiles into /etc/apparmor.d.
Table 1. Ubuntu Packages Having AppArmor Profiles
Ubuntu Package Name | AppArmor Default Mode | Package Description |
---|---|---|
bind | enforce | The BIND DNS server |
clamd | enforce | ClamAV antivirus scanner |
cups | enforce | Print services dæmon |
dhcp3-client | enforce | ISC's DHCP client |
dhcp3-server | enforce | ISC's DHCP server |
mysql | enforce | MySQL database engine |
slapd | enforce | OpenLDAP LDAP server |
tcpdump | enforce | Command-line network sniffer |
If you install the package apparmor-profiles, you'll additionally get default protection for the packages shown in Table 2.
Table 2. Packages Whose AppArmor Profiles Are Provided by apparmor-profiles
Ubuntu Package Name | AppArmor Default Mode | Package Description |
---|---|---|
ping | complain | Network diagnostic tool |
klogd | complain | Kernel message logger |
syslogd | complain | Berkeley system message logger |
syslog-ng | complain | Syslog-NG system message logger |
avahi-daemon | enforce | Multicast-DNS (network auto-discover) |
dnsmasq | complain | DNS/DHCP forwarder used for Internet connection sharing |
identd | complain | Maps user names to processes/sockets |
mdnsd | complain | Scans for Multicast-DNS services |
nmbd | complain | Part of Samba (MS file sharing) |
nscd | complain | Nameservice (DNS) Caching Dæmon |
ntpd | complain | Network Time Protocol Dæmon |
smbd | complain | Part of Samba (MS file sharing) |
traceroute | complain | Network diagnostic tool |
The lists in Tables 1 and 2 are perhaps as notable for what they lack as for what they include. Although such high-profile server applications as BIND, MySQL, Samba, NTPD and CUPS are represented, very notably absent are Apache, Postfix, Sendmail, Squid and SSHD. And, what about important client-side network tools like Firefox, Skype, Evolution, Acrobat and Opera?
Profiles for those applications and many more are provided by apparmor-profiles in /usr/share/doc/apparmor-profiles/extras, but because they reside there rather than /etc/apparmor.d, they're effectively disabled. These profiles are disabled either because they haven't yet been updated to work with the latest version of whatever package they protect or because they don't yet provide enough protection relative to the Ubuntu AppArmor team's concerns about their stability.
Testing and tweaking such profiles is beyond the scope of this article, but suffice it to say, it involves the logprof command.
At a high level, creating a new AppArmor profile involves creating a deny all policy and then running that profile in complain (log-only) mode; running your application in as typical a fashion as possible; using the resulting log messages to loosen up the profile enough (but only enough) for the application to work properly; and setting the finished, tuned profile to enforce mode.
AppArmor, through its genprof and logprof commands, walks you through this entire process interactively. I'm not going to cover the process for tweaking existing AppArmor profiles with logprof. logprof sessions are very similar to genprof sessions, so if you're comfortable creating new profiles, it's easy to tweak existing ones. (See Resources for more information on the latter.)
So, let's walk through the process of creating a new AppArmor profile. For this example scenario, let's start with a simple shell script, spaztacle.sh, that could use some protection. Listing 1 shows the script itself.
As you can see, this script allows users to create a backup archive of the directory /var/spaetzle, using the archive filename specified in the command line (for example, spaztacle.sh mybackup.tar). To create an AppArmor profile for it, run the following command:
bash-$ sudo genprof spaztacle.sh
What follows is an interactive question-and-answer session in which:
genprof creates a new AppArmor profile for spaztacle.sh, containing a simple “deny all access” policy.
genprof loads the new policy in complain mode and prompts you to start the application in a separate window (this is your first opportunity to demonstrate normal application activity to genprof).
After you've demonstrated the application sufficiently, genprof analyzes the messages the new profile generated in /var/log/messages.
For each log message, genprof asks what sort of rule to add to your new AppArmor profile to account for the behavior that was logged.
After all log messages have been analyzed, genprof allows you to repeat the test/analyze cycle, which may or may not result in additional rules for the profile.
When you're done with the testing/log-analyzing cycle, genprof saves the profile and loads it in enforce mode. You're done!
A full genprof session is too lengthy to list and dissect here, but we can discuss some highlights from my sudo genprof spaztacle.sh session that illustrate how the process works.
First, I'm asked whether genprof should query the AppArmor profile repository at opensuse.org. I select d to disable repository access.
Next, I'm prompted to run my application. So I open another xterm window, and from my home directory, run the command spaztacle.sh arf.tar. That command results in the file arf.tar being written in my home directory, as expected.
Back in the genprof session, I type s to begin scanning the system log for AppArmor messages. genprof asks me whether and how to allow /bin/tar to be executed. This, of course, is the core function of spaztacle.sh, so I type i to cause tar to be allowed, “inheriting” the same profile as spaztacle.sh itself.
Next, I'm asked whether to allow /bin/dash to run. Because spaztacle.sh is a Bourne shell script, it needs to be interpreted by /bin/dash (on Ubuntu 9.04 /bin/sh actually is a symbolic link to /bin/dash). I type a to allow /bin/dash to run.
Then, I'm asked whether spaztacle.sh may read itself—that is, /usr/bin/spaztacle.sh. This is an expected part of the script-parsing process; I type a.
For now, there are no further log messages to process, so genprof prompts me to save the tweaked profile and asks whether to scan for more events. Before answering, I switch to my other xterm window, change my working directory to /home/mick/Public, and run the command spaztacle.sh anothertar.tar.
Sure enough, back in the genprof session after I type s again, there's a new set of “complaints” to process. The first concerns whether spaztacle.sh (actually tar) can read /etc/group. I'm given the option of allowing access only to /etc/group or of enabling the abstraction called nameservice.
Abstractions are groups of commonly accessed profile objects that constitute common system functions and services, such as checking file permissions, looking up hostnames and so forth. In this case, I select the nameservice abstraction and type a to allow it.
Next, genprof asks me whether to allow only write access to the (new) file anothertar.tar, or to use some sort of wild card (“glob” in AppArmor parlance). Because I want users to be able to create arbitrary tar archives in their respective home directories, I type n to specify a new glob, and specify /home/**.
In AppArmor profiles, ** is a wild card that means “any string, including /”. This is in contrast to *, which means “any string up to and excluding a / and anything after it”. Therefore, /home/** means “everything within /home/, including all subdirectories of its subdirectories”.
This implies that users might be able to write files to other users' home directories, but AppArmor controls augment normal Linux filesystem permissions; they don't replace them. In our example, therefore, users will be able to write to other other users' directories only if those directories' permissions are set accordingly.
In fact, our /home/** rule actually reduces the number of places spaztacle.sh can create tar archives. Without this rule, spaztacle.sh can write in any directory in which the user executing it has write privileges, not just subdirectories of /home/.
There are just two more log entries to account for. One concerns read access to /var/spaetzle. I type a to allow this access. You might be tempted to create a new glob instead, /var/spaetzle/**, but as it happens, tar handles the directory itself and its contents separately.
Therefore, only after creating the rule allowing access to /var/spaetzle and being prompted for a decision on allowing access to the file /var/spaetzle/arf.txt, will I type n, create the new glob /var/spaetzle/** and allow access to it.
Finally, we've reached the end of the new AppArmor events in /var/log/messages. When genprof asks me what to do after saving the changed profile, I finish the genprof session. genprof puts my new profile into enforce mode, reloads it and I'm done! Listing 2 shows the result, /etc/apparmor.d/usr.bin.spaztacle.sh.
Happily, if I run spaztacle.sh again, it still works. But, is AppArmor doing anything? I can make sure the new profile is loaded with this command:
bash-$ sudo aa-status
Here's part of its output:
apparmor module is loaded. 26 profiles are loaded. 13 profiles are in enforce mode. /usr/sbin/clamd /usr/sbin/cupsd /usr/bin/spaztacle.sh [...]
Great! The spaztacle.sh profile is loaded in enforce mode. Besides showing what profiles are loaded and in what mode, aa-status also lists which processes are being protected actively. Because spaztacle.sh isn't actually running at the moment, it doesn't turn up in aa-status' output as an active process, but that's okay—normally you'd expect server dæmons, not commands per se, to turn up in that part of aa-status' output.
There's just one more test we'll do to see if AppArmor is doing its job. The more astute among you may have noticed that there's a glaring flaw in my little shell script (Listing 1). Because I didn't contain $1 in quotation marks, it's possible for a mischievous user to execute spaztacle.sh like this:
bash-$ spaztacle.sh "tarfile.tar /etc/apparmor.d/"
When the tar command in spaztacle expands the command input, it will correctly interpret tarfile.tar as the target file, but will include not only /var/spaetzle but also /etc/apparmor.d/ in the tar archive! On the one hand, local file permissions still apply. This works only if users in question have read access to /etc/apparmor.d, which means that although they're tricking spaztacle.sh, they aren't copying anything they'd otherwise be unable to get at.
But on the other hand, this is unexpected behavior for my unfortunate script. I don't want users to be able to include arbitrary directories in their authorized backups of /var/spaetzle.
So I'm glad to see that if I actually try running spaztacle.sh that way with my new AppArmor profile in enforce mode, this is the result:
tar: /etc/apparmor.d: Cannot open: Permission denied tar: Error exit delayed from previous errors
The following message also has been written to /var/log/messages:
Jun 16 01:17:43 micksbox kernel: [57354.414567] type=1503 audit(1245133063.520:1004): operation="inode_permission" requested_mask="::r" denied_mask="::r" fsuid=1000 name="/etc/apparmor.d/" pid=28019 profile="/usr/bin/spaztacle.sh"
Success! AppArmor has correctly identified bad behavior on spaztacle.sh's part. And, the intended tar file (tarfile.tar) not only was created, it also contains the backup of /var/spaetzle that I did want the user to be able to create—only the unexpected part of spaztacle.sh's activity was blocked. Success indeed!
Using genprof may seem a little involved, but the man pages for genprof, logprof and apparmor.d explain most of what you need to know. The tutorials listed in Resources should be helpful too. I hope I've covered enough here to get you started using AppArmor on your own Ubuntu system!