support multi-image (docker) archives

Support loading and saving tarballs with more than one image.
Add a new `/libpod/images/export` endpoint to the rest API to
allow for exporting/saving multiple images into an archive.

Note that a non-release version of containers/image is vendored.
A release version must be vendored before cutting a new Podman
release.  We force the containers/image version via a replace in
the go.mod file; this way go won't try to match the versions.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2020-07-31 09:27:21 +02:00
parent be7778df6c
commit 7fea46752c
115 changed files with 3790 additions and 1680 deletions

View File

@ -1,25 +1,92 @@
// NOTE: this package has originally been copied from
// github.com/opencontainers/runc and modified to work for other use cases
package seccomp
import "fmt"
import (
"fmt"
var goArchToSeccompArchMap = map[string]Arch{
"386": ArchX86,
"amd64": ArchX86_64,
"amd64p32": ArchX32,
"arm": ArchARM,
"arm64": ArchAARCH64,
"mips": ArchMIPS,
"mips64": ArchMIPS64,
"mips64le": ArchMIPSEL64,
"mips64p32": ArchMIPS64N32,
"mips64p32le": ArchMIPSEL64N32,
"mipsle": ArchMIPSEL,
"ppc": ArchPPC,
"ppc64": ArchPPC64,
"ppc64le": ArchPPC64LE,
"s390": ArchS390,
"s390x": ArchS390X,
}
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
var (
goArchToSeccompArchMap = map[string]Arch{
"386": ArchX86,
"amd64": ArchX86_64,
"amd64p32": ArchX32,
"arm": ArchARM,
"arm64": ArchAARCH64,
"mips": ArchMIPS,
"mips64": ArchMIPS64,
"mips64le": ArchMIPSEL64,
"mips64p32": ArchMIPS64N32,
"mips64p32le": ArchMIPSEL64N32,
"mipsle": ArchMIPSEL,
"ppc": ArchPPC,
"ppc64": ArchPPC64,
"ppc64le": ArchPPC64LE,
"s390": ArchS390,
"s390x": ArchS390X,
}
specArchToLibseccompArchMap = map[specs.Arch]string{
specs.ArchX86: "x86",
specs.ArchX86_64: "amd64",
specs.ArchX32: "x32",
specs.ArchARM: "arm",
specs.ArchAARCH64: "arm64",
specs.ArchMIPS: "mips",
specs.ArchMIPS64: "mips64",
specs.ArchMIPS64N32: "mips64n32",
specs.ArchMIPSEL: "mipsel",
specs.ArchMIPSEL64: "mipsel64",
specs.ArchMIPSEL64N32: "mipsel64n32",
specs.ArchPPC: "ppc",
specs.ArchPPC64: "ppc64",
specs.ArchPPC64LE: "ppc64le",
specs.ArchS390: "s390",
specs.ArchS390X: "s390x",
}
specArchToSeccompArchMap = map[specs.Arch]Arch{
specs.ArchX86: ArchX86,
specs.ArchX86_64: ArchX86_64,
specs.ArchX32: ArchX32,
specs.ArchARM: ArchARM,
specs.ArchAARCH64: ArchAARCH64,
specs.ArchMIPS: ArchMIPS,
specs.ArchMIPS64: ArchMIPS64,
specs.ArchMIPS64N32: ArchMIPS64N32,
specs.ArchMIPSEL: ArchMIPSEL,
specs.ArchMIPSEL64: ArchMIPSEL64,
specs.ArchMIPSEL64N32: ArchMIPSEL64N32,
specs.ArchPPC: ArchPPC,
specs.ArchPPC64: ArchPPC64,
specs.ArchPPC64LE: ArchPPC64LE,
specs.ArchS390: ArchS390,
specs.ArchS390X: ArchS390X,
}
specActionToSeccompActionMap = map[specs.LinuxSeccompAction]Action{
specs.ActKill: ActKill,
// TODO: wait for this PR to get merged:
// https://github.com/opencontainers/runtime-spec/pull/1064
// specs.ActKillProcess ActKillProcess,
// specs.ActKillThread ActKillThread,
specs.ActErrno: ActErrno,
specs.ActTrap: ActTrap,
specs.ActAllow: ActAllow,
specs.ActTrace: ActTrace,
specs.ActLog: ActLog,
}
specOperatorToSeccompOperatorMap = map[specs.LinuxSeccompOperator]Operator{
specs.OpNotEqual: OpNotEqual,
specs.OpLessThan: OpLessThan,
specs.OpLessEqual: OpLessEqual,
specs.OpEqualTo: OpEqualTo,
specs.OpGreaterEqual: OpGreaterEqual,
specs.OpGreaterThan: OpGreaterThan,
specs.OpMaskedEqual: OpMaskedEqual,
}
)
// GoArchToSeccompArch converts a runtime.GOARCH to a seccomp `Arch`. The
// function returns an error if the architecture conversion is not supported.
@ -30,3 +97,100 @@ func GoArchToSeccompArch(goArch string) (Arch, error) {
}
return arch, nil
}
// specToSeccomp converts a `LinuxSeccomp` spec into a `Seccomp` struct.
func specToSeccomp(spec *specs.LinuxSeccomp) (*Seccomp, error) {
res := &Seccomp{
Syscalls: []*Syscall{},
}
for _, arch := range spec.Architectures {
newArch, err := specArchToSeccompArch(arch)
if err != nil {
return nil, errors.Wrap(err, "convert spec arch")
}
res.Architectures = append(res.Architectures, newArch)
}
// Convert default action
newDefaultAction, err := specActionToSeccompAction(spec.DefaultAction)
if err != nil {
return nil, errors.Wrap(err, "convert default action")
}
res.DefaultAction = newDefaultAction
// Loop through all syscall blocks and convert them to the internal format
for _, call := range spec.Syscalls {
newAction, err := specActionToSeccompAction(call.Action)
if err != nil {
return nil, errors.Wrap(err, "convert action")
}
for _, name := range call.Names {
newCall := Syscall{
Name: name,
Action: newAction,
ErrnoRet: call.ErrnoRet,
Args: []*Arg{},
}
// Loop through all the arguments of the syscall and convert them
for _, arg := range call.Args {
newOp, err := specOperatorToSeccompOperator(arg.Op)
if err != nil {
return nil, errors.Wrap(err, "convert operator")
}
newArg := Arg{
Index: arg.Index,
Value: arg.Value,
ValueTwo: arg.ValueTwo,
Op: newOp,
}
newCall.Args = append(newCall.Args, &newArg)
}
res.Syscalls = append(res.Syscalls, &newCall)
}
}
return res, nil
}
// specArchToLibseccompArch converts a spec arch into a libseccomp one.
func specArchToLibseccompArch(arch specs.Arch) (string, error) {
if res, ok := specArchToLibseccompArchMap[arch]; ok {
return res, nil
}
return "", errors.Errorf(
"architecture %q is not valid for libseccomp", arch,
)
}
// specArchToSeccompArch converts a spec arch into an internal one.
func specArchToSeccompArch(arch specs.Arch) (Arch, error) {
if res, ok := specArchToSeccompArchMap[arch]; ok {
return res, nil
}
return "", errors.Errorf("architecture %q is not valid", arch)
}
// specActionToSeccompAction converts a spec action into a seccomp one.
func specActionToSeccompAction(action specs.LinuxSeccompAction) (Action, error) {
if res, ok := specActionToSeccompActionMap[action]; ok {
return res, nil
}
return "", errors.Errorf(
"spec action %q is not valid internal action", action,
)
}
// specOperatorToSeccompOperator converts a spec operator into a seccomp one.
func specOperatorToSeccompOperator(operator specs.LinuxSeccompOperator) (Operator, error) {
if op, ok := specOperatorToSeccompOperatorMap[operator]; ok {
return op, nil
}
return "", errors.Errorf(
"spec operator %q is not a valid internal operator", operator,
)
}

