package apparmor import ( "errors" "fmt" "os" "sync" "golang.org/x/sys/unix" "github.com/opencontainers/runc/internal/pathrs" ) var ( appArmorEnabled bool checkAppArmor sync.Once ) // isEnabled returns true if apparmor is enabled for the host. func isEnabled() bool { checkAppArmor.Do(func() { if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil { buf, err := os.ReadFile("/sys/module/apparmor/parameters/enabled") appArmorEnabled = err == nil && len(buf) > 1 && buf[0] == 'Y' } }) return appArmorEnabled } func setProcAttr(attr, value string) error { attr = pathrs.LexicallyCleanPath(attr) attrSubPath := "attr/apparmor/" + attr if _, err := os.Stat("/proc/self/" + attrSubPath); errors.Is(err, os.ErrNotExist) { // fall back to the old convention attrSubPath = "attr/" + attr } // Under AppArmor you can only change your own attr, so there's no reason // to not use /proc/thread-self/ (instead of /proc//, like libapparmor // does). f, closer, err := pathrs.ProcThreadSelfOpen(attrSubPath, unix.O_WRONLY|unix.O_CLOEXEC) if err != nil { return err } defer closer() defer f.Close() _, err = f.WriteString(value) return err } // changeOnExec reimplements aa_change_onexec from libapparmor in Go. func changeOnExec(name string) error { if err := setProcAttr("exec", "exec "+name); err != nil { return fmt.Errorf("apparmor failed to apply profile: %w", err) } return nil } // applyProfile will apply the profile with the specified name to the process // after the next exec. func applyProfile(name string) error { if name == "" { return nil } return changeOnExec(name) }