github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/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}/* w,   # deny write for all files directly in /proc (not in a subdir)
    48    # deny write to files not in /proc/<number>/** or /proc/sys/**
    49    deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
    50    deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
    51    deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
    52    deny @{PROC}/sysrq-trigger rwklx,
    53    deny @{PROC}/mem rwklx,
    54    deny @{PROC}/kmem rwklx,
    55    deny @{PROC}/kcore rwklx,
    56  
    57    deny mount,
    58  
    59    deny /sys/[^f]*/** wklx,
    60    deny /sys/f[^s]*/** wklx,
    61    deny /sys/fs/[^c]*/** wklx,
    62    deny /sys/fs/c[^g]*/** wklx,
    63    deny /sys/fs/cg[^r]*/** wklx,
    64    deny /sys/firmware/efi/efivars/** rwklx,
    65    deny /sys/kernel/security/** rwklx,
    66  
    67  {{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}}
    68    # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
    69    ptrace (trace,read) peer=docker-default,
    70  {{end}}{{end}}
    71  {{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    72    # docker daemon confinement requires explict allow rule for signal
    73    signal (receive) set=(kill,term) peer={{.ExecPath}},
    74  {{end}}{{end}}
    75  }
    76  `
    77  
    78  func generateProfile(out io.Writer) error {
    79  	compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	data := &data{
    84  		Name: "docker-default",
    85  	}
    86  	if tunablesExists() {
    87  		data.Imports = append(data.Imports, "#include <tunables/global>")
    88  	} else {
    89  		data.Imports = append(data.Imports, "@{PROC}=/proc/")
    90  	}
    91  	if abstractionsExists() {
    92  		data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
    93  	}
    94  	data.MajorVersion, data.MinorVersion, err = aaparser.GetVersion()
    95  	if err != nil {
    96  		return err
    97  	}
    98  	data.ExecPath, err = exec.LookPath("docker")
    99  	if err != nil {
   100  		return err
   101  	}
   102  	if err := compiled.Execute(out, data); err != nil {
   103  		return err
   104  	}
   105  	return nil
   106  }
   107  
   108  // check if the tunables/global exist
   109  func tunablesExists() bool {
   110  	_, err := os.Stat("/etc/apparmor.d/tunables/global")
   111  	return err == nil
   112  }
   113  
   114  // check if abstractions/base exist
   115  func abstractionsExists() bool {
   116  	_, err := os.Stat("/etc/apparmor.d/abstractions/base")
   117  	return err == nil
   118  }
   119  
   120  func installAppArmorProfile() error {
   121  	if !apparmor.IsEnabled() {
   122  		return nil
   123  	}
   124  
   125  	// Make sure /etc/apparmor.d exists
   126  	if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
   127  		return err
   128  	}
   129  
   130  	f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	if err := generateProfile(f); err != nil {
   135  		f.Close()
   136  		return err
   137  	}
   138  	f.Close()
   139  
   140  	cmd := exec.Command("/sbin/apparmor_parser", "-r", "-W", "docker")
   141  	// to use the parser directly we have to make sure we are in the correct
   142  	// dir with the profile
   143  	cmd.Dir = "/etc/apparmor.d"
   144  
   145  	output, err := cmd.CombinedOutput()
   146  	if err != nil {
   147  		return fmt.Errorf("Error loading docker apparmor profile: %s (%s)", err, output)
   148  	}
   149  	return nil
   150  }
   151  
   152  func hasAppArmorProfileLoaded(profile string) error {
   153  	file, err := os.Open("/sys/kernel/security/apparmor/profiles")
   154  	if err != nil {
   155  		return err
   156  	}
   157  	r := bufio.NewReader(file)
   158  	for {
   159  		p, err := r.ReadString('\n')
   160  		if err != nil {
   161  			return err
   162  		}
   163  		if strings.HasPrefix(p, profile+" ") {
   164  			return nil
   165  		}
   166  	}
   167  }