ソースを参照

move default apparmor policy into package

Signed-off-by: Jessica Frazelle <acidburn@docker.com>
Jessica Frazelle 9 年 前
コミット
35e50119fc

+ 0 - 161
daemon/execdriver/native/apparmor.go

@@ -1,161 +0,0 @@
-// +build linux
-
-package native
-
-import (
-	"bufio"
-	"io"
-	"os"
-	"os/exec"
-	"path"
-	"strings"
-	"text/template"
-
-	"github.com/docker/docker/pkg/aaparser"
-	"github.com/opencontainers/runc/libcontainer/apparmor"
-)
-
-const (
-	apparmorProfilePath = "/etc/apparmor.d/docker"
-)
-
-type data struct {
-	Name         string
-	ExecPath     string
-	Imports      []string
-	InnerImports []string
-	MajorVersion int
-	MinorVersion int
-}
-
-const baseTemplate = `
-{{range $value := .Imports}}
-{{$value}}
-{{end}}
-
-profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
-{{range $value := .InnerImports}}
-  {{$value}}
-{{end}}
-
-  network,
-  capability,
-  file,
-  umount,
-
-  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
-  # deny write to files not in /proc/<number>/** or /proc/sys/**
-  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
-  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
-  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
-  deny @{PROC}/sysrq-trigger rwklx,
-  deny @{PROC}/mem rwklx,
-  deny @{PROC}/kmem rwklx,
-  deny @{PROC}/kcore rwklx,
-
-  deny mount,
-
-  deny /sys/[^f]*/** wklx,
-  deny /sys/f[^s]*/** wklx,
-  deny /sys/fs/[^c]*/** wklx,
-  deny /sys/fs/c[^g]*/** wklx,
-  deny /sys/fs/cg[^r]*/** wklx,
-  deny /sys/firmware/efi/efivars/** rwklx,
-  deny /sys/kernel/security/** rwklx,
-
-{{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}}
-  # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
-  ptrace (trace,read) peer=docker-default,
-{{end}}{{end}}
-{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
-  # docker daemon confinement requires explict allow rule for signal
-  signal (receive) set=(kill,term) peer={{.ExecPath}},
-{{end}}{{end}}
-}
-`
-
-func generateProfile(out io.Writer) error {
-	compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
-	if err != nil {
-		return err
-	}
-	data := &data{
-		Name: "docker-default",
-	}
-	if tunablesExists() {
-		data.Imports = append(data.Imports, "#include <tunables/global>")
-	} else {
-		data.Imports = append(data.Imports, "@{PROC}=/proc/")
-	}
-	if abstractionsExists() {
-		data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
-	}
-	data.MajorVersion, data.MinorVersion, err = aaparser.GetVersion()
-	if err != nil {
-		return err
-	}
-	data.ExecPath, err = exec.LookPath("docker")
-	if err != nil {
-		return err
-	}
-	if err := compiled.Execute(out, data); err != nil {
-		return err
-	}
-	return nil
-}
-
-// check if the tunables/global exist
-func tunablesExists() bool {
-	_, err := os.Stat("/etc/apparmor.d/tunables/global")
-	return err == nil
-}
-
-// check if abstractions/base exist
-func abstractionsExists() bool {
-	_, err := os.Stat("/etc/apparmor.d/abstractions/base")
-	return err == nil
-}
-
-func installAppArmorProfile() error {
-	if !apparmor.IsEnabled() {
-		return nil
-	}
-
-	// Make sure /etc/apparmor.d exists
-	if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
-		return err
-	}
-
-	f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
-	if err != nil {
-		return err
-	}
-	if err := generateProfile(f); err != nil {
-		f.Close()
-		return err
-	}
-	f.Close()
-
-	if err := aaparser.LoadProfile(apparmorProfilePath); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func hasAppArmorProfileLoaded(profile string) error {
-	file, err := os.Open("/sys/kernel/security/apparmor/profiles")
-	if err != nil {
-		return err
-	}
-	r := bufio.NewReader(file)
-	for {
-		p, err := r.ReadString('\n')
-		if err != nil {
-			return err
-		}
-		if strings.HasPrefix(p, profile+" ") {
-			return nil
-		}
-	}
-}

+ 6 - 3
daemon/execdriver/native/driver.go

