Skip to content

Commit cc3ae88

Browse files
committed
Validate whether process belongs to the container's NetNS
ignores any sockets created in non-container NetNS including nested one. Signed-off-by: Naoki MATSUMOTO <[email protected]>
1 parent 8c74716 commit cc3ae88

File tree

4 files changed

+96
-11
lines changed

4 files changed

+96
-11
lines changed

pkg/bypass4netns/bypass4netns.go

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,19 @@ func (h *notifHandler) registerSocket(pid int, sockfd int, syscallName string) (
417417
sock.state = NotBypassable
418418
logger.Debugf("failed to get socket args err=%q", err)
419419
} else {
420-
if sockDomain != syscall.AF_INET && sockDomain != syscall.AF_INET6 {
420+
// compare process's netns is same or not with container init process's netns
421+
isSameNetNS, err := util.SameNetNS(h.containerInitPid, pid)
422+
if err != nil {
423+
logger.Errorf("failed to check NetNS: err=%q", err)
424+
sock.state = NotBypassable
425+
}
426+
427+
// check the process is executed with container's netns.
428+
// processes with nested netns are not handled by bypass4netns
429+
if !isSameNetNS {
430+
logger.Infof("process seems to be executed in other netns. socket is NotBypassable and ignored")
431+
sock.state = NotBypassable
432+
} else if sockDomain != syscall.AF_INET && sockDomain != syscall.AF_INET6 {
421433
// non IP sockets are not handled.
422434
sock.state = NotBypassable
423435
logger.Debugf("socket domain=0x%x", sockDomain)
@@ -629,6 +641,8 @@ type Handler struct {
629641

630642
// key is child port
631643
forwardingPorts map[int]ForwardPortMapping
644+
645+
ContainerInitPid int
632646
}
633647

634648
// NewHandler creates new seccomp notif handler
@@ -640,6 +654,7 @@ func NewHandler(socketPath, comSocketPath, tracerAgentLogPath string) *Handler {
640654
ignoredSubnets: []net.IPNet{},
641655
forwardingPorts: map[int]ForwardPortMapping{},
642656
readyFd: -1,
657+
ContainerInitPid: -1,
643658
}
644659

645660
return &handler
@@ -711,6 +726,10 @@ type notifHandler struct {
711726

712727
// cache pidfd to reduce latency. key is pid.
713728
pidInfos map[int]pidInfo
729+
730+
// container init process's pid
731+
// used to check whether netns is container or not.
732+
containerInitPid int
714733
}
715734

716735
type containerInterface struct {
@@ -732,14 +751,15 @@ type pidInfo struct {
732751
tgid int
733752
}
734753

735-
func (h *Handler) newNotifHandler(fd uintptr, state *specs.ContainerProcessState) *notifHandler {
754+
func (h *Handler) newNotifHandler(fd uintptr, state *specs.ContainerProcessState, containerInitPid int) *notifHandler {
736755
notifHandler := notifHandler{
737-
fd: libseccomp.ScmpFd(fd),
738-
state: state,
739-
forwardingPorts: map[int]ForwardPortMapping{},
740-
processes: map[int]*processStatus{},
741-
memfds: map[int]int{},
742-
pidInfos: map[int]pidInfo{},
756+
fd: libseccomp.ScmpFd(fd),
757+
state: state,
758+
forwardingPorts: map[int]ForwardPortMapping{},
759+
processes: map[int]*processStatus{},
760+
memfds: map[int]int{},
761+
pidInfos: map[int]pidInfo{},
762+
containerInitPid: containerInitPid,
743763
}
744764
notifHandler.nonBypassable = nonbypassable.New(h.ignoredSubnets)
745765
notifHandler.nonBypassableAutoUpdate = h.ignoredSubnetsAutoUpdate
@@ -793,8 +813,17 @@ func (h *Handler) StartHandle(c2cConfig *C2CConnectionHandleConfig, multinodeCon
793813
continue
794814
}
795815

816+
// state.Pid can be the process in nested netns when executed with 'ip netns exec'.
817+
// so, we cannot distinguish container netns and nested netns with simply comparing state.Pid and hooked process's pid
818+
// Instead of state.Pid, init process's pid should be used.
819+
// bypass4netns recognizes the first process as a init process.
820+
if h.ContainerInitPid < 0 {
821+
h.ContainerInitPid = state.Pid
822+
logrus.Infof("ContainerInitPid is %d", h.ContainerInitPid)
823+
}
824+
796825
logrus.Infof("Received new seccomp fd: %v", newFd)
797-
notifHandler := h.newNotifHandler(newFd, state)
826+
notifHandler := h.newNotifHandler(newFd, state, h.ContainerInitPid)
798827
notifHandler.c2cConnections = c2cConfig
799828
notifHandler.multinode = multinodeConfig
800829
if notifHandler.multinode.Enable {

pkg/util/util.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,16 @@ func ShrinkID(id string) string {
2020
}
2121

2222
func SameUserNS(pidX, pidY int) (bool, error) {
23-
nsX := fmt.Sprintf("/proc/%d/ns/user", pidX)
24-
nsY := fmt.Sprintf("/proc/%d/ns/user", pidY)
23+
return sameNS(pidX, pidY, "user")
24+
}
25+
26+
func SameNetNS(pidX, pidY int) (bool, error) {
27+
return sameNS(pidX, pidY, "net")
28+
}
29+
30+
func sameNS(pidX, pidY int, nsName string) (bool, error) {
31+
nsX := fmt.Sprintf("/proc/%d/ns/%s", pidX, nsName)
32+
nsY := fmt.Sprintf("/proc/%d/ns/%s", pidY, nsName)
2533
nsXResolved, err := os.Readlink(nsX)
2634
if err != nil {
2735
return false, err

test/DockerfileNestedNetNS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM ubuntu:22.04
2+
3+
RUN apt update && apt upgrade -y
4+
RUN apt install -y netcat iproute2 tcpdump iperf3

test/run_test.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,47 @@ echo "===== multinode test (single node) ===="
211211
systemctl --user stop etcd.service
212212
systemctl --user reset-failed
213213
)
214+
215+
echo "===== nested netns test ===="
216+
(
217+
CONTAINER_NAME="test-nested"
218+
set +e
219+
nerdctl rm -f $CONTAINER_NAME
220+
systemctl --user stop run-iperf3
221+
systemctl --user stop run-bypass4netnsd
222+
systemctl --user reset-failed
223+
set -ex
224+
225+
226+
IMAGE_NAME="b4ns:nested"
227+
nerdctl build -f ./DockerfileNestedNetNS -t $IMAGE_NAME .
228+
229+
systemd-run --user --unit run-bypass4netnsd bypass4netnsd
230+
sleep 1
231+
nerdctl run --privileged --annotation nerdctl/bypass4netns=true -d -p 5202:5201 --name $CONTAINER_NAME $IMAGE_NAME sleep infinity
232+
233+
# with container's netns
234+
systemd-run --user --unit run-iperf3 nerdctl exec $CONTAINER_NAME iperf3 -s
235+
sleep 1
236+
iperf3 -c localhost -t 1 -p 5202 --connect-timeout 1000 # it must success to connect.
237+
systemctl --user stop run-iperf3
238+
systemctl --user reset-failed
239+
240+
# with nested netns
241+
nerdctl exec $CONTAINER_NAME mkdir /sys2
242+
nerdctl exec $CONTAINER_NAME mount -t sysfs --make-private /sys2
243+
nerdctl exec $CONTAINER_NAME ip netns add nested
244+
systemd-run --user --unit run-iperf3 nerdctl exec $CONTAINER_NAME ip netns exec nested iperf3 -s
245+
sleep 1
246+
set +e
247+
iperf3 -c localhost -t 1 -p 5202 --connect-timeout 1000 # it must fail
248+
if [ $? -eq 0 ]; then
249+
echo "iperf3 must not success to connect."
250+
exit 1
251+
fi
252+
set -e
253+
systemctl --user stop run-iperf3
254+
255+
nerdctl rm -f test-nested
256+
systemctl --user reset-failed
257+
)

0 commit comments

Comments
 (0)