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