podman load: support downloading files

Support downloading files, for instance via
`podman load -i server.com/image.tar`.  The specified URL is downloaded
in the frontend and stored as a temp file that gets passed down to the
backend.

Also vendor in c/common@main to use the new `pkg/download`.

Fixes: #11970
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2021-11-10 15:39:29 +01:00
parent 5437568fcd
commit 1ef66d6d7f
18 changed files with 276 additions and 78 deletions

View File

@ -1,46 +0,0 @@
package libimage
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"github.com/pkg/errors"
)
// tmpdir returns a path to a temporary directory.
func tmpdir() string {
tmpdir := os.Getenv("TMPDIR")
if tmpdir == "" {
tmpdir = "/var/tmp"
}
return tmpdir
}
// downloadFromURL downloads an image in the format "https:/example.com/myimage.tar"
// and temporarily saves in it $TMPDIR/importxyz, which is deleted after the image is imported
func (r *Runtime) downloadFromURL(source string) (string, error) {
fmt.Printf("Downloading from %q\n", source)
outFile, err := ioutil.TempFile(r.systemContext.BigFilesTemporaryDir, "import")
if err != nil {
return "", errors.Wrap(err, "error creating file")
}
defer outFile.Close()
response, err := http.Get(source) // nolint:noctx
if err != nil {
return "", errors.Wrapf(err, "error downloading %q", source)
}
defer response.Body.Close()
_, err = io.Copy(outFile, response.Body)
if err != nil {
return "", errors.Wrapf(err, "error saving %s to %s", source, outFile.Name())
}
return outFile.Name(), nil
}

View File

