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