@@ -21,6 +21,7 @@ import (
 	"github.com/docker/docker/pkg/reexec"
 	sysinfo "github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/term"
+	aaprofile "github.com/docker/docker/profiles/apparmor"
 	"github.com/opencontainers/runc/libcontainer"
 	"github.com/opencontainers/runc/libcontainer/apparmor"
 	"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
@@ -33,6 +34,8 @@ import (
 const (
 	DriverName = "native"
 	Version    = "0.2"
+
+	defaultApparmorProfile = "docker-default"
 )
 
 // Driver contains all information for native driver,
@@ -57,13 +60,13 @@ func NewDriver(root string, options []string) (*Driver, error) {
 	}
 
 	if apparmor.IsEnabled() {
-		if err := installAppArmorProfile(); err != nil {
-			apparmorProfiles := []string{"docker-default"}
+		if err := aaprofile.InstallDefault(defaultApparmorProfile); err != nil {
+			apparmorProfiles := []string{defaultApparmorProfile}
 
 			// Allow daemon to run if loading failed, but are active
 			// (possibly through another run, manually, or via system startup)
 			for _, policy := range apparmorProfiles {
-				if err := hasAppArmorProfileLoaded(policy); err != nil {
+				if err := aaprofile.IsLoaded(policy); err != nil {
 					return nil, fmt.Errorf("AppArmor enabled on system but the %s profile could not be loaded.", policy)
 				}
 			}

+ 110 - 0
profiles/apparmor/apparmor.go

@@ -0,0 +1,110 @@
+// +build linux
+
+package apparmor
+
+import (
+	"bufio"
+	"io"
+	"os"
+	"path"
+	"strings"
+	"text/template"
+
+	"github.com/docker/docker/pkg/aaparser"
+)
+
+var (
+	// profileDirectory is the file store for apparmor profiles and macros.
+	profileDirectory = "/etc/apparmor.d"
+	// defaultProfilePath is the default path for the apparmor profile to be saved.
+	defaultProfilePath = path.Join(profileDirectory, "docker")
+)
+
+// profileData holds information about the given profile for generation.
+type profileData struct {
+	// Name is profile name.
+	Name string
+	// ExecPath is the path to the docker binary.
+	ExecPath string
+	// Imports defines the apparmor functions to import, before defining the profile.
+	Imports []string
+	// InnerImports defines the apparmor functions to import in the profile.
+	InnerImports []string
+	// MajorVersion is the apparmor_parser major version.
+	MajorVersion int
+	// MinorVersion is the apparmor_parser minor version.
+	MinorVersion int
+}
+
+// generateDefault creates an apparmor profile from ProfileData.
+func (p *profileData) generateDefault(out io.Writer) error {
+	compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
+	if err != nil {
+		return err
+	}
+	if macroExists("tunables/global") {
+		p.Imports = append(p.Imports, "#include <tunables/global>")
+	} else {
+		p.Imports = append(p.Imports, "@{PROC}=/proc/")
+	}
+	if macroExists("abstractions/base") {
+		p.InnerImports = append(p.InnerImports, "#include <abstractions/base>")
+	}
+	if err := compiled.Execute(out, p); err != nil {
+		return err
+	}
+	return nil
+}
+
+// macrosExists checks if the passed macro exists.
+func macroExists(m string) bool {
+	_, err := os.Stat(path.Join(profileDirectory, m))
+	return err == nil
+}
+
+// InstallDefault generates a default profile and installs it in the
+// ProfileDirectory with `apparmor_parser`.
+func InstallDefault(name string) error {
+	// Make sure the path where they want to save the profile exists
+	if err := os.MkdirAll(profileDirectory, 0755); err != nil {
+		return err
+	}
+
+	p := profileData{
+		Name: name,
+	}
+
+	f, err := os.OpenFile(defaultProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+	if err != nil {
+		return err
+	}
+	if err := p.generateDefault(f); err != nil {
+		f.Close()
+		return err
+	}
+	f.Close()
+
+	if err := aaparser.LoadProfile(defaultProfilePath); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// IsLoaded checks if a passed profile as been loaded into the kernel.
+func IsLoaded(name string) error {
+	file, err := os.Open("/sys/kernel/security/apparmor/profiles")
+	if err != nil {
+		return err
+	}
+	r := bufio.NewReader(file)
+	for {
+		p, err := r.ReadString('\n')
+		if err != nil {
+			return err
+		}
+		if strings.HasPrefix(p, name+" ") {
+			return nil
+		}
+	}
+}

+ 50 - 0
profiles/apparmor/template.go

@@ -0,0 +1,50 @@
+// +build linux
+
+package apparmor
+
+// baseTemplate defines the default apparmor profile for containers.
+const baseTemplate = `
+{{range $value := .Imports}}
+{{$value}}
+{{end}}
+
+profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
+{{range $value := .InnerImports}}
+  {{$value}}
+{{end}}
+
+  network,
+  capability,
+  file,
+  umount,
+
+  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
+  # deny write to files not in /proc/<number>/** or /proc/sys/**
+  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
+  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
+  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
+  deny @{PROC}/sysrq-trigger rwklx,
+  deny @{PROC}/mem rwklx,
+  deny @{PROC}/kmem rwklx,
+  deny @{PROC}/kcore rwklx,
+
+  deny mount,
+
+  deny /sys/[^f]*/** wklx,
+  deny /sys/f[^s]*/** wklx,
+  deny /sys/fs/[^c]*/** wklx,
+  deny /sys/fs/c[^g]*/** wklx,
+  deny /sys/fs/cg[^r]*/** wklx,
+  deny /sys/firmware/efi/efivars/** rwklx,
+  deny /sys/kernel/security/** rwklx,
+
+{{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}}
+  # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
+  ptrace (trace,read) peer=docker-default,
+{{end}}{{end}}
+{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
+  # docker daemon confinement requires explict allow rule for signal
+  signal (receive) set=(kill,term) peer={{.ExecPath}},
+{{end}}{{end}}
+}
+`