@ -2,9 +2,11 @@ package libimage
import (
"context"
"fmt"
"net/url"
"os"
"github.com/containers/common/pkg/download"
storageTransport "github.com/containers/image/v5/storage"
tarballTransport "github.com/containers/image/v5/tarball"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
@ -61,7 +63,8 @@ func (r *Runtime) Import(ctx context.Context, path string, options *ImportOption
u, err := url.ParseRequestURI(path)
if err == nil && u.Scheme != "" {
// If source is a URL, download the file.
file, err := r.downloadFromURL(path)
fmt.Printf("Downloading from %q\n", path)
file, err := download.FromURL(r.systemContext.BigFilesTemporaryDir, path)
if err != nil {
return "", err
}

View File

@ -21,6 +21,16 @@ import (
// Faster than the standard library, see https://github.com/json-iterator/go.
var json = jsoniter.ConfigCompatibleWithStandardLibrary
// tmpdir returns a path to a temporary directory.
func tmpdir() string {
tmpdir := os.Getenv("TMPDIR")
if tmpdir == "" {
tmpdir = "/var/tmp"
}
return tmpdir
}
// RuntimeOptions allow for creating a customized Runtime.
type RuntimeOptions struct {
// The base system context of the runtime which will be used throughout

View File

@ -461,6 +461,10 @@ type SetOptions struct {
// NetworkConfig represents the "network" TOML config table
type NetworkConfig struct {
// NetworkBackend determines what backend should be used for Podman's
// networking.
NetworkBackend string `toml:"network_backend,omitempty"`
// CNIPluginDirs is where CNI plugin binaries are stored.
CNIPluginDirs []string `toml:"cni_plugin_dirs,omitempty"`

View File

@ -260,6 +260,10 @@ default_sysctls = [
[network]
# Network backend to use. Default "CNI".
#
#network_backend = "cni"
# Path to directory where CNI plugin binaries are located.
#
#cni_plugin_dirs = [

View File

@ -201,6 +201,7 @@ func DefaultConfig() (*Config, error) {
UserNSSize: DefaultUserNSSize,
},
Network: NetworkConfig{
NetworkBackend: "cni",
DefaultNetwork: "podman",
DefaultSubnet: DefaultSubnet,
NetworkConfigDir: cniConfig,

View File

@ -0,0 +1,31 @@
package download
import (
"fmt"
"io"
"io/ioutil"
"net/http"
)
// FromURL downloads the specified source to a file in tmpdir (OS defaults if
// empty).
func FromURL(tmpdir, source string) (string, error) {
tmp, err := ioutil.TempFile(tmpdir, "")
if err != nil {
return "", fmt.Errorf("creating temporary download file: %w", err)
}
defer tmp.Close()
response, err := http.Get(source) // nolint:noctx
if err != nil {
return "", fmt.Errorf("downloading %s: %w", source, err)
}
defer response.Body.Close()
_, err = io.Copy(tmp, response.Body)
if err != nil {
return "", fmt.Errorf("copying %s to %s: %w", source, tmp.Name(), err)
}
return tmp.Name(), nil
}

View File

@ -80,6 +80,7 @@ func DefaultProfile() *Seccomp {
"vmsplice",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
},
@ -574,6 +575,7 @@ func DefaultProfile() *Seccomp {
"open_by_handle_at",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -609,6 +611,7 @@ func DefaultProfile() *Seccomp {
"setns",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -630,6 +633,7 @@ func DefaultProfile() *Seccomp {
"chroot",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -657,6 +661,7 @@ func DefaultProfile() *Seccomp {
"query_module",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -678,6 +683,7 @@ func DefaultProfile() *Seccomp {
"acct",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -707,6 +713,7 @@ func DefaultProfile() *Seccomp {
"ptrace",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -730,6 +737,7 @@ func DefaultProfile() *Seccomp {
"ioperm",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -757,6 +765,7 @@ func DefaultProfile() *Seccomp {
"clock_settime64",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -778,6 +787,7 @@ func DefaultProfile() *Seccomp {
"vhangup",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@ -789,6 +799,7 @@ func DefaultProfile() *Seccomp {
"socket",
},
Action: ActErrno,
Errno: "EINVAL",
ErrnoRet: &einval,
Args: []*Arg{
{
@ -867,6 +878,7 @@ func DefaultProfile() *Seccomp {
return &Seccomp{
DefaultAction: ActErrno,
DefaultErrno: "ENOSYS",
DefaultErrnoRet: &enosys,
ArchMap: arches(),
Syscalls: syscalls,

View File

@ -0,0 +1,91 @@
package seccomp
import (
"golang.org/x/sys/unix"
)
// Error table
var errnoArch = map[string]uint{
"EPERM": uint(unix.EPERM),
"ENOENT": uint(unix.ENOENT),
"ESRCH": uint(unix.ESRCH),
"EIO": uint(unix.EIO),
"ENXIO": uint(unix.ENXIO),
"E2BIG": uint(unix.E2BIG),
"ENOEXEC": uint(unix.ENOEXEC),
"EBADF": uint(unix.EBADF),
"ECHILD": uint(unix.ECHILD),
"EDEADLK": uint(unix.EDEADLK),
"ENOMEM": uint(unix.ENOMEM),
"EACCES": uint(unix.EACCES),
"EFAULT": uint(unix.EFAULT),
"ENOTBLK": uint(unix.ENOTBLK),
"EBUSY": uint(unix.EBUSY),
"EEXIST": uint(unix.EEXIST),
"EXDEV": uint(unix.EXDEV),
"ENODEV": uint(unix.ENODEV),
"ENOTDIR": uint(unix.ENOTDIR),
"EISDIR": uint(unix.EISDIR),
"EINVAL": uint(unix.EINVAL),
"ENFILE": uint(unix.ENFILE),
"EMFILE": uint(unix.EMFILE),
"ENOTTY": uint(unix.ENOTTY),
"ETXTBSY": uint(unix.ETXTBSY),
"EFBIG": uint(unix.EFBIG),
"ENOSPC": uint(unix.ENOSPC),
"ESPIPE": uint(unix.ESPIPE),
"EROFS": uint(unix.EROFS),
"EMLINK": uint(unix.EMLINK),
"EPIPE": uint(unix.EPIPE),
"EDOM": uint(unix.EDOM),
"ERANGE": uint(unix.ERANGE),
"EAGAIN": uint(unix.EAGAIN),
"EINPROGRESS": uint(unix.EINPROGRESS),
"EALREADY": uint(unix.EALREADY),
"ENOTSOCK": uint(unix.ENOTSOCK),
"EDESTADDRREQ": uint(unix.EDESTADDRREQ),
"EMSGSIZE": uint(unix.EMSGSIZE),
"EPROTOTYPE": uint(unix.EPROTOTYPE),
"ENOPROTOOPT": uint(unix.ENOPROTOOPT),
"EPROTONOSUPPORT": uint(unix.EPROTONOSUPPORT),
"ESOCKTNOSUPPORT": uint(unix.ESOCKTNOSUPPORT),
"EOPNOTSUPP": uint(unix.EOPNOTSUPP),
"EPFNOSUPPORT": uint(unix.EPFNOSUPPORT),
"EAFNOSUPPORT": uint(unix.EAFNOSUPPORT),
"EADDRINUSE": uint(unix.EADDRINUSE),
"EADDRNOTAVAIL": uint(unix.EADDRNOTAVAIL),
"ENETDOWN": uint(unix.ENETDOWN),
"ENETUNREACH": uint(unix.ENETUNREACH),
"ENETRESET": uint(unix.ENETRESET),
"ECONNABORTED": uint(unix.ECONNABORTED),
"ECONNRESET": uint(unix.ECONNRESET),
"ENOBUFS": uint(unix.ENOBUFS),
"EISCONN": uint(unix.EISCONN),
"ENOTCONN": uint(unix.ENOTCONN),
"ESHUTDOWN": uint(unix.ESHUTDOWN),
"ETOOMANYREFS": uint(unix.ETOOMANYREFS),
"ETIMEDOUT": uint(unix.ETIMEDOUT),
"ECONNREFUSED": uint(unix.ECONNREFUSED),
"ELOOP": uint(unix.ELOOP),
"ENAMETOOLONG": uint(unix.ENAMETOOLONG),
"EHOSTDOWN": uint(unix.EHOSTDOWN),
"EHOSTUNREACH": uint(unix.EHOSTUNREACH),
"ENOTEMPTY": uint(unix.ENOTEMPTY),
"EUSERS": uint(unix.EUSERS),
"EDQUOT": uint(unix.EDQUOT),
"ESTALE": uint(unix.ESTALE),
"EREMOTE": uint(unix.EREMOTE),
"ENOLCK": uint(unix.ENOLCK),
"ENOSYS": uint(unix.ENOSYS),
"EILSEQ": uint(unix.EILSEQ),
"ENOMEDIUM": uint(unix.ENOMEDIUM),
"EMEDIUMTYPE": uint(unix.EMEDIUMTYPE),
"EOVERFLOW": uint(unix.EOVERFLOW),
"ECANCELED": uint(unix.ECANCELED),
"EIDRM": uint(unix.EIDRM),
"ENOMSG": uint(unix.ENOMSG),
"ENOTSUP": uint(unix.ENOTSUP),
"EBADMSG": uint(unix.EBADMSG),
"ENOTRECOVERABLE": uint(unix.ENOTRECOVERABLE),
"EOWNERDEAD": uint(unix.EOWNERDEAD),
}

View File

@ -1,6 +1,7 @@
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 38,
"defaultErrno": "ENOSYS",
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
@ -87,7 +88,8 @@
"comment": "",
"includes": {},
"excludes": {},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -650,7 +652,8 @@
"CAP_DAC_READ_SEARCH"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -693,7 +696,8 @@
"CAP_SYS_ADMIN"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -722,7 +726,8 @@
"CAP_SYS_CHROOT"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -757,7 +762,8 @@
"CAP_SYS_MODULE"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -786,7 +792,8 @@
"CAP_SYS_PACCT"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -823,7 +830,8 @@
"CAP_SYS_PTRACE"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -854,7 +862,8 @@
"CAP_SYS_RAWIO"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -889,7 +898,8 @@
"CAP_SYS_TIME"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -918,7 +928,8 @@
"CAP_SYS_TTY_CONFIG"
]
},
"errnoRet": 1
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -946,7 +957,8 @@
"CAP_AUDIT_WRITE"
]
},
"errnoRet": 22
"errnoRet": 22,
"errno": "EINVAL"
},
{
"names": [

View File

@ -10,6 +10,7 @@ import (
"encoding/json"
"errors"
"fmt"
"strconv"
"github.com/opencontainers/runtime-spec/specs-go"
libseccomp "github.com/seccomp/libseccomp-golang"
@ -66,6 +67,37 @@ func inSlice(slice []string, s string) bool {
return false
}
func getArchitectures(config *Seccomp, newConfig *specs.LinuxSeccomp) error {
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
return errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
}
// if config.Architectures == 0 then libseccomp will figure out the architecture to use
if len(config.Architectures) != 0 {
for _, a := range config.Architectures {
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a))
}
}
return nil
}
func getErrno(errno string, def *uint) (*uint, error) {
if errno == "" {
return def, nil
}
v, err := strconv.ParseUint(errno, 10, 32)
if err == nil {
v2 := uint(v)
return &v2, nil
}
v2, found := errnoArch[errno]
if !found {
return nil, fmt.Errorf("unknown errno %s", errno)
}
return &v2, nil
}
func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
if config == nil {
return nil, nil
@ -84,15 +116,8 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
arch = native.String()
}
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
return nil, errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
}
// if config.Architectures == 0 then libseccomp will figure out the architecture to use
if len(config.Architectures) != 0 {
for _, a := range config.Architectures {
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a))
}
if err := getArchitectures(config, newConfig); err != nil {
return nil, err
}
if len(config.ArchMap) != 0 {
@ -111,7 +136,11 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
}
newConfig.DefaultAction = specs.LinuxSeccompAction(config.DefaultAction)
newConfig.DefaultErrnoRet = config.DefaultErrnoRet
newConfig.DefaultErrnoRet, err = getErrno(config.DefaultErrno, config.DefaultErrnoRet)
if err != nil {
return nil, err
}
Loop:
// Loop through all syscall blocks and convert them to libcontainer format after filtering them
@ -145,12 +174,17 @@ Loop:
return nil, errors.New("'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'")
}
errno, err := getErrno(call.Errno, call.ErrnoRet)
if err != nil {
return nil, err
}
if call.Name != "" {
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args, call.ErrnoRet))
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args, errno))
}
if len(call.Names) > 0 {
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, call.ErrnoRet))
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, errno))
}
}

View File

@ -6,8 +6,12 @@ package seccomp
// Seccomp represents the config for a seccomp profile for syscall restriction.
type Seccomp struct {
DefaultAction Action `json:"defaultAction"`
DefaultAction Action `json:"defaultAction"`
// DefaultErrnoRet is obsolete, please use DefaultErrno
DefaultErrnoRet *uint `json:"defaultErrnoRet,omitempty"`
DefaultErrno string `json:"defaultErrno,omitempty"`
// Architectures is kept to maintain backward compatibility with the old
// seccomp profile.
Architectures []Arch `json:"architectures,omitempty"`
@ -107,5 +111,7 @@ type Syscall struct {
Comment string `json:"comment"`
Includes Filter `json:"includes"`
Excludes Filter `json:"excludes"`
ErrnoRet *uint `json:"errnoRet,omitempty"`
// ErrnoRet is obsolete, please use Errno
ErrnoRet *uint `json:"errnoRet,omitempty"`
Errno string `json:"errno,omitempty"`
}