github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/daemon/execdriver/native/apparmor.go (about) 1 // +build linux 2 3 package native 4 5 import ( 6 "bufio" 7 "fmt" 8 "io" 9 "os" 10 "os/exec" 11 "path" 12 "strings" 13 "text/template" 14 15 "github.com/docker/docker/pkg/aaparser" 16 "github.com/opencontainers/runc/libcontainer/apparmor" 17 ) 18 19 const ( 20 apparmorProfilePath = "/etc/apparmor.d/docker" 21 ) 22 23 type data struct { 24 Name string 25 ExecPath string 26 Imports []string 27 InnerImports []string 28 MajorVersion int 29 MinorVersion int 30 } 31 32 const baseTemplate = ` 33 {{range $value := .Imports}} 34 {{$value}} 35 {{end}} 36 37 profile {{.Name}} flags=(attach_disconnected,mediate_deleted) { 38 {{range $value := .InnerImports}} 39 {{$value}} 40 {{end}} 41 42 network, 43 capability, 44 file, 45 umount, 46 47 deny @{PROC}/{*,**^[0-9]*,sys/kernel/shm*} wkx, 48 deny @{PROC}/sysrq-trigger rwklx, 49 deny @{PROC}/mem rwklx, 50 deny @{PROC}/kmem rwklx, 51 deny @{PROC}/kcore rwklx, 52 53 deny mount, 54 55 deny /sys/[^f]*/** wklx, 56 deny /sys/f[^s]*/** wklx, 57 deny /sys/fs/[^c]*/** wklx, 58 deny /sys/fs/c[^g]*/** wklx, 59 deny /sys/fs/cg[^r]*/** wklx, 60 deny /sys/firmware/efi/efivars/** rwklx, 61 deny /sys/kernel/security/** rwklx, 62 63 {{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}} 64 # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container 65 ptrace (trace,read) peer=docker-default, 66 {{end}}{{end}} 67 {{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} 68 # docker daemon confinement requires explict allow rule for signal 69 signal (receive) set=(kill,term) peer={{.ExecPath}}, 70 {{end}}{{end}} 71 } 72 ` 73 74 func generateProfile(out io.Writer) error { 75 compiled, err := template.New("apparmor_profile").Parse(baseTemplate) 76 if err != nil { 77 return err 78 } 79 data := &data{ 80 Name: "docker-default", 81 } 82 if tunablesExists() { 83 data.Imports = append(data.Imports, "#include <tunables/global>") 84 } else { 85 data.Imports = append(data.Imports, "@{PROC}=/proc/") 86 } 87 if abstractionsExists() { 88 data.InnerImports = append(data.InnerImports, "#include <abstractions/base>") 89 } 90 data.MajorVersion, data.MinorVersion, err = aaparser.GetVersion() 91 if err != nil { 92 return err 93 } 94 data.ExecPath, err = exec.LookPath("docker") 95 if err != nil { 96 return err 97 } 98 if err := compiled.Execute(out, data); err != nil { 99 return err 100 } 101 return nil 102 } 103 104 // check if the tunables/global exist 105 func tunablesExists() bool { 106 _, err := os.Stat("/etc/apparmor.d/tunables/global") 107 return err == nil 108 } 109 110 // check if abstractions/base exist 111 func abstractionsExists() bool { 112 _, err := os.Stat("/etc/apparmor.d/abstractions/base") 113 return err == nil 114 } 115 116 func installAppArmorProfile() error { 117 if !apparmor.IsEnabled() { 118 return nil 119 } 120 121 // Make sure /etc/apparmor.d exists 122 if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil { 123 return err 124 } 125 126 f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 127 if err != nil { 128 return err 129 } 130 if err := generateProfile(f); err != nil { 131 f.Close() 132 return err 133 } 134 f.Close() 135 136 cmd := exec.Command("/sbin/apparmor_parser", "-r", "-W", "docker") 137 // to use the parser directly we have to make sure we are in the correct 138 // dir with the profile 139 cmd.Dir = "/etc/apparmor.d" 140 141 output, err := cmd.CombinedOutput() 142 if err != nil { 143 return fmt.Errorf("Error loading docker apparmor profile: %s (%s)", err, output) 144 } 145 return nil 146 } 147 148 func hasAppArmorProfileLoaded(profile string) error { 149 file, err := os.Open("/sys/kernel/security/apparmor/profiles") 150 if err != nil { 151 return err 152 } 153 r := bufio.NewReader(file) 154 for { 155 p, err := r.ReadString('\n') 156 if err != nil { 157 return err 158 } 159 if strings.HasPrefix(p, profile+" ") { 160 return nil 161 } 162 } 163 }