[lxc-devel] [lxd/master] Fix AppArmor stack handling with nesting

stgraber on Github lxc-bot at linuxcontainers.org
Thu Apr 20 04:21:04 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 844 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170420/e90f8f6f/attachment.bin>
-------------- next part --------------
From 546e2a60809a108a1f505b99c6edbda52b12c739 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 20 Apr 2017 00:17:42 -0400
Subject: [PATCH] Fix AppArmor stack handling with nesting
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This does the following
 - Re-work detection code to always fill all the various AppArmor variables
 - Add detection of LXD being run in an AppArmor stacked configuration
 - If running stacked (nesting), then load a per-container profile but
   don't attempt to setup a second level of stacking as this isn't
   supported by AppArmor.
 - Treat a security.privileged=true container inside an unprivileged
   container the same as running an unprivileged container.

Closes #3172

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/apparmor.go      |  11 +++--
 lxd/container_lxc.go |   4 +-
 lxd/daemon.go        | 132 ++++++++++++++++++++++++++-------------------------
 3 files changed, 75 insertions(+), 72 deletions(-)

diff --git a/lxd/apparmor.go b/lxd/apparmor.go
index f4563b2..47a1881 100644
--- a/lxd/apparmor.go
+++ b/lxd/apparmor.go
@@ -319,7 +319,7 @@ func getAAProfileContent(c container) string {
 		profile += "  mount fstype=cgroup -> /sys/fs/cgroup/**,\n"
 	}
 
