github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/daemon/execdriver/native/apparmor.go (about)

     1  // +build linux
     2  
     3  package native
     4  
     5  import (
     6  	"bufio"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"os/exec"
    11  	"path"
    12  	"strings"
    13  	"text/template"
    14  
    15  	"github.com/docker/docker/pkg/aaparser"
    16  	"github.com/opencontainers/runc/libcontainer/apparmor"
    17  )
    18  
    19  const (
    20  	apparmorProfilePath = "/etc/apparmor.d/docker"
    21  )
    22  
    23  type data struct {
    24  	Name         string
    25  	ExecPath     string
    26  	Imports      []string
    27  	InnerImports []string
    28  	MajorVersion int
    29  	MinorVersion int
    30  }
    31  
    32  const baseTemplate = `
    33  {{range $value := .Imports}}
    34  {{$value}}
    35  {{end}}
    36  
    37  profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
    38  {{range $value := .InnerImports}}
    39    {{$value}}
    40  {{end}}
    41  
    42    network,
    43    capability,
    44    file,
    45    umount,
    46  
    47    deny @{PROC}/{*,**^[0-9]*,sys/kernel/shm*} wkx,
    48    deny @{PROC}/sysrq-trigger rwklx,
    49    deny @{PROC}/mem rwklx,
    50    deny @{PROC}/kmem rwklx,
    51    deny @{PROC}/kcore rwklx,
    52  
    53    deny mount,
    54  
    55    deny /sys/[^f]*/** wklx,
    56    deny /sys/f[^s]*/** wklx,
    57    deny /sys/fs/[^c]*/** wklx,
    58    deny /sys/fs/c[^g]*/** wklx,
    59    deny /sys/fs/cg[^r]*/** wklx,
    60    deny /sys/firmware/efi/efivars/** rwklx,
    61    deny /sys/kernel/security/** rwklx,
    62  
    63  {{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}}
    64    # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
    65    ptrace (trace,read) peer=docker-default,
    66  {{end}}{{end}}
    67  {{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    68    # docker daemon confinement requires explict allow rule for signal
    69    signal (receive) set=(kill,term) peer={{.ExecPath}},
    70  {{end}}{{end}}
    71  }
    72  `
    73  
    74  func generateProfile(out io.Writer) error {
    75  	compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	data := &data{
    80  		Name: "docker-default",
    81  	}
    82  	if tunablesExists() {
    83  		data.Imports = append(data.Imports, "#include <tunables/global>")
    84  	} else {
    85  		data.Imports = append(data.Imports, "@{PROC}=/proc/")
    86  	}
    87  	if abstractionsExists() {
    88  		data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
    89  	}
    90  	data.MajorVersion, data.MinorVersion, err = aaparser.GetVersion()
    91  	if err != nil {
    92  		return err
    93  	}
    94  	data.ExecPath, err = exec.LookPath("docker")
    95  	if err != nil {
    96  		return err
    97  	}
    98  	if err := compiled.Execute(out, data); err != nil {
    99  		return err
   100  	}
   101  	return nil
   102  }
   103  
   104  // check if the tunables/global exist
   105  func tunablesExists() bool {
   106  	_, err := os.Stat("/etc/apparmor.d/tunables/global")
   107  	return err == nil
   108  }
   109  
   110  // check if abstractions/base exist
   111  func abstractionsExists() bool {
   112  	_, err := os.Stat("/etc/apparmor.d/abstractions/base")
   113  	return err == nil
   114  }
   115  
   116  func installAppArmorProfile() error {
   117  	if !apparmor.IsEnabled() {
   118  		return nil
   119  	}
   120  
   121  	// Make sure /etc/apparmor.d exists
   122  	if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
   123  		return err
   124  	}
   125  
   126  	f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	if err := generateProfile(f); err != nil {
   131  		f.Close()
   132  		return err
   133  	}
   134  	f.Close()
   135  
   136  	cmd := exec.Command("/sbin/apparmor_parser", "-r", "-W", "docker")
   137  	// to use the parser directly we have to make sure we are in the correct
   138  	// dir with the profile
   139  	cmd.Dir = "/etc/apparmor.d"
   140  
   141  	output, err := cmd.CombinedOutput()
   142  	if err != nil {
   143  		return fmt.Errorf("Error loading docker apparmor profile: %s (%s)", err, output)
   144  	}
   145  	return nil
   146  }
   147  
   148  func hasAppArmorProfileLoaded(profile string) error {
   149  	file, err := os.Open("/sys/kernel/security/apparmor/profiles")
   150  	if err != nil {
   151  		return err
   152  	}
   153  	r := bufio.NewReader(file)
   154  	for {
   155  		p, err := r.ReadString('\n')
   156  		if err != nil {
   157  			return err
   158  		}
   159  		if strings.HasPrefix(p, profile+" ") {
   160  			return nil
   161  		}
   162  	}
   163  }