View File

@ -0,0 +1,237 @@
// +build seccomp
// NOTE: this package has originally been copied from
// github.com/opencontainers/runc and modified to work for other use cases
package seccomp
import (
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
libseccomp "github.com/seccomp/libseccomp-golang"
"golang.org/x/sys/unix"
)
// NOTE: this package has originally been copied from
// github.com/opencontainers/runc and modified to work for other use cases
var (
// ErrSpecNil is a possible return error from BuildFilter() and occurs if
// the provided spec is nil.
ErrSpecNil = errors.New("spec is nil")
// ErrSpecEmpty is a possible return error from BuildFilter() and occurs if
// the provided spec has neither a DefaultAction nor any syscalls.
ErrSpecEmpty = errors.New("spec contains neither a default action nor any syscalls")
)
// BuildFilter does a basic validation for the provided seccomp profile
// string and returns a filter for it.
func BuildFilter(spec *specs.LinuxSeccomp) (*libseccomp.ScmpFilter, error) {
// Sanity checking to allow consumers to act accordingly
if spec == nil {
return nil, ErrSpecNil
}
if spec.DefaultAction == "" && len(spec.Syscalls) == 0 {
return nil, ErrSpecEmpty
}
profile, err := specToSeccomp(spec)
if err != nil {
return nil, errors.Wrap(err, "convert spec to seccomp profile")
}
defaultAction, err := toAction(profile.DefaultAction, nil)
if err != nil {
return nil, errors.Wrapf(err, "convert default action %s", profile.DefaultAction)
}
filter, err := libseccomp.NewFilter(defaultAction)
if err != nil {
return nil, errors.Wrapf(err, "create filter for default action %s", defaultAction)
}
// Add extra architectures
for _, arch := range spec.Architectures {
libseccompArch, err := specArchToLibseccompArch(arch)
if err != nil {
return nil, errors.Wrap(err, "convert spec arch")
}
scmpArch, err := libseccomp.GetArchFromString(libseccompArch)
if err != nil {
return nil, errors.Wrapf(err, "validate Seccomp architecture %s", arch)
}
if err := filter.AddArch(scmpArch); err != nil {
return nil, errors.Wrap(err, "add architecture to seccomp filter")
}
}
// Unset no new privs bit
if err := filter.SetNoNewPrivsBit(false); err != nil {
return nil, errors.Wrap(err, "set no new privileges flag")
}
// Add a rule for each syscall
for _, call := range profile.Syscalls {
if call == nil {
return nil, errors.New("encountered nil syscall while initializing seccomp")
}
if err = matchSyscall(filter, call); err != nil {
return nil, errors.Wrap(err, "filter matches syscall")
}
}
return filter, nil
}
func matchSyscall(filter *libseccomp.ScmpFilter, call *Syscall) error {
if call == nil || filter == nil {
return errors.New("cannot use nil as syscall to block")
}
if call.Name == "" {
return errors.New("empty string is not a valid syscall")
}
// If we can't resolve the syscall, assume it's not supported on this kernel
// Ignore it, don't error out
callNum, err := libseccomp.GetSyscallFromName(call.Name)
if err != nil {
return nil
}
// Convert the call's action to the libseccomp equivalent
callAct, err := toAction(call.Action, call.ErrnoRet)
if err != nil {
return errors.Wrapf(err, "convert action %s", call.Action)
}
// Unconditional match - just add the rule
if len(call.Args) == 0 {
if err = filter.AddRule(callNum, callAct); err != nil {
return errors.Wrapf(err, "add seccomp filter rule for syscall %s", call.Name)
}
} else {
// Linux system calls can have at most 6 arguments
const syscallMaxArguments int = 6
// If two or more arguments have the same condition,
// Revert to old behavior, adding each condition as a separate rule
argCounts := make([]uint, syscallMaxArguments)
conditions := []libseccomp.ScmpCondition{}
for _, cond := range call.Args {
newCond, err := toCondition(cond)
if err != nil {
return errors.Wrapf(err, "create seccomp syscall condition for syscall %s", call.Name)
}
argCounts[cond.Index] += 1
conditions = append(conditions, newCond)
}
hasMultipleArgs := false
for _, count := range argCounts {
if count > 1 {
hasMultipleArgs = true
break
}
}
if hasMultipleArgs {
// Revert to old behavior
// Add each condition attached to a separate rule
for _, cond := range conditions {
condArr := []libseccomp.ScmpCondition{cond}
if err = filter.AddRuleConditional(callNum, callAct, condArr); err != nil {
return errors.Wrapf(err, "add seccomp rule for syscall %s", call.Name)
}
}
} else if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil {
// No conditions share same argument
// Use new, proper behavior
return errors.Wrapf(err, "add seccomp rule for syscall %s", call.Name)
}
}
return nil
}
// toAction converts an internal `Action` type to a `libseccomp.ScmpAction`
// type.
func toAction(act Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
switch act {
case ActKill:
return libseccomp.ActKill, nil
case ActKillProcess:
return libseccomp.ActKillProcess, nil
case ActErrno:
if errnoRet != nil {
return libseccomp.ActErrno.SetReturnCode(int16(*errnoRet)), nil
}
return libseccomp.ActErrno.SetReturnCode(int16(unix.EPERM)), nil
case ActTrap:
return libseccomp.ActTrap, nil
case ActAllow:
return libseccomp.ActAllow, nil
case ActTrace:
if errnoRet != nil {
return libseccomp.ActTrace.SetReturnCode(int16(*errnoRet)), nil
}
return libseccomp.ActTrace.SetReturnCode(int16(unix.EPERM)), nil
case ActLog:
return libseccomp.ActLog, nil
default:
return libseccomp.ActInvalid, errors.Errorf("invalid action %s", act)
}
}
// toCondition converts an internal `Arg` type to a `libseccomp.ScmpCondition`
// type.
func toCondition(arg *Arg) (cond libseccomp.ScmpCondition, err error) {
if arg == nil {
return cond, errors.New("cannot convert nil to syscall condition")
}
op, err := toCompareOp(arg.Op)
if err != nil {
return cond, errors.Wrap(err, "convert compare operator")
}
condition, err := libseccomp.MakeCondition(
arg.Index, op, arg.Value, arg.ValueTwo,
)
if err != nil {
return cond, errors.Wrap(err, "make condition")
}
return condition, nil
}
// toCompareOp converts an internal `Operator` type to a
// `libseccomp.ScmpCompareOp`.
func toCompareOp(op Operator) (libseccomp.ScmpCompareOp, error) {
switch op {
case OpEqualTo:
return libseccomp.CompareEqual, nil
case OpNotEqual:
return libseccomp.CompareNotEqual, nil
case OpGreaterThan:
return libseccomp.CompareGreater, nil
case OpGreaterEqual:
return libseccomp.CompareGreaterEqual, nil
case OpLessThan:
return libseccomp.CompareLess, nil
case OpLessEqual:
return libseccomp.CompareLessOrEqual, nil
case OpMaskedEqual:
return libseccomp.CompareMaskedEqual, nil
default:
return libseccomp.CompareInvalid, errors.Errorf("invalid operator %s", op)
}
}

