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