Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cri-o needs cni workaround #1804

Closed
mike-nguyen opened this issue Sep 19, 2018 · 28 comments
Closed

cri-o needs cni workaround #1804

mike-nguyen opened this issue Sep 19, 2018 · 28 comments
Labels
lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale.

Comments

@mike-nguyen
Copy link

mike-nguyen commented Sep 19, 2018

Description
Port forwarding from the container to the host doesn't seem to be working. I need to run with hostNetwork: true to workaround it.

I am using kubelet to deploy a static nginx pod with containerPort:80 and hostPort:80. When kubelet is using docker, I can curl http://localhost:80 and I get the nginx default page. When I switch kubelet to run with crio, I can see the container running but curl http://localhost:80 does not respond. This is a similar issue I was seeing with exposing ports using podman.

cri-o might need a version of containers/podman#1431
mheon mentioned:

You can probably vendor libpod's pkg/firewall and then modify your netns setup code to run it though you might need to store more information than you do right now, libpod already stores the whole CNI response CRI-O does not

Steps to reproduce the issue:
1.

cat << EOF > /etc/kubernetes/manifests/static-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: static-web
 labels:
   role: myrole
spec:
 containers:
   - name: web
     image: docker.io/nginx
     ports:
       - name: web
         containerPort: 80
         hostPort: 80
EOF
cat << EOF > /etc/kubernetes/kubeletconfig
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
clusterDomain: kube.local
clusterDNS: [10.254.0.10]
cgroupDriver: systemd
staticPodPath: /etc/kubernetes/manifests/
authorization:
  mode: AlwaysAllow
authentication:
  anonymous:
    enabled: true
  webhook:
    enabled: false
EOF

start kubelet
/usr/bin/hyperkube kubelet --config /etc/kubernetes/kubeletconfig --container-runtime=remote --container-runtime-endpoint=unix:///var/run/crio/crio.sock --runtime-request-timeout=10m
4
curl http://localhost:80

Describe the results you received:
Cannot access the nginx container through the exposed host port

Describe the results you expected:
Able to access the nginx container through the exposed host port

Additional information you deem important (e.g. issue happens only occasionally):
Always happens

Output of crio --version:

crio version 1.11.4
cri-o-1.11.4-2.rhaos3.11.gite0c89d8.el7_5.x86_64

Additional Environment Information
RHCOS 4.0.5796 in AWS

@mike-nguyen
Copy link
Author

/cc @mrunalp @ashcrow @mheon

@giuseppe
Copy link
Member

giuseppe commented Oct 2, 2018

could you verify if it works when you try to access another host IP that is not localhost? For example, it works when I use the local IP 192.168.125.240 but it hangs on 127.0.0.1.

Using the firewall plugin from containernetworking/plugins#75 doesn't seem to make a difference.

/cc @dcbw

@mike-nguyen
Copy link
Author

@giuseppe I tested on AWS and was able to get to my webserver through the AWS private IP address. So it only hangs on localhost/127.0.0.1 like you said

@dcbw
Copy link
Contributor

dcbw commented Oct 2, 2018

@mike-nguyen can you run 'iptables-save' on the node and post the results somewhere?

@mike-nguyen
Copy link
Author

mike-nguyen commented Oct 2, 2018

@dcbw
Copy link
Contributor

dcbw commented Oct 2, 2018

@mike-nguyen can you "tcpdump -vvvne cni0" and then do the curl localhost:80?

@mike-nguyen
Copy link
Author

@dcbw tcpdump -vvvne -i cn0 gives me no output when I curl localhost:80

@dcbw
Copy link
Contributor

dcbw commented Oct 3, 2018

@mike-nguyen Ok, so clearly the traffic isn't even getting to cni0. That helps narrow things down.

@giuseppe
Copy link
Member

giuseppe commented Oct 4, 2018

@dcbw I got it to work with the following changes (tested manually):

@@ -31,9 +31,10 @@
 -A PREROUTING -m comment --comment "kube hostport portals" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS
 -A OUTPUT -m comment --comment "kube hostport portals" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS
 -A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
