File: supported.go

package info (click to toggle)
golang-github-containers-common 0.33.4%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 856 kB
  • sloc: makefile: 118; sh: 25
file content (113 lines) | stat: -rw-r--r-- 2,977 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package supported

import (
	"os"
	"os/exec"
	"path/filepath"
	"sync"

	"github.com/containers/storage/pkg/unshare"
	runcaa "github.com/opencontainers/runc/libcontainer/apparmor"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate

// ApparmorVerifier is the global struct for verifying if AppAmor is available
// on the system.
type ApparmorVerifier struct {
	impl             verifierImpl
	parserBinaryPath string
}

var (
	singleton *ApparmorVerifier
	once      sync.Once
)

// NewAppArmorVerifier can be used to retrieve a new ApparmorVerifier instance.
func NewAppArmorVerifier() *ApparmorVerifier {
	once.Do(func() {
		singleton = &ApparmorVerifier{impl: &defaultVerifier{}}
	})
	return singleton
}

// IsSupported returns nil if AppAmor is supported by the host system.
// The method will error if:
// - the process runs in rootless mode
// - AppArmor is disabled by the host system
// - the `apparmor_parser` binary is not discoverable
func (a *ApparmorVerifier) IsSupported() error {
	if a.impl.UnshareIsRootless() {
		return errors.New("AppAmor is not supported on rootless containers")
	}
	if !a.impl.RuncIsEnabled() {
		return errors.New("AppArmor not supported by the host system")
	}

	_, err := a.FindAppArmorParserBinary()
	return err
}

// FindAppArmorParserBinary returns the `apparmor_parser` binary either from
// `/sbin` or from `$PATH`. It returns an error if the binary could not be
// found.
func (a *ApparmorVerifier) FindAppArmorParserBinary() (string, error) {
	// Use the memoized path if available
	if a.parserBinaryPath != "" {
		logrus.Debugf("Using %s binary", a.parserBinaryPath)
		return a.parserBinaryPath, nil
	}

	const (
		binary = "apparmor_parser"
		sbin   = "/sbin"
	)

	// `/sbin` is not always in `$PATH`, so we check it explicitly
	sbinBinaryPath := filepath.Join(sbin, binary)
	if _, err := a.impl.OsStat(sbinBinaryPath); err == nil {
		logrus.Debugf("Found %s binary in %s", binary, sbinBinaryPath)
		a.parserBinaryPath = sbinBinaryPath
		return sbinBinaryPath, nil
	}

	// Fallback to checking $PATH
	if path, err := a.impl.ExecLookPath(binary); err == nil {
		logrus.Debugf("Found %s binary in %s", binary, path)
		a.parserBinaryPath = path
		return path, nil
	}

	return "", errors.Errorf(
		"%s binary neither found in %s nor $PATH", binary, sbin,
	)
}

//counterfeiter:generate . verifierImpl
type verifierImpl interface {
	UnshareIsRootless() bool
	RuncIsEnabled() bool
	OsStat(name string) (os.FileInfo, error)
	ExecLookPath(file string) (string, error)
}

type defaultVerifier struct{}

func (d *defaultVerifier) UnshareIsRootless() bool {
	return unshare.IsRootless()
}

func (d *defaultVerifier) RuncIsEnabled() bool {
	return runcaa.IsEnabled()
}

func (d *defaultVerifier) OsStat(name string) (os.FileInfo, error) {
	return os.Stat(name)
}

func (d *defaultVerifier) ExecLookPath(file string) (string, error) {
	return exec.LookPath(file)
}