Index: docker-1.10.1/contrib/apparmor/main.go =================================================================== --- docker-1.10.1.orig/contrib/apparmor/main.go +++ docker-1.10.1/contrib/apparmor/main.go @@ -11,8 +11,7 @@ import ( ) type profileData struct { - MajorVersion int - MinorVersion int + Version int } func main() { @@ -23,13 +22,12 @@ func main() { // parse the arg apparmorProfilePath := os.Args[1] - majorVersion, minorVersion, err := aaparser.GetVersion() + version, err := aaparser.GetVersion() if err != nil { log.Fatal(err) } data := profileData{ - MajorVersion: majorVersion, - MinorVersion: minorVersion, + Version: version, } fmt.Printf("apparmor_parser is of version %+v\n", data) Index: docker-1.10.1/daemon/execdriver/native/apparmor.go =================================================================== --- docker-1.10.1.orig/daemon/execdriver/native/apparmor.go +++ docker-1.10.1/daemon/execdriver/native/apparmor.go @@ -25,8 +25,7 @@ type data struct { ExecPath string Imports []string InnerImports []string - MajorVersion int - MinorVersion int + Version int } const baseTemplate = ` @@ -64,14 +63,17 @@ profile {{.Name}} flags=(attach_disconne deny /sys/firmware/efi/efivars/** rwklx, deny /sys/kernel/security/** rwklx, -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}} +{{if ge .Version 208095}} + # apparmor-2.8.95 is Ubuntu 14.04 LTS (Trusty Tahr) + # apparmor-2.8.95 is apparmor-2.9 beta, which supports ptrace rule + # other apparmor-2.8 versions do not support this rule # 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}} +{{end}} +{{if ge .Version 209000}} # docker daemon confinement requires explict allow rule for signal signal (receive) set=(kill,term) peer={{.ExecPath}}, -{{end}}{{end}} +{{end}} } ` @@ -91,7 +93,7 @@ func generateProfile(out io.Writer) erro if abstractionsExists() { data.InnerImports = append(data.InnerImports, "#include ") } - data.MajorVersion, data.MinorVersion, err = aaparser.GetVersion() + data.Version, err = aaparser.GetVersion() if err != nil { return err } Index: docker-1.10.1/pkg/aaparser/aaparser.go =================================================================== --- docker-1.10.1.orig/pkg/aaparser/aaparser.go +++ docker-1.10.1/pkg/aaparser/aaparser.go @@ -1,45 +1,92 @@ +// Package aaparser is a convenience package interacting with `apparmor_parser`. package aaparser import ( "fmt" - "log" "os/exec" + "path/filepath" "strconv" "strings" ) -// GetVersion returns the major and minor version of apparmor_parser -func GetVersion() (int, int, error) { - // get the apparmor_version version - cmd := exec.Command("apparmor_parser", "--version") +const ( + binary = "apparmor_parser" +) + +// GetVersion returns the major and minor version of apparmor_parser. +func GetVersion() (int, error) { + output, err := cmd("", "--version") + if err != nil { + return -1, err + } + + return parseVersion(output) +} - output, err := cmd.CombinedOutput() +// LoadProfile runs `apparmor_parser -r -W` on a specified apparmor profile to +// replace and write it to disk. +func LoadProfile(profilePath string) error { + _, err := cmd(filepath.Dir(profilePath), "-r", "-W", filepath.Base(profilePath)) if err != nil { - log.Fatalf("getting apparmor_parser version failed: %s (%s)", err, output) + return err } + return nil +} + +// cmd runs `apparmor_parser` with the passed arguments. +func cmd(dir string, arg ...string) (string, error) { + c := exec.Command(binary, arg...) + c.Dir = dir - // parse the version from the output + output, err := c.CombinedOutput() + if err != nil { + return "", fmt.Errorf("running `%s %s` failed with output: %s\nerror: %v", c.Path, strings.Join(c.Args, " "), string(output), err) + } + + return string(output), nil +} + +// parseVersion takes the output from `apparmor_parser --version` and returns +// a representation of the {major, minor, patch} version as a single number of +// the form MMmmPPP {major, minor, patch}. +func parseVersion(output string) (int, error) { // output is in the form of the following: // AppArmor parser version 2.9.1 // Copyright (C) 1999-2008 Novell Inc. // Copyright 2009-2012 Canonical Ltd. - lines := strings.SplitN(string(output), "\n", 2) + + lines := strings.SplitN(output, "\n", 2) words := strings.Split(lines[0], " ") version := words[len(words)-1] + // split by major minor version v := strings.Split(version, ".") - if len(v) < 2 { - return -1, -1, fmt.Errorf("parsing major minor version failed for %q", version) + if len(v) == 0 || len(v) > 3 { + return -1, fmt.Errorf("parsing version failed for output: `%s`", output) } + // Default the versions to 0. + var majorVersion, minorVersion, patchLevel int + majorVersion, err := strconv.Atoi(v[0]) if err != nil { - return -1, -1, err + return -1, err } - minorVersion, err := strconv.Atoi(v[1]) - if err != nil { - return -1, -1, err + + if len(v) > 1 { + minorVersion, err = strconv.Atoi(v[1]) + if err != nil { + return -1, err + } + } + if len(v) > 2 { + patchLevel, err = strconv.Atoi(v[2]) + if err != nil { + return -1, err + } } - return majorVersion, minorVersion, nil + // major*10^5 + minor*10^3 + patch*10^0 + numericVersion := majorVersion*1e5 + minorVersion*1e3 + patchLevel + return numericVersion, nil } Index: docker-1.10.1/contrib/apparmor/template.go =================================================================== --- docker-1.10.1.orig/contrib/apparmor/template.go +++ docker-1.10.1/contrib/apparmor/template.go @@ -20,11 +20,11 @@ profile /usr/bin/docker (attach_disconne umount, pivot_root, -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} +{{if ge .Version 209000}} signal (receive) peer=@{profile_name}, signal (receive) peer=unconfined, signal (send), -{{end}}{{end}} +{{end}} network, capability, owner /** rw, @@ -46,12 +46,12 @@ profile /usr/bin/docker (attach_disconne /etc/ld.so.cache r, /etc/passwd r, -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} +{{if ge .Version 209000}} ptrace peer=@{profile_name}, ptrace (read) peer=docker-default, deny ptrace (trace) peer=docker-default, deny ptrace peer=/usr/bin/docker///bin/ps, -{{end}}{{end}} +{{end}} /usr/lib/** rm, /lib/** rm, @@ -72,11 +72,11 @@ profile /usr/bin/docker (attach_disconne /sbin/zfs rCx, /sbin/apparmor_parser rCx, -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} +{{if ge .Version 209000}} # Transitions change_profile -> docker-*, change_profile -> unconfined, -{{end}}{{end}} +{{end}} profile /bin/cat (complain) { /etc/ld.so.cache r, @@ -98,10 +98,10 @@ profile /usr/bin/docker (attach_disconne /dev/null rw, /bin/ps mr, -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} +{{if ge .Version 209000}} # We don't need ptrace so we'll deny and ignore the error. deny ptrace (read, trace), -{{end}}{{end}} +{{end}} # Quiet dac_override denials deny capability dac_override, @@ -119,15 +119,15 @@ profile /usr/bin/docker (attach_disconne /proc/tty/drivers r, } profile /sbin/iptables (complain) { -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} +{{if ge .Version 209000}} signal (receive) peer=/usr/bin/docker, -{{end}}{{end}} +{{end}} capability net_admin, } profile /sbin/auplink flags=(attach_disconnected, complain) { -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} +{{if ge .Version 209000}} signal (receive) peer=/usr/bin/docker, -{{end}}{{end}} +{{end}} capability sys_admin, capability dac_override, @@ -146,9 +146,9 @@ profile /usr/bin/docker (attach_disconne /proc/[0-9]*/mounts rw, } profile /sbin/modprobe /bin/kmod (complain) { -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} +{{if ge .Version 209000}} signal (receive) peer=/usr/bin/docker, -{{end}}{{end}} +{{end}} capability sys_module, /etc/ld.so.cache r, /lib/** rm, @@ -162,9 +162,9 @@ profile /usr/bin/docker (attach_disconne } # xz works via pipes, so we do not need access to the filesystem. profile /usr/bin/xz (complain) { -{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} +{{if ge .Version 209000}} signal (receive) peer=/usr/bin/docker, -{{end}}{{end}} +{{end}} /etc/ld.so.cache r, /lib/** rm, /usr/bin/xz rm,