115 lines
2.9 KiB
Go
115 lines
2.9 KiB
Go
package supported
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/containers/storage/pkg/unshare"
|
|
runcaa "github.com/opencontainers/runc/libcontainer/apparmor"
|
|
"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 "", fmt.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)
|
|
}
|