buildah: add pasta(1) support
Like podman allow buildah and therefore podman build to use the network mode pasta. The pasta integration is very simple and we do not even need a teardown handler for that as pasta will exit on its own when the netns path is removed. However right now this is broken, pasta will fail to open /proc/$pid/ns/net. I send a patch[1] to fix this upstream in pasta. I assume this will land quickly so I like to get this in now just so we have this included in podman v4.6. Thus the test is skipped for now. [1] https://archives.passt.top/passt-dev/20230623082531.25947-2-pholzing@redhat.com/ Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
parent
11ba328e2b
commit
74b885b9e2
|
|
@ -542,6 +542,43 @@ Valid _mode_ values are:
|
||||||
- **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp binds to.
|
- **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp binds to.
|
||||||
- **outbound_addr6=INTERFACE**: Specify the outbound interface slirp binds to (ipv6 traffic only).
|
- **outbound_addr6=INTERFACE**: Specify the outbound interface slirp binds to (ipv6 traffic only).
|
||||||
- **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp binds to.
|
- **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp binds to.
|
||||||
|
- **pasta[:OPTIONS,...]**: use **pasta**(1) to create a user-mode networking
|
||||||
|
stack. \
|
||||||
|
This is only supported in rootless mode. \
|
||||||
|
By default, IPv4 and IPv6 addresses and routes, as well as the pod interface
|
||||||
|
name, are copied from the host. If port forwarding isn't configured, ports
|
||||||
|
are forwarded dynamically as services are bound on either side (init
|
||||||
|
namespace or container namespace). Port forwarding preserves the original
|
||||||
|
source IP address. Options described in pasta(1) can be specified as
|
||||||
|
comma-separated arguments. \
|
||||||
|
In terms of pasta(1) options, **--config-net** is given by default, in
|
||||||
|
order to configure networking when the container is started, and
|
||||||
|
**--no-map-gw** is also assumed by default, to avoid direct access from
|
||||||
|
container to host using the gateway address. The latter can be overridden
|
||||||
|
by passing **--map-gw** in the pasta-specific options (despite not being an
|
||||||
|
actual pasta(1) option). \
|
||||||
|
Also, **-t none** and **-u none** are passed to disable
|
||||||
|
automatic port forwarding based on bound ports. Similarly, **-T none** and
|
||||||
|
**-U none** are given to disable the same functionality from container to
|
||||||
|
host. \
|
||||||
|
Some examples:
|
||||||
|
- **pasta:--map-gw**: Allow the container to directly reach the host using the
|
||||||
|
gateway address.
|
||||||
|
- **pasta:--mtu,1500**: Specify a 1500 bytes MTU for the _tap_ interface in
|
||||||
|
the container.
|
||||||
|
- **pasta:--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,-m,1500,--no-ndp,--no-dhcpv6,--no-dhcp**,
|
||||||
|
equivalent to default slirp4netns(1) options: disable IPv6, assign
|
||||||
|
`10.0.2.0/24` to the `tap0` interface in the container, with gateway
|
||||||
|
`10.0.2.3`, enable DNS forwarder reachable at `10.0.2.3`, set MTU to 1500
|
||||||
|
bytes, disable NDP, DHCPv6 and DHCP support.
|
||||||
|
- **pasta:-I,tap0,--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,--no-ndp,--no-dhcpv6,--no-dhcp**,
|
||||||
|
equivalent to default slirp4netns(1) options with Podman overrides: same as
|
||||||
|
above, but leave the MTU to 65520 bytes
|
||||||
|
- **pasta:-t,auto,-u,auto,-T,auto,-U,auto**: enable automatic port forwarding
|
||||||
|
based on observed bound ports from both host and container sides
|
||||||
|
- **pasta:-T,5201**: enable forwarding of TCP port 5201 from container to
|
||||||
|
host, using the loopback interface instead of the tap interface for improved
|
||||||
|
performance
|
||||||
|
|
||||||
**--no-cache**
|
**--no-cache**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -300,6 +300,43 @@ Valid _mode_ values are:
|
||||||
- **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp binds to.
|
- **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp binds to.
|
||||||
- **outbound_addr6=INTERFACE**: Specify the outbound interface slirp binds to (ipv6 traffic only).
|
- **outbound_addr6=INTERFACE**: Specify the outbound interface slirp binds to (ipv6 traffic only).
|
||||||
- **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp binds to.
|
- **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp binds to.
|
||||||
|
- **pasta[:OPTIONS,...]**: use **pasta**(1) to create a user-mode networking
|
||||||
|
stack. \
|
||||||
|
This is only supported in rootless mode. \
|
||||||
|
By default, IPv4 and IPv6 addresses and routes, as well as the pod interface
|
||||||
|
name, are copied from the host. If port forwarding isn't configured, ports
|
||||||
|
are forwarded dynamically as services are bound on either side (init
|
||||||
|
namespace or container namespace). Port forwarding preserves the original
|
||||||
|
source IP address. Options described in pasta(1) can be specified as
|
||||||
|
comma-separated arguments. \
|
||||||
|
In terms of pasta(1) options, **--config-net** is given by default, in
|
||||||
|
order to configure networking when the container is started, and
|
||||||
|
**--no-map-gw** is also assumed by default, to avoid direct access from
|
||||||
|
container to host using the gateway address. The latter can be overridden
|
||||||
|
by passing **--map-gw** in the pasta-specific options (despite not being an
|
||||||
|
actual pasta(1) option). \
|
||||||
|
Also, **-t none** and **-u none** are passed to disable
|
||||||
|
automatic port forwarding based on bound ports. Similarly, **-T none** and
|
||||||
|
**-U none** are given to disable the same functionality from container to
|
||||||
|
host. \
|
||||||
|
Some examples:
|
||||||
|
- **pasta:--map-gw**: Allow the container to directly reach the host using the
|
||||||
|
gateway address.
|
||||||
|
- **pasta:--mtu,1500**: Specify a 1500 bytes MTU for the _tap_ interface in
|
||||||
|
the container.
|
||||||
|
- **pasta:--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,-m,1500,--no-ndp,--no-dhcpv6,--no-dhcp**,
|
||||||
|
equivalent to default slirp4netns(1) options: disable IPv6, assign
|
||||||
|
`10.0.2.0/24` to the `tap0` interface in the container, with gateway
|
||||||
|
`10.0.2.3`, enable DNS forwarder reachable at `10.0.2.3`, set MTU to 1500
|
||||||
|
bytes, disable NDP, DHCPv6 and DHCP support.
|
||||||
|
- **pasta:-I,tap0,--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,--no-ndp,--no-dhcpv6,--no-dhcp**,
|
||||||
|
equivalent to default slirp4netns(1) options with Podman overrides: same as
|
||||||
|
above, but leave the MTU to 65520 bytes
|
||||||
|
- **pasta:-t,auto,-u,auto,-T,auto,-U,auto**: enable automatic port forwarding
|
||||||
|
based on observed bound ports from both host and container sides
|
||||||
|
- **pasta:-T,5201**: enable forwarding of TCP port 5201 from container to
|
||||||
|
host, using the loopback interface instead of the tap interface for improved
|
||||||
|
performance
|
||||||
|
|
||||||
**--os**="OS"
|
**--os**="OS"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,43 @@ Valid _mode_ values are:
|
||||||
- **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp binds to.
|
- **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp binds to.
|
||||||
- **outbound_addr6=INTERFACE**: Specify the outbound interface slirp binds to (ipv6 traffic only).
|
- **outbound_addr6=INTERFACE**: Specify the outbound interface slirp binds to (ipv6 traffic only).
|
||||||
- **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp binds to.
|
- **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp binds to.
|
||||||
|
- **pasta[:OPTIONS,...]**: use **pasta**(1) to create a user-mode networking
|
||||||
|
stack. \
|
||||||
|
This is only supported in rootless mode. \
|
||||||
|
By default, IPv4 and IPv6 addresses and routes, as well as the pod interface
|
||||||
|
name, are copied from the host. If port forwarding isn't configured, ports
|
||||||
|
are forwarded dynamically as services are bound on either side (init
|
||||||
|
namespace or container namespace). Port forwarding preserves the original
|
||||||
|
source IP address. Options described in pasta(1) can be specified as
|
||||||
|
comma-separated arguments. \
|
||||||
|
In terms of pasta(1) options, **--config-net** is given by default, in
|
||||||
|
order to configure networking when the container is started, and
|
||||||
|
**--no-map-gw** is also assumed by default, to avoid direct access from
|
||||||
|
container to host using the gateway address. The latter can be overridden
|
||||||
|
by passing **--map-gw** in the pasta-specific options (despite not being an
|
||||||
|
actual pasta(1) option). \
|
||||||
|
Also, **-t none** and **-u none** are passed to disable
|
||||||
|
automatic port forwarding based on bound ports. Similarly, **-T none** and
|
||||||
|
**-U none** are given to disable the same functionality from container to
|
||||||
|
host. \
|
||||||
|
Some examples:
|
||||||
|
- **pasta:--map-gw**: Allow the container to directly reach the host using the
|
||||||
|
gateway address.
|
||||||
|
- **pasta:--mtu,1500**: Specify a 1500 bytes MTU for the _tap_ interface in
|
||||||
|
the container.
|
||||||
|
- **pasta:--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,-m,1500,--no-ndp,--no-dhcpv6,--no-dhcp**,
|
||||||
|
equivalent to default slirp4netns(1) options: disable IPv6, assign
|
||||||
|
`10.0.2.0/24` to the `tap0` interface in the container, with gateway
|
||||||
|
`10.0.2.3`, enable DNS forwarder reachable at `10.0.2.3`, set MTU to 1500
|
||||||
|
bytes, disable NDP, DHCPv6 and DHCP support.
|
||||||
|
- **pasta:-I,tap0,--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,--no-ndp,--no-dhcpv6,--no-dhcp**,
|
||||||
|
equivalent to default slirp4netns(1) options with Podman overrides: same as
|
||||||
|
above, but leave the MTU to 65520 bytes
|
||||||
|
- **pasta:-t,auto,-u,auto,-T,auto,-U,auto**: enable automatic port forwarding
|
||||||
|
based on observed bound ports from both host and container sides
|
||||||
|
- **pasta:-T,5201**: enable forwarding of TCP port 5201 from container to
|
||||||
|
host, using the loopback interface instead of the tap interface for improved
|
||||||
|
performance
|
||||||
|
|
||||||
**--no-hosts**
|
**--no-hosts**
|
||||||
|
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -5,6 +5,7 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
github.com/containerd/containerd v1.7.2
|
github.com/containerd/containerd v1.7.2
|
||||||
github.com/containernetworking/cni v1.1.2
|
github.com/containernetworking/cni v1.1.2
|
||||||
|
github.com/containernetworking/plugins v1.3.0
|
||||||
github.com/containers/common v0.53.1-0.20230622141517-6eebb3712106
|
github.com/containers/common v0.53.1-0.20230622141517-6eebb3712106
|
||||||
github.com/containers/image/v5 v5.25.1-0.20230613183705-07ced6137083
|
github.com/containers/image/v5 v5.25.1-0.20230613183705-07ced6137083
|
||||||
github.com/containers/ocicrypt v1.1.7
|
github.com/containers/ocicrypt v1.1.7
|
||||||
|
|
@ -51,7 +52,6 @@ require (
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.5.4 // indirect
|
github.com/container-orchestrated-devices/container-device-interface v0.5.4 // indirect
|
||||||
github.com/containerd/cgroups v1.1.0 // indirect
|
github.com/containerd/cgroups v1.1.0 // indirect
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||||
github.com/containernetworking/plugins v1.3.0 // indirect
|
|
||||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||||
github.com/cyberphone/json-canonicalization v0.0.0-20230514072755-504adb8a8af1 // indirect
|
github.com/cyberphone/json-canonicalization v0.0.0-20230514072755-504adb8a8af1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
|
|
||||||
58
run_linux.go
58
run_linux.go
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containers/buildah/bind"
|
"github.com/containers/buildah/bind"
|
||||||
"github.com/containers/buildah/chroot"
|
"github.com/containers/buildah/chroot"
|
||||||
"github.com/containers/buildah/copier"
|
"github.com/containers/buildah/copier"
|
||||||
|
|
@ -22,9 +23,11 @@ import (
|
||||||
"github.com/containers/buildah/pkg/overlay"
|
"github.com/containers/buildah/pkg/overlay"
|
||||||
"github.com/containers/buildah/pkg/parse"
|
"github.com/containers/buildah/pkg/parse"
|
||||||
"github.com/containers/buildah/util"
|
"github.com/containers/buildah/util"
|
||||||
|
"github.com/containers/common/libnetwork/pasta"
|
||||||
"github.com/containers/common/libnetwork/resolvconf"
|
"github.com/containers/common/libnetwork/resolvconf"
|
||||||
"github.com/containers/common/libnetwork/slirp4netns"
|
"github.com/containers/common/libnetwork/slirp4netns"
|
||||||
nettypes "github.com/containers/common/libnetwork/types"
|
nettypes "github.com/containers/common/libnetwork/types"
|
||||||
|
netUtil "github.com/containers/common/libnetwork/util"
|
||||||
"github.com/containers/common/pkg/capabilities"
|
"github.com/containers/common/pkg/capabilities"
|
||||||
"github.com/containers/common/pkg/chown"
|
"github.com/containers/common/pkg/chown"
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
|
|
@ -516,19 +519,64 @@ func setupSlirp4netnsNetwork(netns, cid string, options []string) (func(), map[s
|
||||||
}, netStatus, nil
|
}, netStatus, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupPasta(netns string, options []string) (func(), map[string]nettypes.StatusBlock, error) {
|
||||||
|
defConfig, err := config.Default()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to get container config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pasta.Setup(&pasta.SetupOptions{
|
||||||
|
Config: defConfig,
|
||||||
|
Netns: netns,
|
||||||
|
ExtraOptions: options,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip string
|
||||||
|
err = ns.WithNetNSPath(netns, func(_ ns.NetNS) error {
|
||||||
|
// get the first ip in the netns and use this as our ip for /etc/hosts
|
||||||
|
ip = netUtil.GetLocalIP()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create fake status to make sure we get the correct ip in hosts
|
||||||
|
subnet := nettypes.IPNet{IPNet: net.IPNet{
|
||||||
|
IP: net.ParseIP(ip),
|
||||||
|
Mask: net.IPv4Mask(255, 255, 255, 0),
|
||||||
|
}}
|
||||||
|
netStatus := map[string]nettypes.StatusBlock{
|
||||||
|
slirp4netns.BinaryName: nettypes.StatusBlock{
|
||||||
|
Interfaces: map[string]nettypes.NetInterface{
|
||||||
|
"tap0": {
|
||||||
|
Subnets: []nettypes.NetAddress{{IPNet: subnet}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, netStatus, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Builder) runConfigureNetwork(pid int, isolation define.Isolation, options RunOptions, network, containerName string) (teardown func(), netStatus map[string]nettypes.StatusBlock, err error) {
|
func (b *Builder) runConfigureNetwork(pid int, isolation define.Isolation, options RunOptions, network, containerName string) (teardown func(), netStatus map[string]nettypes.StatusBlock, err error) {
|
||||||
netns := fmt.Sprintf("/proc/%d/ns/net", pid)
|
netns := fmt.Sprintf("/proc/%d/ns/net", pid)
|
||||||
var configureNetworks []string
|
var configureNetworks []string
|
||||||
|
|
||||||
name, networkOpts, hasOpts := strings.Cut(network, ":")
|
name, networkOpts, hasOpts := strings.Cut(network, ":")
|
||||||
|
var netOpts []string
|
||||||
|
if hasOpts {
|
||||||
|
netOpts = strings.Split(networkOpts, ",")
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case name == slirp4netns.BinaryName,
|
case name == slirp4netns.BinaryName,
|
||||||
isolation == IsolationOCIRootless && name == "":
|
isolation == IsolationOCIRootless && name == "":
|
||||||
var slirpOpts []string
|
return setupSlirp4netnsNetwork(netns, containerName, netOpts)
|
||||||
if hasOpts {
|
case name == pasta.BinaryName:
|
||||||
slirpOpts = strings.Split(networkOpts, ",")
|
return setupPasta(netns, netOpts)
|
||||||
}
|
|
||||||
return setupSlirp4netnsNetwork(netns, containerName, slirpOpts)
|
|
||||||
|
|
||||||
// Basically default case except we make sure to not split an empty
|
// Basically default case except we make sure to not split an empty
|
||||||
// name as this would return a slice with one empty string which is
|
// name as this would return a slice with one empty string which is
|
||||||
|
|
|
||||||
|
|
@ -6008,6 +6008,30 @@ _EOF
|
||||||
assert "$output" =~ "mtu 2000" "ip addr shows mtu 2000"
|
assert "$output" =~ "mtu 2000" "ip addr shows mtu 2000"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "bud with --network pasta" {
|
||||||
|
skip_if_no_runtime
|
||||||
|
skip_if_chroot
|
||||||
|
skip_if_root_environment "pasta only works rootless"
|
||||||
|
|
||||||
|
# FIXME: unskip when we have a new pasta version with:
|
||||||
|
# https://archives.passt.top/passt-dev/20230623082531.25947-2-pholzing@redhat.com/
|
||||||
|
skip "pasta bug prevents this from working"
|
||||||
|
|
||||||
|
_prefetch alpine
|
||||||
|
|
||||||
|
# pasta by default copies the host ip
|
||||||
|
ip=$(hostname -I | cut -f 1 -d " ")
|
||||||
|
|
||||||
|
run_buildah bud $WITH_POLICY_JSON --network pasta $BUDFILES/network
|
||||||
|
assert "$output" =~ "$ip" "ip addr shows default subnet"
|
||||||
|
|
||||||
|
# check some entwork options, it accepts raw pasta(1) areguments
|
||||||
|
mac="9a:dd:31:ea:92:98"
|
||||||
|
run_buildah bud $WITH_POLICY_JSON --network pasta:--mtu,2000,--ns-mac-addr,"$mac" $BUDFILES/network
|
||||||
|
assert "$output" =~ "$mac" "ip addr shows custom mac address"
|
||||||
|
assert "$output" =~ "mtu 2000" "ip addr shows mtu 2000"
|
||||||
|
}
|
||||||
|
|
||||||
@test "bud WORKDIR owned by USER" {
|
@test "bud WORKDIR owned by USER" {
|
||||||
_prefetch alpine
|
_prefetch alpine
|
||||||
target=alpine-image
|
target=alpine-image
|
||||||
|
|
|
||||||
|
|
@ -711,6 +711,24 @@ function configure_and_check_user() {
|
||||||
run_buildah rm -a
|
run_buildah rm -a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "run check /etc/hosts with --network pasta" {
|
||||||
|
skip_if_no_runtime
|
||||||
|
skip_if_chroot
|
||||||
|
skip_if_root_environment "pasta only works rootless"
|
||||||
|
|
||||||
|
# FIXME: unskip when we have a new pasta version with:
|
||||||
|
# https://archives.passt.top/passt-dev/20230623082531.25947-2-pholzing@redhat.com/
|
||||||
|
skip "pasta bug prevents this from working"
|
||||||
|
|
||||||
|
run_buildah from --quiet --pull=false $WITH_POLICY_JSON debian
|
||||||
|
cid=$output
|
||||||
|
|
||||||
|
local hostname=h-$(random_string)
|
||||||
|
ip=$(hostname -I | cut -f 1 -d " ")
|
||||||
|
run_buildah run --network pasta --hostname $hostname $cid cat /etc/hosts
|
||||||
|
assert "$output" =~ "$ip[[:blank:]]$hostname $cid"
|
||||||
|
}
|
||||||
|
|
||||||
@test "run check /etc/resolv.conf" {
|
@test "run check /etc/resolv.conf" {
|
||||||
skip_if_rootless_environment
|
skip_if_rootless_environment
|
||||||
skip_if_no_runtime
|
skip_if_no_runtime
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// pasta.go - Start pasta(1) for user-mode connectivity
|
||||||
|
//
|
||||||
|
// Copyright (c) 2022 Red Hat GmbH
|
||||||
|
// Author: Stefano Brivio <sbrivio@redhat.com>
|
||||||
|
|
||||||
|
// This file has been imported from the podman repository
|
||||||
|
// (libpod/networking_pasta_linux.go), for the full history see there.
|
||||||
|
|
||||||
|
package pasta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/common/libnetwork/types"
|
||||||
|
"github.com/containers/common/pkg/config"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BinaryName = "pasta"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SetupOptions struct {
|
||||||
|
// Config used to get pasta options and binary path via HelperBinariesDir
|
||||||
|
Config *config.Config
|
||||||
|
// Netns is the path to the container Netns
|
||||||
|
Netns string
|
||||||
|
// Ports that should be forwarded in the container
|
||||||
|
Ports []types.PortMapping
|
||||||
|
// ExtraOptions are pasta(1) cli options, these will be appended after the
|
||||||
|
// pasta options from containers.conf to allow some form of overwrite.
|
||||||
|
ExtraOptions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup start the pasta process for the given netns.
|
||||||
|
// The pasta binary is looked up in the HelperBinariesDir and $PATH.
|
||||||
|
// Note that there is no need any special cleanup logic, the pasta process will
|
||||||
|
// automatically exit when the netns path is deleted.
|
||||||
|
func Setup(opts *SetupOptions) error {
|
||||||
|
NoTCPInitPorts := true
|
||||||
|
NoUDPInitPorts := true
|
||||||
|
NoTCPNamespacePorts := true
|
||||||
|
NoUDPNamespacePorts := true
|
||||||
|
NoMapGW := true
|
||||||
|
|
||||||
|
path, err := opts.Config.FindHelperBinary(BinaryName, true)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find pasta, the network namespace can't be configured: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdArgs := []string{}
|
||||||
|
cmdArgs = append(cmdArgs, "--config-net")
|
||||||
|
|
||||||
|
for _, i := range opts.Ports {
|
||||||
|
protocols := strings.Split(i.Protocol, ",")
|
||||||
|
for _, protocol := range protocols {
|
||||||
|
var addr string
|
||||||
|
|
||||||
|
if i.HostIP != "" {
|
||||||
|
addr = fmt.Sprintf("%s/", i.HostIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch protocol {
|
||||||
|
case "tcp":
|
||||||
|
cmdArgs = append(cmdArgs, "-t")
|
||||||
|
case "udp":
|
||||||
|
cmdArgs = append(cmdArgs, "-u")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("can't forward protocol: %s", protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := fmt.Sprintf("%s%d-%d:%d-%d", addr,
|
||||||
|
i.HostPort,
|
||||||
|
i.HostPort+i.Range-1,
|
||||||
|
i.ContainerPort,
|
||||||
|
i.ContainerPort+i.Range-1)
|
||||||
|
cmdArgs = append(cmdArgs, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first append options set in the config
|
||||||
|
cmdArgs = append(cmdArgs, opts.Config.Network.PastaOptions...)
|
||||||
|
// then append the ones that were set on the cli
|
||||||
|
cmdArgs = append(cmdArgs, opts.ExtraOptions...)
|
||||||
|
|
||||||
|
for i, opt := range cmdArgs {
|
||||||
|
switch opt {
|
||||||
|
case "-t", "--tcp-ports":
|
||||||
|
NoTCPInitPorts = false
|
||||||
|
case "-u", "--udp-ports":
|
||||||
|
NoUDPInitPorts = false
|
||||||
|
case "-T", "--tcp-ns":
|
||||||
|
NoTCPNamespacePorts = false
|
||||||
|
case "-U", "--udp-ns":
|
||||||
|
NoUDPNamespacePorts = false
|
||||||
|
case "--map-gw":
|
||||||
|
NoMapGW = false
|
||||||
|
// not an actual pasta(1) option
|
||||||
|
cmdArgs = append(cmdArgs[:i], cmdArgs[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if NoTCPInitPorts {
|
||||||
|
cmdArgs = append(cmdArgs, "-t", "none")
|
||||||
|
}
|
||||||
|
if NoUDPInitPorts {
|
||||||
|
cmdArgs = append(cmdArgs, "-u", "none")
|
||||||
|
}
|
||||||
|
if NoTCPNamespacePorts {
|
||||||
|
cmdArgs = append(cmdArgs, "-T", "none")
|
||||||
|
}
|
||||||
|
if NoUDPNamespacePorts {
|
||||||
|
cmdArgs = append(cmdArgs, "-U", "none")
|
||||||
|
}
|
||||||
|
if NoMapGW {
|
||||||
|
cmdArgs = append(cmdArgs, "--no-map-gw")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdArgs = append(cmdArgs, "--netns", opts.Netns)
|
||||||
|
|
||||||
|
logrus.Debugf("pasta arguments: %s", strings.Join(cmdArgs, " "))
|
||||||
|
|
||||||
|
// pasta forks once ready, and quits once we delete the target namespace
|
||||||
|
_, err = exec.Command(path, cmdArgs...).Output()
|
||||||
|
if err != nil {
|
||||||
|
exitErr := &exec.ExitError{}
|
||||||
|
if errors.As(err, &exitErr) {
|
||||||
|
return fmt.Errorf("pasta failed with exit code %d:\n%s",
|
||||||
|
exitErr.ExitCode(), exitErr.Stderr)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to start pasta: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -98,6 +98,7 @@ github.com/containers/common/libnetwork/etchosts
|
||||||
github.com/containers/common/libnetwork/internal/util
|
github.com/containers/common/libnetwork/internal/util
|
||||||
github.com/containers/common/libnetwork/netavark
|
github.com/containers/common/libnetwork/netavark
|
||||||
github.com/containers/common/libnetwork/network
|
github.com/containers/common/libnetwork/network
|
||||||
|
github.com/containers/common/libnetwork/pasta
|
||||||
github.com/containers/common/libnetwork/resolvconf
|
github.com/containers/common/libnetwork/resolvconf
|
||||||
github.com/containers/common/libnetwork/slirp4netns
|
github.com/containers/common/libnetwork/slirp4netns
|
||||||
github.com/containers/common/libnetwork/types
|
github.com/containers/common/libnetwork/types
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue