123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- // +build linux
- package native
- import (
- "bufio"
- "fmt"
- "io"
- "os"
- "os/exec"
- "path"
- "strings"
- "text/template"
- "github.com/opencontainers/runc/libcontainer/apparmor"
- )
- const (
- apparmorProfilePath = "/etc/apparmor.d/docker"
- )
- type data struct {
- Name string
- Imports []string
- InnerImports []string
- }
- const baseTemplate = `
- {{range $value := .Imports}}
- {{$value}}
- {{end}}
- profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
- {{range $value := .InnerImports}}
- {{$value}}
- {{end}}
- network,
- capability,
- file,
- umount,
- signal (receive) peer=/usr/bin/docker,
- signal (receive) peer=docker-unconfined,
- deny @{PROC}/sys/fs/** wklx,
- deny @{PROC}/fs/** wklx,
- deny @{PROC}/sysrq-trigger rwklx,
- deny @{PROC}/mem rwklx,
- deny @{PROC}/kmem rwklx,
- deny @{PROC}/kore rwklx,
- deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
- deny @{PROC}/sys/kernel/*/** wklx,
- deny mount,
- deny ptrace,
- deny /sys/[^f]*/** wklx,
- deny /sys/f[^s]*/** wklx,
- deny /sys/fs/[^c]*/** wklx,
- deny /sys/fs/c[^g]*/** wklx,
- deny /sys/fs/cg[^r]*/** wklx,
- deny /sys/firmware/efi/efivars/** rwklx,
- deny /sys/kernel/security/** rwklx,
- }
- profile docker-unconfined flags=(attach_disconnected,mediate_deleted,complain) {
- #include <abstractions/base>
- network,
- capability,
- file,
- umount,
- mount,
- pivot_root,
- change_profile -> *,
- ptrace,
- signal,
- }
- `
- func generateProfile(out io.Writer) error {
- compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
- if err != nil {
- return err
- }
- data := &data{
- Name: "docker-default",
- }
- if tunablesExists() {
- data.Imports = append(data.Imports, "#include <tunables/global>")
- } else {
- data.Imports = append(data.Imports, "@{PROC}=/proc/")
- }
- if abstractionsExists() {
- data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
- }
- if err := compiled.Execute(out, data); err != nil {
- return err
- }
- return nil
- }
- // check if the tunables/global exist
- func tunablesExists() bool {
- _, err := os.Stat("/etc/apparmor.d/tunables/global")
- return err == nil
- }
- // check if abstractions/base exist
- func abstractionsExists() bool {
- _, err := os.Stat("/etc/apparmor.d/abstractions/base")
- return err == nil
- }
- func installAppArmorProfile() error {
- if !apparmor.IsEnabled() {
- return nil
- }
- // Make sure /etc/apparmor.d exists
- if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
- return err
- }
- f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
- if err != nil {
- return err
- }
- if err := generateProfile(f); err != nil {
- f.Close()
- return err
- }
- f.Close()
- cmd := exec.Command("/sbin/apparmor_parser", "-r", "-W", "docker")
- // to use the parser directly we have to make sure we are in the correct
- // dir with the profile
- cmd.Dir = "/etc/apparmor.d"
- output, err := cmd.CombinedOutput()
- if err != nil {
- return fmt.Errorf("Error loading docker apparmor profile: %s (%s)", err, output)
- }
- return nil
- }
- func hasAppArmorProfileLoaded(profile string) error {
- file, err := os.Open("/sys/kernel/security/apparmor/profiles")
- if err != nil {
- return err
- }
- r := bufio.NewReader(file)
- for {
- p, err := r.ReadString('\n')
- if err != nil {
- return err
- }
- if strings.HasPrefix(p, profile+" ") {
- return nil
- }
- }
- }
|