+-A POSTROUTING -d 10.88.0.0/16 -m comment --comment "name: \"crio-bridge\" id: \"bf266838ac8d1fc99c6efee82fbe9333d55ae3eecec3248e9e2f568561d1ad8d\"" -j CNI-08a451cf4b6dd952bc70028c
 -A POSTROUTING -s 10.88.0.0/16 -m comment --comment "name: \"crio-bridge\" id: \"bf266838ac8d1fc99c6efee82fbe9333d55ae3eecec3248e9e2f568561d1ad8d\"" -j CNI-08a451cf4b6dd952bc70028c
 -A POSTROUTING -s 127.0.0.0/8 -o lo -m comment --comment "SNAT for localhost access to hostports" -j MASQUERADE
--A CNI-08a451cf4b6dd952bc70028c -d 10.88.0.0/16 -m comment --comment "name: \"crio-bridge\" id: \"bf266838ac8d1fc99c6efee82fbe9333d55ae3eecec3248e9e2f568561d1ad8d\"" -j ACCEPT
+-A CNI-08a451cf4b6dd952bc70028c -d 10.88.0.0/16 -m comment --comment "name: \"crio-bridge\" id: \"bf266838ac8d1fc99c6efee82fbe9333d55ae3eecec3248e9e2f568561d1ad8d\"" -j MASQUERADE
 -A CNI-08a451cf4b6dd952bc70028c ! -d 224.0.0.0/4 -m comment --comment "name: \"crio-bridge\" id: \"bf266838ac8d1fc99c6efee82fbe9333d55ae3eecec3248e9e2f568561d1ad8d\"" -j MASQUERADE
 -A KUBE-HOSTPORTS -p tcp -m comment --comment "k8s_nginx-rhel7_default_6cce1e24d665240f8d2e114edbf52189_3_ hostport 8081" -m tcp --dport 8081 -j KUBE-HP-WWG7ZHMF34UGM273
 -A KUBE-HP-WWG7ZHMF34UGM273 -s 10.88.0.59/32 -m comment --comment "k8s_nginx-rhel7_default_6cce1e24d665240f8d2e114edbf52189_3_ hostport 8081" -j KUBE-MARK-MASQ

@giuseppe
Copy link
Member

@dcbw does the change look fine?

@mike-nguyen
Copy link
Author

@dcbw -- looks like giuseppe has something that may work. What do you think?

@ashcrow
Copy link
Contributor

ashcrow commented Nov 1, 2018

If we can get this looked at soon I'd appreciate it. This work blocks a card @mike-nguyen is trying to close out.

@mrunalp
Copy link
Member

mrunalp commented Jan 5, 2019

@dcbw wdyt of @giuseppe changes?

@dcbw
Copy link
Contributor

dcbw commented Jan 15, 2019

@squeed

@squeed
Copy link
Contributor

squeed commented Jan 16, 2019

You need to masquerade, but only when the source address is 127.0.0.1. Check out https://github.com/containernetworking/plugins/tree/master/plugins/meta/portmap#snat-masquerade

@mrunalp
Copy link
Member

mrunalp commented Mar 8, 2019

@squeed how do we get this fixed in the CNI plugins?

@squeed
Copy link
Contributor

squeed commented Mar 11, 2019

The CNI plugins already handle this; what version and what CNI configuration are you using?

@mike-nguyen
Copy link
Author

The CNI plugins already handle this; what version and what CNI configuration are you using?

@squeed Sorry we swapped jira boards and the card got lost in the migration. Let me know if you need anymore information.

$ rpm -q containernetworking-plugins 
containernetworking-plugins-0.7.4-3.git9ebe139.module+el8.0.0+2958+4e823551.x86_64

These are the configs in /etc/cni/net.d:

100-crio-bridge.conf

{
    "cniVersion": "0.3.0",
    "name": "crio-bridge",
    "type": "bridge",
    "bridge": "cni0",
    "isGateway": true,
    "ipMasq": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.88.0.0/16",
        "routes": [
            { "dst": "0.0.0.0/0" }
        ]
    }
}

200-loopback.conf

{
    "cniVersion": "0.3.0",
    "type": "loopback"
}

87-podman-bridge.conflist

