github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/apparmor/apparmor_linux.go (about)

     1  package apparmor
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"sync"
     8  
     9  	"github.com/opencontainers/runc/libcontainer/utils"
    10  )
    11  
    12  var (
    13  	appArmorEnabled bool
    14  	checkAppArmor   sync.Once
    15  )
    16  
    17  // isEnabled returns true if apparmor is enabled for the host.
    18  func isEnabled() bool {
    19  	checkAppArmor.Do(func() {
    20  		if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil {
    21  			buf, err := os.ReadFile("/sys/module/apparmor/parameters/enabled")
    22  			appArmorEnabled = err == nil && len(buf) > 1 && buf[0] == 'Y'
    23  		}
    24  	})
    25  	return appArmorEnabled
    26  }
    27  
    28  func setProcAttr(attr, value string) error {
    29  	attr = utils.CleanPath(attr)
    30  	attrSubPath := "attr/apparmor/" + attr
    31  	if _, err := os.Stat("/proc/self/" + attrSubPath); errors.Is(err, os.ErrNotExist) {
    32  		// fall back to the old convention
    33  		attrSubPath = "attr/" + attr
    34  	}
    35  
    36  	// Under AppArmor you can only change your own attr, so there's no reason
    37  	// to not use /proc/thread-self/ (instead of /proc/<tid>/, like libapparmor
    38  	// does).
    39  	attrPath, closer := utils.ProcThreadSelf(attrSubPath)
    40  	defer closer()
    41  
    42  	f, err := os.OpenFile(attrPath, os.O_WRONLY, 0)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	defer f.Close()
    47  
    48  	if err := utils.EnsureProcHandle(f); err != nil {
    49  		return err
    50  	}
    51  
    52  	_, err = f.WriteString(value)
    53  	return err
    54  }
    55  
    56  // changeOnExec reimplements aa_change_onexec from libapparmor in Go
    57  func changeOnExec(name string) error {
    58  	if err := setProcAttr("exec", "exec "+name); err != nil {
    59  		return fmt.Errorf("apparmor failed to apply profile: %w", err)
    60  	}
    61  	return nil
    62  }
    63  
    64  // applyProfile will apply the profile with the specified name to the process after
    65  // the next exec. It is only supported on Linux and produces an error on other
    66  // platforms.
    67  func applyProfile(name string) error {
    68  	if name == "" {
    69  		return nil
    70  	}
    71  
    72  	return changeOnExec(name)
    73  }