-	if aaStacking {
+	if aaStacking && !aaStacked {
 		profile += "\n  ### Feature: apparmor stacking\n"
 		profile += `  ### Configuration: apparmor profile loading (in namespace)
   deny /sys/k[^e]*{,/**} wklx,
@@ -357,12 +357,12 @@ func getAAProfileContent(c container) string {
 		// Apply nesting bits
 		profile += "\n  ### Configuration: nesting\n"
 		profile += strings.TrimLeft(AA_PROFILE_NESTING, "\n")
-		if !aaStacking || c.IsPrivileged() {
+		if !aaStacking || aaStacked {
 			profile += fmt.Sprintf("  change_profile -> \"%s\",\n", AAProfileFull(c))
 		}
 	}
 
-	if !c.IsPrivileged() {
+	if !c.IsPrivileged() || runningInUserns {
 		// Apply unprivileged bits
 		profile += "\n  ### Configuration: unprivileged containers\n"
 		profile += strings.TrimLeft(AA_PROFILE_UNPRIVILEGED, "\n")
@@ -404,7 +404,7 @@ func runApparmor(command string, c container) error {
 }
 
 func mkApparmorNamespace(namespace string) error {
-	if !aaStacking {
+	if !aaStacking || aaStacked {
 		return nil
 	}
 
@@ -470,7 +470,7 @@ func AADestroy(c container) error {
 		return nil
 	}
 
-	if aaStacking {
+	if aaStacking && !aaStacked {
 		p := path.Join("/sys/kernel/security/apparmor/policy/namespaces", AANamespace(c))
 		if err := os.Remove(p); err != nil {
 			logger.Error("error removing apparmor namespace", log.Ctx{"err": err, "ns": p})
@@ -508,6 +508,7 @@ func aaProfile() string {
 	if err == nil {
 		return strings.TrimSpace(string(contents))
 	}
+
 	return ""
 }
 
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 52dfbec..4ed8847 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -722,7 +722,7 @@ func (c *containerLXC) initLXC() error {
 
 	// Base config
 	toDrop := "sys_time sys_module sys_rawio"
-	if !aaStacking {
+	if !aaStacking || aaStacked {
 		toDrop = toDrop + " mac_admin mac_override"
 	}
 
@@ -941,7 +941,7 @@ func (c *containerLXC) initLXC() error {
 			 * the old way of nesting, i.e. using the parent's
 			 * profile.
 			 */
-			if aaStacking {
+			if aaStacking && !aaStacked {
 				profile = fmt.Sprintf("%s//&:%s:", profile, AANamespace(c))
 			}
 
diff --git a/lxd/daemon.go b/lxd/daemon.go
index cce17e7..ce0f12a 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -40,10 +40,11 @@ import (
 )
 
 // AppArmor
-var aaAdmin = true
-var aaAvailable = true
+var aaAvailable = false
+var aaAdmin = false
 var aaConfined = false
 var aaStacking = false
+var aaStacked = false
 
 // CGroup
 var cgBlkioController = false
@@ -545,90 +546,91 @@ func (d *Daemon) Init() error {
 	/* Detect user namespaces */
 	runningInUserns = shared.RunningInUserNS()
 
-	/* Detect AppArmor support */
-	if aaAvailable && os.Getenv("LXD_SECURITY_APPARMOR") == "false" {
-		aaAvailable = false
-		aaAdmin = false
+	/* Detect AppArmor availability */
+	_, err = exec.LookPath("apparmor_parser")
+	if os.Getenv("LXD_SECURITY_APPARMOR") == "false" {
 		logger.Warnf("AppArmor support has been manually disabled")
-	}
-
-	if aaAvailable && !shared.IsDir("/sys/kernel/security/apparmor") {
-		aaAvailable = false
-		aaAdmin = false
+	} else if !shared.IsDir("/sys/kernel/security/apparmor") {
 		logger.Warnf("AppArmor support has been disabled because of lack of kernel support")
-	}
-
-	_, err = exec.LookPath("apparmor_parser")
-	if aaAvailable && err != nil {
-		aaAvailable = false
-		aaAdmin = false
+	} else if err != nil {
 		logger.Warnf("AppArmor support has been disabled because 'apparmor_parser' couldn't be found")
+	} else {
+		aaAvailable = true
 	}
 
-	/* Detect AppArmor admin support */
-	if aaAdmin && !haveMacAdmin() {
-		aaAdmin = false
-		logger.Warnf("Per-container AppArmor profiles are disabled because the mac_admin capability is missing.")
-	}
+	/* Detect AppArmor stacking support */
+	aaCanStack := func() bool {
+		contentBytes, err := ioutil.ReadFile("/sys/kernel/security/apparmor/features/domain/stack")
+		if err != nil {
+			return false
+		}
 
-	if aaAdmin && runningInUserns {
-		aaAdmin = false
-		logger.Warnf("Per-container AppArmor profiles are disabled because LXD is running in an unprivileged container.")
-	}
+		if string(contentBytes) != "yes\n" {
+			return false
+		}
 
-	/* Detect AppArmor confinment */
-	if !aaConfined {
-		profile := aaProfile()
-		if profile != "unconfined" && profile != "" {
-			aaConfined = true
-			logger.Warnf("Per-container AppArmor profiles are disabled because LXD is already protected by AppArmor.")
+		contentBytes, err = ioutil.ReadFile("/sys/kernel/security/apparmor/features/domain/version")
+		if err != nil {
+			return false
 		}
-	}
 
-	if aaAvailable {
-		canStack := func() bool {
-			contentBytes, err := ioutil.ReadFile("/sys/kernel/security/apparmor/features/domain/stack")
-			if err != nil {
-				return false
-			}
+		content := string(contentBytes)
 
-			if string(contentBytes) != "yes\n" {
-				return false
-			}
+		parts := strings.Split(strings.TrimSpace(content), ".")
 
-			contentBytes, err = ioutil.ReadFile("/sys/kernel/security/apparmor/features/domain/version")
-			if err != nil {
-				return false
-			}
-
-			content := string(contentBytes)
-
-			parts := strings.Split(strings.TrimSpace(content), ".")
+		if len(parts) == 0 {
+			logger.Warn("unknown apparmor domain version", log.Ctx{"version": content})
+			return false
+		}
 
-			if len(parts) == 0 {
-				logger.Warn("unknown apparmor domain version", log.Ctx{"version": content})
-				return false
-			}
+		major, err := strconv.Atoi(parts[0])
+		if err != nil {
+			logger.Warn("unknown apparmor domain version", log.Ctx{"version": content})
+			return false
+		}
 
-			major, err := strconv.Atoi(parts[0])
+		minor := 0
+		if len(parts) == 2 {
+			minor, err = strconv.Atoi(parts[1])
 			if err != nil {
 				logger.Warn("unknown apparmor domain version", log.Ctx{"version": content})
 				return false
 			}
+		}
 
-			minor := 0
-			if len(parts) == 2 {
-				minor, err = strconv.Atoi(parts[1])
-				if err != nil {
-					logger.Warn("unknown apparmor domain version", log.Ctx{"version": content})
-					return false
-				}
-			}
+		return major >= 1 && minor >= 2
+	}
+
+	aaStacking = aaCanStack()
+
+	/* Detect existing AppArmor stack */
+	if shared.PathExists("/sys/kernel/security/apparmor/.ns_stacked") {
+		contentBytes, err := ioutil.ReadFile("/sys/kernel/security/apparmor/.ns_stacked")
+		if err == nil && string(contentBytes) == "yes\n" {
+			aaStacked = true
+		}
+	}
 
-			return major >= 1 && minor >= 2
+	/* Detect AppArmor admin support */
+	if !haveMacAdmin() {
+		if aaAvailable {
+			logger.Warnf("Per-container AppArmor profiles are disabled because the mac_admin capability is missing.")
 		}
+	} else if runningInUserns && !aaStacked {
+		if aaAvailable {
+			logger.Warnf("Per-container AppArmor profiles are disabled because LXD is running in an unprivileged container without stacking.")
+		}
+	} else {
+		aaAdmin = true
+	}
 
-		aaStacking = canStack()
+	/* Detect AppArmor confinment */
+	profile := aaProfile()
+	if profile != "unconfined" && profile != "" {
+		if aaAvailable {
+			logger.Warnf("Per-container AppArmor profiles are disabled because LXD is already protected by AppArmor.")
+		}
+		aaConfined = true
 	}
 
 	/* Detect CGroup support */


More information about the lxc-devel mailing list