View File

@ -122,7 +122,7 @@ Loop:
}
if len(call.Excludes.Caps) > 0 {
for _, c := range call.Excludes.Caps {
if inSlice(rs.Process.Capabilities.Bounding, c) {
if rs != nil && rs.Process != nil && rs.Process.Capabilities != nil && inSlice(rs.Process.Capabilities.Bounding, c) {
continue Loop
}
}
@ -134,7 +134,7 @@ Loop:
}
if len(call.Includes.Caps) > 0 {
for _, c := range call.Includes.Caps {
if !inSlice(rs.Process.Capabilities.Bounding, c) {
if rs != nil && rs.Process != nil && rs.Process.Capabilities != nil && !inSlice(rs.Process.Capabilities.Bounding, c) {
continue Loop
}
}

View File

@ -0,0 +1,29 @@
// +build seccomp
package seccomp
import (
"encoding/json"
"github.com/pkg/errors"
)
// ValidateProfile does a basic validation for the provided seccomp profile
// string.
func ValidateProfile(content string) error {
profile := &Seccomp{}
if err := json.Unmarshal([]byte(content), &profile); err != nil {
return errors.Wrap(err, "decoding seccomp profile")
}
spec, err := setupSeccomp(profile, nil)
if err != nil {
return errors.Wrap(err, "create seccomp spec")
}
if _, err := BuildFilter(spec); err != nil {
return errors.Wrap(err, "build seccomp filter")
}
return nil
}

View File

@ -1,4 +1,4 @@
package version
// Version is the version of the build.
const Version = "0.20.4-dev"
const Version = "0.21.0"