summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container.go16
-rw-r--r--libpod/container_config.go2
-rw-r--r--libpod/container_inspect.go4
-rw-r--r--libpod/container_internal.go29
-rw-r--r--libpod/container_internal_linux.go139
-rw-r--r--libpod/container_log_linux.go38
-rw-r--r--libpod/define/container_inspect.go15
-rw-r--r--libpod/define/errors.go3
-rw-r--r--libpod/define/info.go51
-rw-r--r--libpod/diff.go5
-rw-r--r--libpod/logs/log.go30
-rw-r--r--libpod/networking_linux.go49
-rw-r--r--libpod/networking_slirp4netns.go92
-rw-r--r--libpod/options.go17
-rw-r--r--libpod/runtime.go15
-rw-r--r--libpod/runtime_ctr.go2
16 files changed, 361 insertions, 146 deletions
diff --git a/libpod/container.go b/libpod/container.go
index c49d8feeb..591cf9bc5 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -126,6 +126,8 @@ type Container struct {
// This is true if a container is restored from a checkpoint.
restoreFromCheckpoint bool
+
+ slirp4netnsSubnet *net.IPNet
}
// ContainerState contains the current state of the container
@@ -235,6 +237,18 @@ type ContainerImageVolume struct {
ReadWrite bool `json:"rw"`
}
+// ContainerSecret is a secret that is mounted in a container
+type ContainerSecret struct {
+ // Secret is the secret
+ *secrets.Secret
+ // UID is tbe UID of the secret file
+ UID uint32
+ // GID is the GID of the secret file
+ GID uint32
+ // Mode is the mode of the secret file
+ Mode uint32
+}
+
// ContainerNetworkDescriptions describes the relationship between the CNI
// network and the ethN where N is an integer
type ContainerNetworkDescriptions map[string]int
@@ -1124,7 +1138,7 @@ func (c *Container) Umask() string {
}
//Secrets return the secrets in the container
-func (c *Container) Secrets() []*secrets.Secret {
+func (c *Container) Secrets() []*ContainerSecret {
return c.config.Secrets
}
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 904c03f9b..0de79fde3 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -148,7 +148,7 @@ type ContainerRootFSConfig struct {
// default, but others do not.
CreateWorkingDir bool `json:"createWorkingDir,omitempty"`
// Secrets lists secrets to mount into the container
- Secrets []*secrets.Secret `json:"secrets,omitempty"`
+ Secrets []*ContainerSecret `json:"secrets,omitempty"`
// SecretPath is the secrets location in storage
SecretsPath string `json:"secretsPath"`
// Volatile specifies whether the container storage can be optimized
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 5b2103c92..4210bc581 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -343,11 +343,13 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.CreateCommand = c.config.CreateCommand
ctrConfig.Timezone = c.config.Timezone
-
for _, secret := range c.config.Secrets {
newSec := define.InspectSecret{}
newSec.Name = secret.Name
newSec.ID = secret.ID
+ newSec.UID = secret.UID
+ newSec.GID = secret.GID
+ newSec.Mode = secret.Mode
ctrConfig.Secrets = append(ctrConfig.Secrets, &newSec)
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 53b85a466..f77825efd 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -15,7 +15,7 @@ import (
metadata "github.com/checkpoint-restore/checkpointctl/lib"
"github.com/containers/buildah/copier"
- "github.com/containers/common/pkg/secrets"
+ butil "github.com/containers/buildah/util"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/pkg/cgroups"
@@ -24,6 +24,7 @@ import (
"github.com/containers/podman/v3/pkg/hooks/exec"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/selinux"
+ "github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/idtools"
@@ -1530,6 +1531,16 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
}()
}
+ // If /etc/mtab does not exist in container image, then we need to
+ // create it, so that mount command within the container will work.
+ mtab := filepath.Join(mountPoint, "/etc/mtab")
+ if err := os.MkdirAll(filepath.Dir(mtab), 0755); err != nil {
+ return "", errors.Wrap(err, "error creating mtab directory")
+ }
+ if err = os.Symlink("/proc/mounts", mtab); err != nil && !os.IsExist(err) {
+ return "", err
+ }
+
// Request a mount of all named volumes
for _, v := range c.config.NamedVolumes {
vol, err := c.mountNamedVolume(v, mountPoint)
@@ -2235,21 +2246,31 @@ func (c *Container) hasNamespace(namespace spec.LinuxNamespaceType) bool {
}
// extractSecretToStorage copies a secret's data from the secrets manager to the container's static dir
-func (c *Container) extractSecretToCtrStorage(name string) error {
- manager, err := secrets.NewManager(c.runtime.GetSecretsStorageDir())
+func (c *Container) extractSecretToCtrStorage(secr *ContainerSecret) error {
+ manager, err := c.runtime.SecretsManager()
if err != nil {
return err
}
- secr, data, err := manager.LookupSecretData(name)
+ _, data, err := manager.LookupSecretData(secr.Name)
if err != nil {
return err
}
secretFile := filepath.Join(c.config.SecretsPath, secr.Name)
+ hostUID, hostGID, err := butil.GetHostIDs(util.IDtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), util.IDtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), secr.UID, secr.GID)
+ if err != nil {
+ return errors.Wrap(err, "unable to extract secret")
+ }
err = ioutil.WriteFile(secretFile, data, 0644)
if err != nil {
return errors.Wrapf(err, "unable to create %s", secretFile)
}
+ if err := os.Lchown(secretFile, int(hostUID), int(hostGID)); err != nil {
+ return err
+ }
+ if err := os.Chmod(secretFile, os.FileMode(secr.Mode)); err != nil {
+ return err
+ }
if err := label.Relabel(secretFile, c.config.MountLabel, false); err != nil {
return err
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 7d57e8965..1b2f5a496 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -29,7 +29,6 @@ import (
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/chown"
"github.com/containers/common/pkg/config"
- "github.com/containers/common/pkg/secrets"
"github.com/containers/common/pkg/subscriptions"
"github.com/containers/common/pkg/umask"
"github.com/containers/podman/v3/libpod/define"
@@ -359,6 +358,25 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
return nil, err
}
+ // Add named volumes
+ for _, namedVol := range c.config.NamedVolumes {
+ volume, err := c.runtime.GetVolume(namedVol.Name)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error retrieving volume %s to add to container %s", namedVol.Name, c.ID())
+ }
+ mountPoint, err := volume.MountPoint()
+ if err != nil {
+ return nil, err
+ }
+ volMount := spec.Mount{
+ Type: "bind",
+ Source: mountPoint,
+ Destination: namedVol.Dest,
+ Options: namedVol.Options,
+ }
+ g.AddMount(volMount)
+ }
+
// Check if the spec file mounts contain the options z, Z or U.
// If they have z or Z, relabel the source directory and then remove the option.
// If they have U, chown the source directory and them remove the option.
@@ -392,25 +410,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
g.SetProcessSelinuxLabel(c.ProcessLabel())
g.SetLinuxMountLabel(c.MountLabel())
- // Add named volumes
- for _, namedVol := range c.config.NamedVolumes {
- volume, err := c.runtime.GetVolume(namedVol.Name)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving volume %s to add to container %s", namedVol.Name, c.ID())
- }
- mountPoint, err := volume.MountPoint()
- if err != nil {
- return nil, err
- }
- volMount := spec.Mount{
- Type: "bind",
- Source: mountPoint,
- Destination: namedVol.Dest,
- Options: namedVol.Options,
- }
- g.AddMount(volMount)
- }
-
// Add bind mounts to container
for dstPath, srcPath := range c.state.BindMounts {
newMount := spec.Mount{
@@ -759,7 +758,10 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
return nil, errors.Wrapf(err, "error setting up OCI Hooks")
}
if len(c.config.EnvSecrets) > 0 {
- manager, err := secrets.NewManager(c.runtime.GetSecretsStorageDir())
+ manager, err := c.runtime.SecretsManager()
+ if err != nil {
+ return nil, err
+ }
if err != nil {
return nil, err
}
@@ -1358,6 +1360,34 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return c.save()
}
+// Retrieves a container's "root" net namespace container dependency.
+func (c *Container) getRootNetNsDepCtr() (depCtr *Container, err error) {
+ containersVisited := map[string]int{c.config.ID: 1}
+ nextCtr := c.config.NetNsCtr
+ for nextCtr != "" {
+ // Make sure we aren't in a loop
+ if _, visited := containersVisited[nextCtr]; visited {
+ return nil, errors.New("loop encountered while determining net namespace container")
+ }
+ containersVisited[nextCtr] = 1
+
+ depCtr, err = c.runtime.state.Container(nextCtr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error fetching dependency %s of container %s", c.config.NetNsCtr, c.ID())
+ }
+ // This should never happen without an error
+ if depCtr == nil {
+ break
+ }
+ nextCtr = depCtr.config.NetNsCtr
+ }
+
+ if depCtr == nil {
+ return nil, errors.New("unexpected error depCtr is nil without reported error from runtime state")
+ }
+ return depCtr, nil
+}
+
// Make standard bind mounts to include in the container
func (c *Container) makeBindMounts() error {
if err := os.Chown(c.state.RunDir, c.RootUID(), c.RootGID()); err != nil {
@@ -1396,24 +1426,9 @@ func (c *Container) makeBindMounts() error {
// We want /etc/resolv.conf and /etc/hosts from the
// other container. Unless we're not creating both of
// them.
- var (
- depCtr *Container
- nextCtr string
- )
-
- // I don't like infinite loops, but I don't think there's
- // a serious risk of looping dependencies - too many
- // protections against that elsewhere.
- nextCtr = c.config.NetNsCtr
- for {
- depCtr, err = c.runtime.state.Container(nextCtr)
- if err != nil {
- return errors.Wrapf(err, "error fetching dependency %s of container %s", c.config.NetNsCtr, c.ID())
- }
- nextCtr = depCtr.config.NetNsCtr
- if nextCtr == "" {
- break
- }
+ depCtr, err := c.getRootNetNsDepCtr()
+ if err != nil {
+ return errors.Wrapf(err, "error fetching network namespace dependency container for container %s", c.ID())
}
// We need that container's bind mounts
@@ -1698,7 +1713,12 @@ func (c *Container) generateResolvConf() (string, error) {
nameservers = resolvconf.GetNameservers(resolv.Content)
// slirp4netns has a built in DNS server.
if c.config.NetMode.IsSlirp4netns() {
- nameservers = append([]string{slirp4netnsDNS}, nameservers...)
+ slirp4netnsDNS, err := GetSlirp4netnsDNS(c.slirp4netnsSubnet)
+ if err != nil {
+ logrus.Warn("failed to determine Slirp4netns DNS: ", err.Error())
+ } else {
+ nameservers = append([]string{slirp4netnsDNS.String()}, nameservers...)
+ }
}
}
@@ -1779,7 +1799,12 @@ func (c *Container) getHosts() string {
if c.Hostname() != "" {
if c.config.NetMode.IsSlirp4netns() {
// When using slirp4netns, the interface gets a static IP
- hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", slirp4netnsIP, c.Hostname(), c.config.Name)
+ slirp4netnsIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet)
+ if err != nil {
+ logrus.Warn("failed to determine slirp4netnsIP: ", err.Error())
+ } else {
+ hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", slirp4netnsIP.String(), c.Hostname(), c.config.Name)
+ }
} else {
hasNetNS := false
netNone := false
@@ -1802,6 +1827,36 @@ func (c *Container) getHosts() string {
}
}
}
+
+ // Add gateway entry
+ var depCtr *Container
+ if c.config.NetNsCtr != "" {
+ // ignoring the error because there isn't anything to do
+ depCtr, _ = c.getRootNetNsDepCtr()
+ } else if len(c.state.NetworkStatus) != 0 {
+ depCtr = c
+ } else {
+ depCtr = nil
+ }
+
+ if depCtr != nil {
+ for _, pluginResultsRaw := range depCtr.state.NetworkStatus {
+ pluginResult, _ := cnitypes.GetResult(pluginResultsRaw)
+ for _, ip := range pluginResult.IPs {
+ hosts += fmt.Sprintf("%s host.containers.internal\n", ip.Gateway)
+ }
+ }
+ } else if c.config.NetMode.IsSlirp4netns() {
+ gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet)
+ if err != nil {
+ logrus.Warn("failed to determine gatewayIP: ", err.Error())
+ } else {
+ hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String())
+ }
+ } else {
+ logrus.Debug("network configuration does not support host.containers.internal address")
+ }
+
return hosts
}
@@ -2339,7 +2394,7 @@ func (c *Container) createSecretMountDir() error {
oldUmask := umask.Set(0)
defer umask.Set(oldUmask)
- if err := os.MkdirAll(src, 0644); err != nil {
+ if err := os.MkdirAll(src, 0755); err != nil {
return err
}
if err := label.Relabel(src, c.config.MountLabel, false); err != nil {
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 4a541b6e7..ec4fa9724 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -8,12 +8,12 @@ import (
"fmt"
"io"
"math"
+ "strings"
"time"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/logs"
journal "github.com/coreos/go-systemd/v22/sdjournal"
- "github.com/hpcloud/tail/watch"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -89,21 +89,19 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
}
}()
go func() {
- for {
- state, err := c.State()
- if err != nil {
- until <- time.Time{}
- logrus.Error(err)
- break
- }
- time.Sleep(watch.POLL_DURATION)
- if state != define.ContainerStateRunning && state != define.ContainerStatePaused {
- until <- time.Time{}
- break
- }
- }
+ // FIXME (#10323): we are facing a terrible
+ // race condition here. At the time the
+ // container dies and `c.Wait()` has returned,
+ // we may not have received all journald logs.
+ // So far there is no other way than waiting
+ // for a second. Ultimately, `r.Follow` is
+ // racy and we may have to implement our custom
+ // logic here.
+ c.Wait(ctx)
+ time.Sleep(time.Second)
+ until <- time.Time{}
}()
- follower := FollowBuffer{logChannel}
+ follower := journaldFollowBuffer{logChannel, options.Multi}
err := r.Follow(until, follower)
if err != nil {
logrus.Debugf(err.Error())
@@ -124,7 +122,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
// because we are reusing bytes, we need to make
// sure the old data doesn't get into the new line
bytestr := string(bytes[:ec])
- logLine, err2 := logs.NewLogLine(bytestr)
+ logLine, err2 := logs.NewJournaldLogLine(bytestr, options.Multi)
if err2 != nil {
logrus.Error(err2)
continue
@@ -210,16 +208,18 @@ func formatterMessage(entry *journal.JournalEntry) (string, error) {
if !ok {
return "", fmt.Errorf("no MESSAGE field present in journal entry")
}
+ msg = strings.TrimSuffix(msg, "\n")
return msg, nil
}
-type FollowBuffer struct {
+type journaldFollowBuffer struct {
logChannel chan *logs.LogLine
+ withID bool
}
-func (f FollowBuffer) Write(p []byte) (int, error) {
+func (f journaldFollowBuffer) Write(p []byte) (int, error) {
bytestr := string(p)
- logLine, err := logs.NewLogLine(bytestr)
+ logLine, err := logs.NewJournaldLogLine(bytestr, f.withID)
if err != nil {
return -1, err
}
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 5283946fa..af8ba6ecf 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -713,13 +713,16 @@ type DriverData struct {
Data map[string]string `json:"Data"`
}
-// InspectHostPort provides information on a port on the host that a container's
-// port is bound to.
+// InspectSecret contains information on secrets mounted inside the container
type InspectSecret struct {
- // IP on the host we are bound to. "" if not specified (binding to all
- // IPs).
+ // Name is the name of the secret
Name string `json:"Name"`
- // Port on the host we are bound to. No special formatting - just an
- // integer stuffed into a string.
+ // ID is the ID of the secret
ID string `json:"ID"`
+ // ID is the UID of the mounted secret file
+ UID uint32 `json:"UID"`
+ // ID is the GID of the mounted secret file
+ GID uint32 `json:"GID"`
+ // ID is the ID of the mode of the mounted secret file
+ Mode uint32 `json:"Mode"`
}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 64c652eec..81bf5f69c 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -179,6 +179,9 @@ var (
// ErrNoNetwork indicates that a container has no net namespace, like network=none
ErrNoNetwork = errors.New("container has no network namespace")
+ // ErrNetworkModeInvalid indicates that a container has the wrong network mode for an operation
+ ErrNetworkModeInvalid = errors.New("invalid network mode")
+
// ErrSetSecurityAttribute indicates that a request to set a container's security attribute
// was not possible.
ErrSetSecurityAttribute = fmt.Errorf("%w: unable to assign security attribute", ErrOCIRuntime)
diff --git a/libpod/define/info.go b/libpod/define/info.go
index 87935be2d..c9d6877c0 100644
--- a/libpod/define/info.go
+++ b/libpod/define/info.go
@@ -21,31 +21,34 @@ type SecurityInfo struct {
SELinuxEnabled bool `json:"selinuxEnabled"`
}
-//HostInfo describes the libpod host
+// HostInfo describes the libpod host
type HostInfo struct {
- Arch string `json:"arch"`
- BuildahVersion string `json:"buildahVersion"`
- CgroupManager string `json:"cgroupManager"`
- CGroupsVersion string `json:"cgroupVersion"`
- Conmon *ConmonInfo `json:"conmon"`
- CPUs int `json:"cpus"`
- Distribution DistributionInfo `json:"distribution"`
- EventLogger string `json:"eventLogger"`
- Hostname string `json:"hostname"`
- IDMappings IDMappings `json:"idMappings,omitempty"`
- Kernel string `json:"kernel"`
- MemFree int64 `json:"memFree"`
- MemTotal int64 `json:"memTotal"`
- OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"`
- OS string `json:"os"`
- RemoteSocket *RemoteSocket `json:"remoteSocket,omitempty"`
- RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"`
- Security SecurityInfo `json:"security"`
- Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"`
- SwapFree int64 `json:"swapFree"`
- SwapTotal int64 `json:"swapTotal"`
- Uptime string `json:"uptime"`
- Linkmode string `json:"linkmode"`
+ Arch string `json:"arch"`
+ BuildahVersion string `json:"buildahVersion"`
+ CgroupManager string `json:"cgroupManager"`
+ CGroupsVersion string `json:"cgroupVersion"`
+ Conmon *ConmonInfo `json:"conmon"`
+ CPUs int `json:"cpus"`
+ Distribution DistributionInfo `json:"distribution"`
+ EventLogger string `json:"eventLogger"`
+ Hostname string `json:"hostname"`
+ IDMappings IDMappings `json:"idMappings,omitempty"`
+ Kernel string `json:"kernel"`
+ MemFree int64 `json:"memFree"`
+ MemTotal int64 `json:"memTotal"`
+ OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"`
+ OS string `json:"os"`
+ // RemoteSocket returns the UNIX domain socket the Podman service is listening on
+ RemoteSocket *RemoteSocket `json:"remoteSocket,omitempty"`
+ RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"`
+ // ServiceIsRemote is true when the podman/libpod service is remote to the client
+ ServiceIsRemote bool `json:"serviceIsRemote"`
+ Security SecurityInfo `json:"security"`
+ Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"`
+ SwapFree int64 `json:"swapFree"`
+ SwapTotal int64 `json:"swapTotal"`
+ Uptime string `json:"uptime"`
+ Linkmode string `json:"linkmode"`
}
// RemoteSocket describes information about the API socket
diff --git a/libpod/diff.go b/libpod/diff.go
index 6ce8d809a..c5a53478b 100644
--- a/libpod/diff.go
+++ b/libpod/diff.go
@@ -7,7 +7,7 @@ import (
"github.com/pkg/errors"
)
-var containerMounts = map[string]bool{
+var initInodes = map[string]bool{
"/dev": true,
"/etc/hostname": true,
"/etc/hosts": true,
@@ -17,6 +17,7 @@ var containerMounts = map[string]bool{
"/run/.containerenv": true,
"/run/secrets": true,
"/sys": true,
+ "/etc/mtab": true,
}
// GetDiff returns the differences between the two images, layers, or containers
@@ -36,7 +37,7 @@ func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) {
changes, err := r.store.Changes(fromLayer, toLayer)
if err == nil {
for _, c := range changes {
- if containerMounts[c.Path] {
+ if initInodes[c.Path] {
continue
}
rchanges = append(rchanges, c)
diff --git a/libpod/logs/log.go b/libpod/logs/log.go
index bba52408d..308053b47 100644
--- a/libpod/logs/log.go
+++ b/libpod/logs/log.go
@@ -206,6 +206,36 @@ func NewLogLine(line string) (*LogLine, error) {
return &l, nil
}
+// NewJournaldLogLine creates a LogLine from the specified line from journald.
+// Note that if withID is set, the first item of the message is considerred to
+// be the container ID and set as such.
+func NewJournaldLogLine(line string, withID bool) (*LogLine, error) {
+ splitLine := strings.Split(line, " ")
+ if len(splitLine) < 4 {
+ return nil, errors.Errorf("'%s' is not a valid container log line", line)
+ }
+ logTime, err := time.Parse(LogTimeFormat, splitLine[0])
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to convert time %s from container log", splitLine[0])
+ }
+ var msg, id string
+ if withID {
+ id = splitLine[3]
+ msg = strings.Join(splitLine[4:], " ")
+ } else {
+ msg = strings.Join(splitLine[3:], " ")
+ // NO ID
+ }
+ l := LogLine{
+ Time: logTime,
+ Device: splitLine[1],
+ ParseLogType: splitLine[2],
+ Msg: msg,
+ CID: id,
+ }
+ return &l, nil
+}
+
// Partial returns a bool if the log line is a partial log type
func (l *LogLine) Partial() bool {
return l.ParseLogType == PartialLogType
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index cfed5a1f2..0e8a4f768 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -23,6 +23,7 @@ import (
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/libpod/network"
"github.com/containers/podman/v3/pkg/errorhandling"
+ "github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/netns"
"github.com/containers/podman/v3/pkg/resolvconf"
"github.com/containers/podman/v3/pkg/rootless"
@@ -37,16 +38,12 @@ import (
)
const (
- // slirp4netnsIP is the IP used by slirp4netns to configure the tap device
- // inside the network namespace.
- slirp4netnsIP = "10.0.2.100"
-
- // slirp4netnsDNS is the IP for the built-in DNS server in the slirp network
- slirp4netnsDNS = "10.0.2.3"
-
// slirp4netnsMTU the default MTU override
slirp4netnsMTU = 65520
+ // default slirp4ns subnet
+ defaultSlirp4netnsSubnet = "10.0.2.0/24"
+
// rootlessCNINSName is the file name for the rootless network namespace bind mount
rootlessCNINSName = "rootless-cni-ns"
)
@@ -360,15 +357,20 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
}
// build a new resolv.conf file which uses the slirp4netns dns server address
- resolveIP := slirp4netnsDNS
+ resolveIP, err := GetSlirp4netnsDNS(nil)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to determine default slirp4netns DNS address")
+ }
+
if netOptions.cidr != "" {
_, cidr, err := net.ParseCIDR(netOptions.cidr)
if err != nil {
return nil, errors.Wrap(err, "failed to parse slirp4netns cidr")
}
- // the slirp dns ip is always the third ip in the subnet
- cidr.IP[len(cidr.IP)-1] = cidr.IP[len(cidr.IP)-1] + 3
- resolveIP = cidr.IP.String()
+ resolveIP, err = GetSlirp4netnsDNS(cidr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to determine slirp4netns DNS address from cidr: %s", cidr.String())
+ }
}
conf, err := resolvconf.Get()
if err != nil {
@@ -377,7 +379,7 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
searchDomains := resolvconf.GetSearchDomains(conf.Content)
dnsOptions := resolvconf.GetOptions(conf.Content)
- _, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), []string{resolveIP}, searchDomains, dnsOptions)
+ _, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), []string{resolveIP.String()}, searchDomains, dnsOptions)
if err != nil {
return nil, errors.Wrap(err, "failed to create rootless cni resolv.conf")
}
@@ -577,7 +579,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
// set up port forwarder for CNI-in-slirp4netns
netnsPath := ctr.state.NetNS.Path()
// TODO: support slirp4netns port forwarder as well
- return r.setupRootlessPortMappingViaRLK(ctr, netnsPath, "")
+ return r.setupRootlessPortMappingViaRLK(ctr, netnsPath)
}
return nil
}
@@ -757,6 +759,15 @@ func getContainerNetNS(ctr *Container) (string, error) {
return "", nil
}
+// isBridgeNetMode checks if the given network mode is bridge.
+// It returns nil when it is set to bridge and an error otherwise.
+func isBridgeNetMode(n namespaces.NetworkMode) error {
+ if !n.IsBridge() {
+ return errors.Wrapf(define.ErrNetworkModeInvalid, "%q is not supported", n)
+ }
+ return nil
+}
+
// Reload only works with containers with a configured network.
// It will tear down, and then reconfigure, the network of the container.
// This is mainly used when a reload of firewall rules wipes out existing
@@ -770,8 +781,8 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, er
if ctr.state.NetNS == nil {
return nil, errors.Wrapf(define.ErrCtrStateInvalid, "container %s network is not configured, refusing to reload", ctr.ID())
}
- if rootless.IsRootless() || ctr.config.NetMode.IsSlirp4netns() {
- return nil, errors.Wrapf(define.ErrRootless, "network reload only supported for root containers")
+ if err := isBridgeNetMode(ctr.config.NetMode); err != nil {
+ return nil, err
}
logrus.Infof("Going to reload container %s network", ctr.ID())
@@ -1025,8 +1036,8 @@ func (w *logrusDebugWriter) Write(p []byte) (int, error) {
// NetworkDisconnect removes a container from the network
func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) error {
// only the bridge mode supports cni networks
- if !c.config.NetMode.IsBridge() {
- return errors.Errorf("network mode %q is not supported", c.config.NetMode)
+ if err := isBridgeNetMode(c.config.NetMode); err != nil {
+ return err
}
networks, err := c.networksByNameIndex()
@@ -1086,8 +1097,8 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
// ConnectNetwork connects a container to a given network
func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) error {
// only the bridge mode supports cni networks
- if !c.config.NetMode.IsBridge() {
- return errors.Errorf("network mode %q is not supported", c.config.NetMode)
+ if err := isBridgeNetMode(c.config.NetMode); err != nil {
+ return err
}
networks, err := c.networksByNameIndex()
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
index c46dc6972..74d390d29 100644
--- a/libpod/networking_slirp4netns.go
+++ b/libpod/networking_slirp4netns.go
@@ -308,15 +308,89 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
return err
}
+ // Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself
+ _, ctr.slirp4netnsSubnet, _ = net.ParseCIDR(defaultSlirp4netnsSubnet)
+
+ // Set slirp4netnsSubnet addresses now that we are pretty sure the command executed
+ if netOptions.cidr != "" {
+ ipv4, ipv4network, err := net.ParseCIDR(netOptions.cidr)
+ if err != nil || ipv4.To4() == nil {
+ return errors.Errorf("invalid cidr %q", netOptions.cidr)
+ }
+ ctr.slirp4netnsSubnet = ipv4network
+ }
+
if havePortMapping {
if netOptions.isSlirpHostForward {
return r.setupRootlessPortMappingViaSlirp(ctr, cmd, apiSocket)
}
- return r.setupRootlessPortMappingViaRLK(ctr, netnsPath, netOptions.cidr)
+ return r.setupRootlessPortMappingViaRLK(ctr, netnsPath)
}
+
return nil
}
+// Get expected slirp ipv4 address based on subnet. If subnet is null use default subnet
+// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
+func GetSlirp4netnsIP(subnet *net.IPNet) (*net.IP, error) {
+ _, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
+ if subnet != nil {
+ slirpSubnet = subnet
+ }
+ expectedIP, err := addToIP(slirpSubnet, uint32(100))
+ if err != nil {
+ return nil, errors.Wrapf(err, "error calculating expected ip for slirp4netns")
+ }
+ return expectedIP, nil
+}
+
+// Get expected slirp Gateway ipv4 address based on subnet
+// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
+func GetSlirp4netnsGateway(subnet *net.IPNet) (*net.IP, error) {
+ _, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
+ if subnet != nil {
+ slirpSubnet = subnet
+ }
+ expectedGatewayIP, err := addToIP(slirpSubnet, uint32(2))
+ if err != nil {
+ return nil, errors.Wrapf(err, "error calculating expected gateway ip for slirp4netns")
+ }
+ return expectedGatewayIP, nil
+}
+
+// Get expected slirp DNS ipv4 address based on subnet
+// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
+func GetSlirp4netnsDNS(subnet *net.IPNet) (*net.IP, error) {
+ _, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
+ if subnet != nil {
+ slirpSubnet = subnet
+ }
+ expectedDNSIP, err := addToIP(slirpSubnet, uint32(3))
+ if err != nil {
+ return nil, errors.Wrapf(err, "error calculating expected dns ip for slirp4netns")
+ }
+ return expectedDNSIP, nil
+}
+
+// Helper function to calculate slirp ip address offsets
+// Adapted from: https://github.com/signalsciences/ipv4/blob/master/int.go#L12-L24
+func addToIP(subnet *net.IPNet, offset uint32) (*net.IP, error) {
+ // I have no idea why I have to do this, but if I don't ip is 0
+ ipFixed := subnet.IP.To4()
+
+ ipInteger := uint32(ipFixed[3]) | uint32(ipFixed[2])<<8 | uint32(ipFixed[1])<<16 | uint32(ipFixed[0])<<24
+ ipNewRaw := ipInteger + offset
+ // Avoid overflows
+ if ipNewRaw < ipInteger {
+ return nil, errors.Errorf("integer overflow while calculating ip address offset, %s + %d", ipFixed, offset)
+ }
+ ipNew := net.IPv4(byte(ipNewRaw>>24), byte(ipNewRaw>>16&0xFF), byte(ipNewRaw>>8)&0xFF, byte(ipNewRaw&0xFF))
+ if !subnet.Contains(ipNew) {
+ return nil, errors.Errorf("calculated ip address %s is not within given subnet %s", ipNew.String(), subnet.String())
+ }
+ return &ipNew, nil
+}
+
func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout time.Duration) error {
prog := filepath.Base(cmd.Path)
if len(cmd.Args) > 0 {
@@ -363,7 +437,7 @@ func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout t
return nil
}
-func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath, slirp4CIDR string) error {
+func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath string) error {
syncR, syncW, err := os.Pipe()
if err != nil {
return errors.Wrapf(err, "failed to open pipe")
@@ -390,17 +464,11 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath, slir
}
}
- childIP := slirp4netnsIP
- // set the correct childIP when a custom cidr is set
- if slirp4CIDR != "" {
- _, cidr, err := net.ParseCIDR(slirp4CIDR)
- if err != nil {
- return errors.Wrap(err, "failed to parse slirp4netns cidr")
- }
- // the slirp container ip is always the hundredth ip in the subnet
- cidr.IP[len(cidr.IP)-1] = cidr.IP[len(cidr.IP)-1] + 100
- childIP = cidr.IP.String()
+ slirp4netnsIP, err := GetSlirp4netnsIP(ctr.slirp4netnsSubnet)
+ if err != nil {
+ return errors.Wrapf(err, "failed to get slirp4ns ip")
}
+ childIP := slirp4netnsIP.String()
outer:
for _, r := range ctr.state.NetworkStatus {
for _, i := range r.IPs {
diff --git a/libpod/options.go b/libpod/options.go
index be26ced99..f942d264b 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1695,23 +1695,12 @@ func WithUmask(umask string) CtrCreateOption {
}
// WithSecrets adds secrets to the container
-func WithSecrets(secretNames []string) CtrCreateOption {
+func WithSecrets(containerSecrets []*ContainerSecret) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
- manager, err := secrets.NewManager(ctr.runtime.GetSecretsStorageDir())
- if err != nil {
- return err
- }
- for _, name := range secretNames {
- secr, err := manager.Lookup(name)
- if err != nil {
- return err
- }
- ctr.config.Secrets = append(ctr.config.Secrets, secr)
- }
-
+ ctr.config.Secrets = containerSecrets
return nil
}
}
@@ -1723,7 +1712,7 @@ func WithEnvSecrets(envSecrets map[string]string) CtrCreateOption {
if ctr.valid {
return define.ErrCtrFinalized
}
- manager, err := secrets.NewManager(ctr.runtime.GetSecretsStorageDir())
+ manager, err := ctr.runtime.SecretsManager()
if err != nil {
return err
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 80fe92b54..d0bdeb574 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -16,6 +16,7 @@ import (
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
+ "github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/pkg/sysregistriesv2"
is "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/types"
@@ -103,6 +104,8 @@ type Runtime struct {
// noStore indicates whether we need to interact with a store or not
noStore bool
+ // secretsManager manages secrets
+ secretsManager *secrets.SecretsManager
}
// SetXdgDirs ensures the XDG_RUNTIME_DIR env and XDG_CONFIG_HOME variables are set.
@@ -1022,6 +1025,18 @@ func (r *Runtime) GetSecretsStorageDir() string {
return filepath.Join(r.store.GraphRoot(), "secrets")
}
+// SecretsManager returns the directory that the secrets manager should take
+func (r *Runtime) SecretsManager() (*secrets.SecretsManager, error) {
+ if r.secretsManager == nil {
+ manager, err := secrets.NewManager(r.GetSecretsStorageDir())
+ if err != nil {
+ return nil, err
+ }
+ r.secretsManager = manager
+ }
+ return r.secretsManager, nil
+}
+
func graphRootMounted() bool {
f, err := os.OpenFile("/run/.containerenv", os.O_RDONLY, os.ModePerm)
if err != nil {
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 7d31e392f..4e4b2a8ab 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -366,7 +366,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
return nil, err
}
for _, secr := range ctr.config.Secrets {
- err = ctr.extractSecretToCtrStorage(secr.Name)
+ err = ctr.extractSecretToCtrStorage(secr)
if err != nil {
return nil, err
}