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)
}
|