github.com/containerd/Containerd@v1.4.13/contrib/apparmor/template.go (about)

     1  // +build linux
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package apparmor
    20  
    21  import (
    22  	"bufio"
    23  	"fmt"
    24  	"io"
    25  	"os"
    26  	"os/exec"
    27  	"path"
    28  	"strconv"
    29  	"strings"
    30  	"text/template"
    31  
    32  	"github.com/pkg/errors"
    33  )
    34  
    35  const dir = "/etc/apparmor.d"
    36  
    37  const defaultTemplate = `
    38  {{range $value := .Imports}}
    39  {{$value}}
    40  {{end}}
    41  
    42  profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
    43  {{range $value := .InnerImports}}
    44    {{$value}}
    45  {{end}}
    46  
    47    network,
    48    capability,
    49    file,
    50    umount,
    51  
    52    deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
    53    # deny write to files not in /proc/<number>/** or /proc/sys/**
    54    deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
    55    deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
    56    deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
    57    deny @{PROC}/sysrq-trigger rwklx,
    58    deny @{PROC}/mem rwklx,
    59    deny @{PROC}/kmem rwklx,
    60    deny @{PROC}/kcore rwklx,
    61  
    62    deny mount,
    63  
    64    deny /sys/[^f]*/** wklx,
    65    deny /sys/f[^s]*/** wklx,
    66    deny /sys/fs/[^c]*/** wklx,
    67    deny /sys/fs/c[^g]*/** wklx,
    68    deny /sys/fs/cg[^r]*/** wklx,
    69    deny /sys/firmware/** rwklx,
    70    deny /sys/kernel/security/** rwklx,
    71  
    72  {{if ge .Version 208095}}
    73    ptrace (trace,read) peer={{.Name}},
    74  {{end}}
    75  }
    76  `
    77  
    78  type data struct {
    79  	Name         string
    80  	Imports      []string
    81  	InnerImports []string
    82  	Version      int
    83  }
    84  
    85  func loadData(name string) (*data, error) {
    86  	p := data{
    87  		Name: name,
    88  	}
    89  
    90  	if macroExists("tunables/global") {
    91  		p.Imports = append(p.Imports, "#include <tunables/global>")
    92  	} else {
    93  		p.Imports = append(p.Imports, "@{PROC}=/proc/")
    94  	}
    95  	if macroExists("abstractions/base") {
    96  		p.InnerImports = append(p.InnerImports, "#include <abstractions/base>")
    97  	}
    98  	ver, err := getVersion()
    99  	if err != nil {
   100  		return nil, errors.Wrap(err, "get apparmor_parser version")
   101  	}
   102  	p.Version = ver
   103  	return &p, nil
   104  }
   105  
   106  func generate(p *data, o io.Writer) error {
   107  	t, err := template.New("apparmor_profile").Parse(defaultTemplate)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	return t.Execute(o, p)
   112  }
   113  
   114  func load(path string) error {
   115  	out, err := aaParser("-Kr", path)
   116  	if err != nil {
   117  		return errors.Errorf("%s: %s", err, out)
   118  	}
   119  	return nil
   120  }
   121  
   122  // macrosExists checks if the passed macro exists.
   123  func macroExists(m string) bool {
   124  	_, err := os.Stat(path.Join(dir, m))
   125  	return err == nil
   126  }
   127  
   128  func aaParser(args ...string) (string, error) {
   129  	out, err := exec.Command("apparmor_parser", args...).CombinedOutput()
   130  	if err != nil {
   131  		return "", err
   132  	}
   133  	return string(out), nil
   134  }
   135  
   136  func getVersion() (int, error) {
   137  	out, err := aaParser("--version")
   138  	if err != nil {
   139  		return -1, err
   140  	}
   141  	return parseVersion(out)
   142  }
   143  
   144  // parseVersion takes the output from `apparmor_parser --version` and returns
   145  // a representation of the {major, minor, patch} version as a single number of
   146  // the form MMmmPPP {major, minor, patch}.
   147  func parseVersion(output string) (int, error) {
   148  	// output is in the form of the following:
   149  	// AppArmor parser version 2.9.1
   150  	// Copyright (C) 1999-2008 Novell Inc.
   151  	// Copyright 2009-2012 Canonical Ltd.
   152  
   153  	lines := strings.SplitN(output, "\n", 2)
   154  	words := strings.Split(lines[0], " ")
   155  	version := words[len(words)-1]
   156  
   157  	// split by major minor version
   158  	v := strings.Split(version, ".")
   159  	if len(v) == 0 || len(v) > 3 {
   160  		return -1, fmt.Errorf("parsing version failed for output: `%s`", output)
   161  	}
   162  
   163  	// Default the versions to 0.
   164  	var majorVersion, minorVersion, patchLevel int
   165  
   166  	majorVersion, err := strconv.Atoi(v[0])
   167  	if err != nil {
   168  		return -1, err
   169  	}
   170  
   171  	if len(v) > 1 {
   172  		minorVersion, err = strconv.Atoi(v[1])
   173  		if err != nil {
   174  			return -1, err
   175  		}
   176  	}
   177  	if len(v) > 2 {
   178  		patchLevel, err = strconv.Atoi(v[2])
   179  		if err != nil {
   180  			return -1, err
   181  		}
   182  	}
   183  
   184  	// major*10^5 + minor*10^3 + patch*10^0
   185  	numericVersion := majorVersion*1e5 + minorVersion*1e3 + patchLevel
   186  	return numericVersion, nil
   187  }
   188  
   189  func isLoaded(name string) (bool, error) {
   190  	f, err := os.Open("/sys/kernel/security/apparmor/profiles")
   191  	if err != nil {
   192  		return false, err
   193  	}
   194  	defer f.Close()
   195  	r := bufio.NewReader(f)
   196  	for {
   197  		p, err := r.ReadString('\n')
   198  		if err == io.EOF {
   199  			break
   200  		}
   201  		if err != nil {
   202  			return false, err
   203  		}
   204  		if strings.HasPrefix(p, name+" ") {
   205  			return true, nil
   206  		}
   207  	}
   208  	return false, nil
   209  }