github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/profiles/apparmor/apparmor.go (about)

     1  // +build linux
     2  
     3  package apparmor // import "github.com/docker/docker/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/docker/docker/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  }