github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/profiles/apparmor/apparmor.go (about) 1 // +build linux 2 3 package apparmor // import "github.com/demonoid81/moby/profiles/apparmor" 4 5 import ( 6 "bufio" 7 "io" 8 "io/ioutil" 9 "os" 10 "path" 11 "strings" 12 "text/template" 13 14 "github.com/demonoid81/moby/pkg/aaparser" 15 ) 16 17 var ( 18 // profileDirectory is the file store for apparmor profiles and macros. 19 profileDirectory = "/etc/apparmor.d" 20 ) 21 22 // profileData holds information about the given profile for generation. 23 type profileData struct { 24 // Name is profile name. 25 Name string 26 // DaemonProfile is the profile name of our daemon. 27 DaemonProfile string 28 // Imports defines the apparmor functions to import, before defining the profile. 29 Imports []string 30 // InnerImports defines the apparmor functions to import in the profile. 31 InnerImports []string 32 // Version is the {major, minor, patch} version of apparmor_parser as a single number. 33 Version int 34 } 35 36 // generateDefault creates an apparmor profile from ProfileData. 37 func (p *profileData) generateDefault(out io.Writer) error { 38 compiled, err := template.New("apparmor_profile").Parse(baseTemplate) 39 if err != nil { 40 return err 41 } 42 43 if macroExists("tunables/global") { 44 p.Imports = append(p.Imports, "#include <tunables/global>") 45 } else { 46 p.Imports = append(p.Imports, "@{PROC}=/proc/") 47 } 48 49 if macroExists("abstractions/base") { 50 p.InnerImports = append(p.InnerImports, "#include <abstractions/base>") 51 } 52 53 ver, err := aaparser.GetVersion() 54 if err != nil { 55 return err 56 } 57 p.Version = ver 58 59 return compiled.Execute(out, p) 60 } 61 62 // macrosExists checks if the passed macro exists. 63 func macroExists(m string) bool { 64 _, err := os.Stat(path.Join(profileDirectory, m)) 65 return err == nil 66 } 67 68 // InstallDefault generates a default profile in a temp directory determined by 69 // os.TempDir(), then loads the profile into the kernel using 'apparmor_parser'. 70 func InstallDefault(name string) error { 71 p := profileData{ 72 Name: name, 73 } 74 75 // Figure out the daemon profile. 76 currentProfile, err := ioutil.ReadFile("/proc/self/attr/current") 77 if err != nil { 78 // If we couldn't get the daemon profile, assume we are running 79 // unconfined which is generally the default. 80 currentProfile = nil 81 } 82 daemonProfile := string(currentProfile) 83 // Normally profiles are suffixed by " (enforcing)" or similar. AppArmor 84 // profiles cannot contain spaces so this doesn't restrict daemon profile 85 // names. 86 if parts := strings.SplitN(daemonProfile, " ", 2); len(parts) >= 1 { 87 daemonProfile = parts[0] 88 } 89 if daemonProfile == "" { 90 daemonProfile = "unconfined" 91 } 92 p.DaemonProfile = daemonProfile 93 94 // Install to a temporary directory. 95 f, err := ioutil.TempFile("", name) 96 if err != nil { 97 return err 98 } 99 profilePath := f.Name() 100 101 defer f.Close() 102 defer os.Remove(profilePath) 103 104 if err := p.generateDefault(f); err != nil { 105 return err 106 } 107 108 return aaparser.LoadProfile(profilePath) 109 } 110 111 // IsLoaded checks if a profile with the given name has been loaded into the 112 // kernel. 113 func IsLoaded(name string) (bool, error) { 114 file, err := os.Open("/sys/kernel/security/apparmor/profiles") 115 if err != nil { 116 return false, err 117 } 118 defer file.Close() 119 120 r := bufio.NewReader(file) 121 for { 122 p, err := r.ReadString('\n') 123 if err == io.EOF { 124 break 125 } 126 if err != nil { 127 return false, err 128 } 129 if strings.HasPrefix(p, name+" ") { 130 return true, nil 131 } 132 } 133 134 return false, nil 135 }