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