{
    "cniVersion": "0.3.0",
    "name": "podman",
    "plugins": [
      {
        "type": "bridge",
        "bridge": "cni0",
        "isGateway": true,
        "ipMasq": true,
        "ipam": {
            "type": "host-local",
            "subnet": "10.88.0.0/16",
            "routes": [
                { "dst": "0.0.0.0/0" }
            ]
        }
      },
      {
        "type": "portmap",
        "capabilities": {
          "portMappings": true
        }
      }
    ]
}

@bkmeneguello
Copy link

I'm seeing this error with:

  • Centos 7
  • containernetworking-plugins-0.8.1-2.el7.centos.x86_64
  • cri-o-1.15.2-1.el7.x86_64
  • kubelet-1.16.2-0.x86_64
  • quay.io/coreos/flannel:v0.11.0-amd64

Any solution available?

@prasenforu
Copy link

so far any workaround ?

@haircommander
Copy link
Member

@squeed @dcbw @giuseppe any further thoughts here?

@amartinunowhy
Copy link

amartinunowhy commented Dec 3, 2021

Hello. I'm not sure I'm having the same issue, but looking at what podman does and the differences, I found that by adding the following ip-tables rule, it works :

 -A KUBE-HP-GSFDA2UGA5JYS6WS -s 10.85.1.12/32 -m comment --comment "k8s_nginx-sandbox_default_hdishd83djaidwnduwk28bcsb_1_ hostport 8089" -j KUBE-MARK-MASQ
+ -A KUBE-HP-GSFDA2UGA5JYS6WS -s 127.0.0.1/32 -m comment --comment "k8s_nginx-sandbox_default_hdishd83djaidwnduwk28bcsb_1_ hostport 8089" -j KUBE-MARK-MASQ

My understanding is that KUBE-MARK-MASQ adds a mark:

-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000

And that the mark allows the following rules to be run:

-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully

and in particular the last one. I don't really understand what it does but it may be because as 127.0.0.1 exists in the container, without masquerading the source IP the response packets would never go out of it.

Hope it helps.

@amartinunowhy
Copy link

Hi again. Looking at the source code, I would add the following code here: https://github.com/cri-o/cri-o/blob/main/internal/hostport/hostport_manager.go#L151

// SNAT if the traffic comes from the host through localhost (127.0.0.1)
if pm.HostIP == "" || pm.HostIP == "0.0.0.0" || pm.HostIP == "::"  || pm.HostIP == "127.0.0.1" {
	writeLine(natRules, "-A", string(chain),
		"-m", "comment", "--comment", fmt.Sprintf(`"%s hostport %d"`, podFullName, pm.HostPort),
		"-s 127.0.0.1",
		"-j", string(iptablesproxy.KubeMarkMasqChain))
}

I'm sorry but I am unable to test myself.

@haircommander
Copy link
Member

thanks for looking into it! would you be interested in opening a PR @amartinunowhy ? cc @aojea

@aojea
Copy link
Contributor

aojea commented Dec 4, 2021

/assign
I'm on vacation until end of next week, but I'll take a look at this, I vaguely remember kubelet is also blocking some 127.0.0.1 traffic due to a CVE, I want to check first if that can impact this

@github-actions
Copy link

A friendly reminder that this issue had no activity for 30 days.

@github-actions github-actions bot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Jun 30, 2022
@aojea
Copy link
Contributor

aojea commented Jun 30, 2022

/unassign

I'm sorry but I didn't have time for this, squeed reference to the cni portmap plugin is right

https://github.com/containernetworking/plugins/blob/9c59728d39b729558bb712c79e2c80acc0864191/plugins/meta/portmap/portmap.go#L77-L99

and seems to match the solutions proposed

#1804 (comment)

I'm happy to act as a reviewer, but I will not able to send PRs for this

@haircommander
Copy link
Member

it doesn't seem like this is a very high prio bug (there aren't that many people looking for a fix). It also doesn't seem we have the bandwidth to fix the bug. If someone else sees the bug and wants it fixed, please reopen. Otherwise, I'm leaving it closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale.
Projects
None yet
Development

No branches or pull requests