Switch to containers/common for seccomp

The seccomp/containers-golang library is not maintained any more and we
should stick to containers/common.

Signed-off-by: Sascha Grunert <sgrunert@suse.com>
This commit is contained in:
Sascha Grunert
2020-08-27 21:14:55 +02:00
parent 72c5b35ea5
commit 98ead36531
48 changed files with 2604 additions and 221 deletions

View File

@@ -13,6 +13,7 @@ import (
"strings"
"text/template"
"github.com/containers/common/pkg/apparmor/internal/supported"
"github.com/containers/storage/pkg/unshare"
runcaa "github.com/opencontainers/runc/libcontainer/apparmor"
"github.com/pkg/errors"
@@ -22,12 +23,11 @@ import (
// profileDirectory is the file store for apparmor profiles and macros.
var profileDirectory = "/etc/apparmor.d"
// IsEnabled returns true if AppArmor is enabled on the host.
// IsEnabled returns true if AppArmor is enabled on the host. It also checks
// for the existence of the `apparmor_parser` binary, which will be required to
// apply profiles.
func IsEnabled() bool {
if unshare.IsRootless() {
return false
}
return runcaa.IsEnabled()
return supported.NewAppArmorVerifier().IsSupported() == nil
}
// profileData holds information about the given profile for generation.
@@ -43,7 +43,7 @@ type profileData struct {
}
// generateDefault creates an apparmor profile from ProfileData.
func (p *profileData) generateDefault(out io.Writer) error {
func (p *profileData) generateDefault(apparmorParserPath string, out io.Writer) error {
compiled, err := template.New("apparmor_profile").Parse(defaultProfileTemplate)
if err != nil {
return errors.Wrap(err, "create AppArmor profile from template")
@@ -59,7 +59,7 @@ func (p *profileData) generateDefault(out io.Writer) error {
p.InnerImports = append(p.InnerImports, "#include <abstractions/base>")
}
ver, err := getAAParserVersion()
ver, err := getAAParserVersion(apparmorParserPath)
if err != nil {
return errors.Wrap(err, "get AppArmor version")
}
@@ -85,18 +85,23 @@ func InstallDefault(name string) error {
Name: name,
}
cmd := exec.Command("apparmor_parser", "-Kr")
apparmorParserPath, err := supported.NewAppArmorVerifier().FindAppArmorParserBinary()
if err != nil {
return errors.Wrap(err, "find `apparmor_parser` binary")
}
cmd := exec.Command(apparmorParserPath, "-Kr")
pipe, err := cmd.StdinPipe()
if err != nil {
return errors.Wrap(err, "execute apparmor_parser")
return errors.Wrapf(err, "execute %s", apparmorParserPath)
}
if err := cmd.Start(); err != nil {
if pipeErr := pipe.Close(); pipeErr != nil {
logrus.Errorf("unable to close AppArmor pipe: %q", pipeErr)
}
return errors.Wrap(err, "start apparmor_parser command")
return errors.Wrapf(err, "start %s command", apparmorParserPath)
}
if err := p.generateDefault(pipe); err != nil {
if err := p.generateDefault(apparmorParserPath, pipe); err != nil {
if pipeErr := pipe.Close(); pipeErr != nil {
logrus.Errorf("unable to close AppArmor pipe: %q", pipeErr)
}
@@ -118,11 +123,17 @@ func InstallDefault(name string) error {
// generation fails.
func DefaultContent(name string) ([]byte, error) {
p := profileData{Name: name}
var bytes bytes.Buffer
if err := p.generateDefault(&bytes); err != nil {
buffer := &bytes.Buffer{}
apparmorParserPath, err := supported.NewAppArmorVerifier().FindAppArmorParserBinary()
if err != nil {
return nil, errors.Wrap(err, "find `apparmor_parser` binary")
}
if err := p.generateDefault(apparmorParserPath, buffer); err != nil {
return nil, errors.Wrap(err, "generate default AppAmor profile")
}
return bytes.Bytes(), nil
return buffer.Bytes(), nil
}
// IsLoaded checks if a profile with the given name has been loaded into the
@@ -159,8 +170,8 @@ func IsLoaded(name string) (bool, error) {
}
// execAAParser runs `apparmor_parser` with the passed arguments.
func execAAParser(dir string, args ...string) (string, error) {
c := exec.Command("apparmor_parser", args...)
func execAAParser(apparmorParserPath, dir string, args ...string) (string, error) {
c := exec.Command(apparmorParserPath, args...)
c.Dir = dir
output, err := c.Output()
@@ -172,8 +183,8 @@ func execAAParser(dir string, args ...string) (string, error) {
}
// getAAParserVersion returns the major and minor version of apparmor_parser.
func getAAParserVersion() (int, error) {
output, err := execAAParser("", "--version")
func getAAParserVersion(apparmorParserPath string) (int, error) {
output, err := execAAParser(apparmorParserPath, "", "--version")
if err != nil {
return -1, errors.Wrap(err, "execute apparmor_parser")
}

View File

@@ -0,0 +1,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)
}