github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/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}/* w, # deny write for all files directly in /proc (not in a subdir) 48 # deny write to files not in /proc/<number>/** or /proc/sys/** 49 deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w, 50 deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) 51 deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ 52 deny @{PROC}/sysrq-trigger rwklx, 53 deny @{PROC}/mem rwklx, 54 deny @{PROC}/kmem rwklx, 55 deny @{PROC}/kcore rwklx, 56 57 deny mount, 58 59 deny /sys/[^f]*/** wklx, 60 deny /sys/f[^s]*/** wklx, 61 deny /sys/fs/[^c]*/** wklx, 62 deny /sys/fs/c[^g]*/** wklx, 63 deny /sys/fs/cg[^r]*/** wklx, 64 deny /sys/firmware/efi/efivars/** rwklx, 65 deny /sys/kernel/security/** rwklx, 66 67 {{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}} 68 # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container 69 ptrace (trace,read) peer=docker-default, 70 {{end}}{{end}} 71 {{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}} 72 # docker daemon confinement requires explict allow rule for signal 73 signal (receive) set=(kill,term) peer={{.ExecPath}}, 74 {{end}}{{end}} 75 } 76 ` 77 78 func generateProfile(out io.Writer) error { 79 compiled, err := template.New("apparmor_profile").Parse(baseTemplate) 80 if err != nil { 81 return err 82 } 83 data := &data{ 84 Name: "docker-default", 85 } 86 if tunablesExists() { 87 data.Imports = append(data.Imports, "#include <tunables/global>") 88 } else { 89 data.Imports = append(data.Imports, "@{PROC}=/proc/") 90 } 91 if abstractionsExists() { 92 data.InnerImports = append(data.InnerImports, "#include <abstractions/base>") 93 } 94 data.MajorVersion, data.MinorVersion, err = aaparser.GetVersion() 95 if err != nil { 96 return err 97 } 98 data.ExecPath, err = exec.LookPath("docker") 99 if err != nil { 100 return err 101 } 102 if err := compiled.Execute(out, data); err != nil { 103 return err 104 } 105 return nil 106 } 107 108 // check if the tunables/global exist 109 func tunablesExists() bool { 110 _, err := os.Stat("/etc/apparmor.d/tunables/global") 111 return err == nil 112 } 113 114 // check if abstractions/base exist 115 func abstractionsExists() bool { 116 _, err := os.Stat("/etc/apparmor.d/abstractions/base") 117 return err == nil 118 } 119 120 func installAppArmorProfile() error { 121 if !apparmor.IsEnabled() { 122 return nil 123 } 124 125 // Make sure /etc/apparmor.d exists 126 if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil { 127 return err 128 } 129 130 f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 131 if err != nil { 132 return err 133 } 134 if err := generateProfile(f); err != nil { 135 f.Close() 136 return err 137 } 138 f.Close() 139 140 cmd := exec.Command("/sbin/apparmor_parser", "-r", "-W", "docker") 141 // to use the parser directly we have to make sure we are in the correct 142 // dir with the profile 143 cmd.Dir = "/etc/apparmor.d" 144 145 output, err := cmd.CombinedOutput() 146 if err != nil { 147 return fmt.Errorf("Error loading docker apparmor profile: %s (%s)", err, output) 148 } 149 return nil 150 } 151 152 func hasAppArmorProfileLoaded(profile string) error { 153 file, err := os.Open("/sys/kernel/security/apparmor/profiles") 154 if err != nil { 155 return err 156 } 157 r := bufio.NewReader(file) 158 for { 159 p, err := r.ReadString('\n') 160 if err != nil { 161 return err 162 } 163 if strings.HasPrefix(p, profile+" ") { 164 return nil 165 } 166 } 167 }