{"id":615,"date":"2025-04-12T09:42:06","date_gmt":"2025-04-12T09:42:06","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=615"},"modified":"2025-04-12T09:42:06","modified_gmt":"2025-04-12T09:42:06","slug":"linux-apparmor","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=615","title":{"rendered":"Linux AppArmor: A Beginner-Friendly Guide"},"content":{"rendered":"<p>In this blog, we will look into AppArmor, a key Linux security concept.<\/p>\n<p>We will look at,<\/p>\n<ol>\n<li>What is AppArmor?<\/li>\n<li>Example Use case<\/li>\n<li>AppArmor &amp; Mandatory Access Control (MAC)<\/li>\n<li>How Does AppArmor Work (Hands on Example)<\/li>\n<li>AppArmor in Container Runtimes<\/li>\n<\/ol>\n<p>AppArmor is widely used in containers and Kubernetes. Also an important topic for the CKS certification.<\/p>\n<p>We&#8217;ll break it down with a practical example to help you understand how it works.<\/p>\n<h2 id=\"what-is-apparmor\">What is AppArmor?<\/h2>\n<p>AppArmor (Application Armor)&nbsp;is a security feature in built into some&nbsp;Linux systems&nbsp;that controls what individual programs can and cannot do.<\/p>\n<p>If an application has a security flaw or is compromised by malware, AppArmor&nbsp;<strong>limits the damage<\/strong>&nbsp;it can do.<\/p>\n<p>For example, even if a hacker exploits a vulnerability in a web server, AppArmor can prevent it from accessing sensitive system files or running harmful commands.<\/p>\n<h2 id=\"example-use-case\">Example Use case<\/h2>\n<p>Let\u2019s say you are running an Apache web server.<\/p>\n<p><strong>Without AppArmor<\/strong>, if Apache is compromised, it can read any file on the system or execute random scripts, posing a serious security risk.<\/p>\n<p><strong>With an AppArmor profile<\/strong>, you can restrict Apache\u2019s access by:<\/p>\n<ul>\n<li>Allowing it to read only files from&nbsp;<strong>\/var\/www\/html<\/strong>&nbsp;(where website files are stored).<\/li>\n<li>Permitting it to write logs only to&nbsp;<strong>\/var\/log\/apache2<\/strong>.<\/li>\n<li>Restricting it to listen only on port 80.<\/li>\n<\/ul>\n<p>At the same time, it cannot access user files in \/home\/ or run unauthorized commands.<\/p>\n<p>You might ask,&nbsp;<strong>&#8220;Can\u2019t I do this using file permissions?&#8221;<\/strong><\/p>\n<p>Here is where it differs.<\/p>\n<p>File permissions control access based on user\/group IDs. If a user is compromised, the attacker can potentially&nbsp;<strong>access all the files<\/strong>&nbsp;owned by the user.<\/p>\n<p>Whereas AppArmor controls access based on what a program itself is allowed to do, no matter which user is running it.<\/p>\n<p>So even if Apache is compromised, it still can\u2019t access anything outside the allowed paths.<\/p>\n<h2 id=\"mandatory-access-control\">Mandatory Access Control<\/h2>\n<p>AppArmor falls under Mandatory Access Control (MAC).<\/p>\n<p>MAC is a&nbsp;<strong>security model&nbsp;<\/strong>where access to resources (files, directories, network ports, etc.) is strictly controlled by a central policy.<\/p>\n<p>Unlike&nbsp;<strong>Discretionary Access Control (DAC)<\/strong>, where the owner of a resource (e.g., a file) can set permissions (read, write, execute), MAC policies are enforced system-wide and cannot be overridden by users or programs, even if they have elevated privileges like&nbsp;<code>root<\/code>.<\/p>\n<p>For example, even if a program runs with root privileges, AppArmor can block it from modifying&nbsp;<strong>\/etc\/passwd<\/strong>. If an attacker compromises the program, they are still restricted by AppArmor.<\/p>\n<h2 id=\"how-does-apparmor-work\">How Does AppArmor Work?<\/h2>\n<p>AppArmor works based on&nbsp;<strong>profiles<\/strong>, similar to seccomp.<\/p>\n<p>Each profile is a plain-text file that defines what a program is allowed to do.<\/p>\n<p>On Linux systems, you can find existing AppArmor profiles in&nbsp;<strong>\/etc\/apparmor.d\/<\/strong><\/p>\n<p>For example,<\/p>\n<pre><code class=\"language-bash\">$ ls -p \/etc\/apparmor.d\/ | grep -v \/\n\nlsb_release\nnvidia_modprobe\nsbin.dhclient\nusr.bin.man\nusr.bin.tcpdump\nusr.lib.snapd.snap-confine.real\nusr.sbin.rsyslogd\n<\/code><\/pre>\n<h2 id=\"apparmor-practical-example\">AppArmor Practical Example<\/h2>\n<p>Let&#8217;s understand an&nbsp;<strong>AppArmor profile<\/strong>&nbsp;with a practical example.<\/p>\n<p>We will create a simple Bash script that attempts to read&nbsp;<strong>\/etc\/shadow<\/strong>&nbsp;(a file that contains hashed passwords and should be protected).<\/p>\n<p>The idea is to&nbsp;<strong>block<\/strong>&nbsp;the script from reading&nbsp;<strong>\/etc\/shadow<\/strong>, even when run as the root user.<\/p>\n<p>Lets create the script.<\/p>\n<pre><code class=\"language-bash\">$ echo -e '#!\/bin\/bash\\ncat \/etc\/shadow' &gt; script.sh\n\n$ chmod +x script.sh<\/code><\/pre>\n<p>If you run the script as root, you will not get any error. It will display the \/etc\/shadow contents.<\/p>\n<pre><code class=\"language-bash\">$ root@ubuntu:~# .\/script.sh \n\nroot:*:19579:0:99999:7:::\ndaemon:*:19579:0:99999:7:::\n.\n.\n.<\/code><\/pre>\n<p>Now, let&#8217;s create an&nbsp;<strong>AppArmor profile<\/strong>&nbsp;that prevents the script from reading&nbsp;<strong>\/etc\/shadow<\/strong>.<\/p>\n<p>We will define this profile in&nbsp;<strong>\/etc\/apparmor.d\/root.script.sh<\/strong>.<\/p>\n<pre><code class=\"language-bash\">#include &lt;tunables\/global&gt;\n\nprofile \/root\/script.sh {\n  # Allow reading its own file\n  \/root\/script.sh r,\n\n  # Deny access to \/etc\/shadow\n  deny \/etc\/shadow r,\n\n  # Allow execution of Bash\n  \/bin\/bash rmix,\n\n  # Allow execution of 'cat' or any other needed commands\n  \/usr\/bin\/cat rmix,\n}<\/code><\/pre>\n<p>Let&#8217;s load the profile.<\/p>\n<pre><code class=\"language-bash\">sudo apparmor_parser -r \/etc\/apparmor.d\/root.script.sh<\/code><\/pre>\n<p>Check the status.<\/p>\n<pre><code class=\"language-bash\">$ root@ubuntu:~# sudo apparmor_status | grep script.sh\n  \n  \/root\/script.sh<\/code><\/pre>\n<p>It should now show the correct path (<code>\/root\/script.sh<\/code>)<\/p>\n<p>Now lets run the script and see what happens. You should get a&nbsp;<strong>permission denied<\/strong>&nbsp;error when trying to access&nbsp;<code>\/etc\/shadow<\/code>, even as root.<\/p>\n<pre><code class=\"language-bash\">$ root@ubuntu:~# .\/script.sh\n\n.\/script.sh: line 2: \/usr\/bin\/cat: Permission denied<\/code><\/pre>\n<p>As you can see, AppArmor restricts applications based on profiles, even if you run them as root.<\/p>\n<p>Why is AppArmor Restricting Root?<\/p>\n<p>Root has full system access, but&nbsp;<strong>AppArmor controls<\/strong>&nbsp;what an application (even one run by root) can do.<\/p>\n<p>This prevents&nbsp;<strong>privilege escalation attacks<\/strong>&nbsp;(e.g., a compromised root script can&#8217;t access sensitive files like&nbsp;<code>\/etc\/shadow<\/code>). It enforces&nbsp;<strong>least privilege<\/strong>, allowing only the necessary permissions.<\/p>\n<h2 id=\"apparmor-in-containers\">AppArmor In Containers<\/h2>\n<p>AppArmor is integrated into container runtimes to enforce Mandatory Access Control (MAC) on container processes.<\/p>\n<p>The runtime loads these profiles when starting a container, ensuring each container operates in a sandbox.<\/p>\n<p>For example, Docker comes with a default profile called docker-default. This profile is applied to every container unless overridden. You can&nbsp;<a href=\"https:\/\/github.com\/moby\/moby\/blob\/master\/contrib\/apparmor\/template.go?ref=devopscube.com\" rel=\"noreferrer\">check the profile here<\/a>.<\/p>\n<p>It is designed to<\/p>\n<ul>\n<li>Allow basic operations (e.g., read\/write within the container\u2019s filesystem).<\/li>\n<li>Deny dangerous actions (e.g., mounting \/proc or accessing \/etc\/passwd on the host).<\/li>\n<\/ul>\n<p>Here is a simple example that shows the docker default AppArmor profile blocking reading \/proc\/sysrq-trigger using cat.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/03\/image-1.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"992\" height=\"486\"><\/figure>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>AppArmor improves Linux security by restricting program actions, preventing exploits, and ensuring the least privilege. A must-know for system admins and CKS aspirants!<\/p>\n<p>If you have any doubts about this blog, drop it on the comment!<\/p>\n<p>Want to Stay Ahead in DevOps &amp; Cloud? Join the Free Newsletter Below.<\/p>\n<p><!--kg-card-begin: html--><br \/>\n <iframe loading=\"lazy\" src=\"https:\/\/embeds.beehiiv.com\/2a495ef4-3de7-4600-8a0d-de5dc968b372\" data-test-id=\"beehiiv-embed\" width=\"100%\" height=\"320\" frameborder=\"0\" scrolling=\"no\" style=\"border-radius: 4px; border: 2px solid #e5e7eb; margin: 0; background-color: transparent;\"><\/iframe><br \/>\n<!--kg-card-end: html--><\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/linux-apparmor\/\" target=\"_blank\" rel=\"noopener noreferrer\">Linux AppArmor: A Beginner-Friendly Guide \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/linux-apparmor\/<\/p>\n","protected":false},"author":1,"featured_media":616,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-615","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/615","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=615"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/615\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/616"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=615"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=615"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=615"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}