github.com/lalkh/containerd@v1.4.3/contrib/apparmor/template.go (about) 1 // +build linux 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package apparmor 20 21 import ( 22 "bufio" 23 "fmt" 24 "io" 25 "os" 26 "os/exec" 27 "path" 28 "strconv" 29 "strings" 30 "text/template" 31 32 "github.com/pkg/errors" 33 ) 34 35 const dir = "/etc/apparmor.d" 36 37 const defaultTemplate = ` 38 {{range $value := .Imports}} 39 {{$value}} 40 {{end}} 41 42 profile {{.Name}} flags=(attach_disconnected,mediate_deleted) { 43 {{range $value := .InnerImports}} 44 {{$value}} 45 {{end}} 46 47 network, 48 capability, 49 file, 50 umount, 51 52 deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) 53 # deny write to files not in /proc/<number>/** or /proc/sys/** 54 deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w, 55 deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) 56 deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ 57 deny @{PROC}/sysrq-trigger rwklx, 58 deny @{PROC}/mem rwklx, 59 deny @{PROC}/kmem rwklx, 60 deny @{PROC}/kcore rwklx, 61 62 deny mount, 63 64 deny /sys/[^f]*/** wklx, 65 deny /sys/f[^s]*/** wklx, 66 deny /sys/fs/[^c]*/** wklx, 67 deny /sys/fs/c[^g]*/** wklx, 68 deny /sys/fs/cg[^r]*/** wklx, 69 deny /sys/firmware/** rwklx, 70 deny /sys/kernel/security/** rwklx, 71 72 {{if ge .Version 208095}} 73 ptrace (trace,read) peer={{.Name}}, 74 {{end}} 75 } 76 ` 77 78 type data struct { 79 Name string 80 Imports []string 81 InnerImports []string 82 Version int 83 } 84 85 func loadData(name string) (*data, error) { 86 p := data{ 87 Name: name, 88 } 89 90 if macroExists("tunables/global") { 91 p.Imports = append(p.Imports, "#include <tunables/global>") 92 } else { 93 p.Imports = append(p.Imports, "@{PROC}=/proc/") 94 } 95 if macroExists("abstractions/base") { 96 p.InnerImports = append(p.InnerImports, "#include <abstractions/base>") 97 } 98 ver, err := getVersion() 99 if err != nil { 100 return nil, errors.Wrap(err, "get apparmor_parser version") 101 } 102 p.Version = ver 103 return &p, nil 104 } 105 106 func generate(p *data, o io.Writer) error { 107 t, err := template.New("apparmor_profile").Parse(defaultTemplate) 108 if err != nil { 109 return err 110 } 111 return t.Execute(o, p) 112 } 113 114 func load(path string) error { 115 out, err := aaParser("-Kr", path) 116 if err != nil { 117 return errors.Errorf("%s: %s", err, out) 118 } 119 return nil 120 } 121 122 // macrosExists checks if the passed macro exists. 123 func macroExists(m string) bool { 124 _, err := os.Stat(path.Join(dir, m)) 125 return err == nil 126 } 127 128 func aaParser(args ...string) (string, error) { 129 out, err := exec.Command("apparmor_parser", args...).CombinedOutput() 130 if err != nil { 131 return "", err 132 } 133 return string(out), nil 134 } 135 136 func getVersion() (int, error) { 137 out, err := aaParser("--version") 138 if err != nil { 139 return -1, err 140 } 141 return parseVersion(out) 142 } 143 144 // parseVersion takes the output from `apparmor_parser --version` and returns 145 // a representation of the {major, minor, patch} version as a single number of 146 // the form MMmmPPP {major, minor, patch}. 147 func parseVersion(output string) (int, error) { 148 // output is in the form of the following: 149 // AppArmor parser version 2.9.1 150 // Copyright (C) 1999-2008 Novell Inc. 151 // Copyright 2009-2012 Canonical Ltd. 152 153 lines := strings.SplitN(output, "\n", 2) 154 words := strings.Split(lines[0], " ") 155 version := words[len(words)-1] 156 157 // split by major minor version 158 v := strings.Split(version, ".") 159 if len(v) == 0 || len(v) > 3 { 160 return -1, fmt.Errorf("parsing version failed for output: `%s`", output) 161 } 162 163 // Default the versions to 0. 164 var majorVersion, minorVersion, patchLevel int 165 166 majorVersion, err := strconv.Atoi(v[0]) 167 if err != nil { 168 return -1, err 169 } 170 171 if len(v) > 1 { 172 minorVersion, err = strconv.Atoi(v[1]) 173 if err != nil { 174 return -1, err 175 } 176 } 177 if len(v) > 2 { 178 patchLevel, err = strconv.Atoi(v[2]) 179 if err != nil { 180 return -1, err 181 } 182 } 183 184 // major*10^5 + minor*10^3 + patch*10^0 185 numericVersion := majorVersion*1e5 + minorVersion*1e3 + patchLevel 186 return numericVersion, nil 187 } 188 189 func isLoaded(name string) (bool, error) { 190 f, err := os.Open("/sys/kernel/security/apparmor/profiles") 191 if err != nil { 192 return false, err 193 } 194 defer f.Close() 195 r := bufio.NewReader(f) 196 for { 197 p, err := r.ReadString('\n') 198 if err == io.EOF { 199 break 200 } 201 if err != nil { 202 return false, err 203 } 204 if strings.HasPrefix(p, name+" ") { 205 return true, nil 206 } 207 } 208 return false, nil 209 }