背景

在搭建了 Kubernetes 集群后,在 master 节点上部署了 Dashboard。我们需要在办公网内访问它,修改 service 端口暴露为 NodePort 模式后就可以达到目的。由于 master 节点部署在公网,我们希望对 NodePort 暴露的端口做限制,换句话说,只允许办公网内的 ip 对其进行访问。开始以为会很简单,用 iptables 在 INPUT 链上加一个 DROP 和 ACCEPT 操作就可以了,但事实上并非如此。

NodePort

查看 kubernetes-dashboard 命名空间里的 service,kubernetes 创建了一个名为 kubernetes-dashboard 的 NodePort,在主机端口上监听50000端口。

1
2
3
4
root@localhost:~# kubectl get svc --namespace=kubernetes-dashboard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dashboard-metrics-scraper ClusterIP 10.110.216.172 <none> 8000/TCP 18h
kubernetes-dashboard NodePort 10.97.249.27 <none> 443:50000/TCP 18h

只要我们访问 MASTER-IP:50000 就可以进入 Dashboard,是在 iptables 里经历了很多条规则才到达的。

  1. 经过 PREROUTING 进入 KUBE-SERVICES。由于 nat 在 filter 之前,所以在 INPUT 链做限制不会起作用。

    1
    2
    3
    Chain PREROUTING (policy ACCEPT)
    target prot opt source destination
    KUBE-SERVICES all -- anywhere anywhere /* kubernetes service portals */

    对应的 iptables 规则( iptables-save 可查看)

    1
    -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
  2. 经过 KUBE-SERVICES 进入 KUBE-NODEPORTS

    1
    2
    3
    4
    Chain KUBE-SERVICES (2 references)
    target prot opt source destination
    ......
    KUBE-NODEPORTS all -- anywhere anywhere /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL

    对应的 iptables 规则,可以看到其会是链里的最后一条规则,只有当上边的所有服务规则都不匹配时才会进入 NODEPORTS

    1
    -A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
  3. 经过 KUBE-NODEPORTS 后,首先经过 KUBE-MARK-MASQ 打上标签,之后进入 KUBE-SVC-CEZPIJSAUFW5MYPQ

    1
    2
    3
    4
    Chain KUBE-NODEPORTS (1 references)
    target prot opt source destination
    KUBE-MARK-MASQ tcp -- anywhere anywhere /* kubernetes-dashboard/kubernetes-dashboard */ tcp dpt:50000
    KUBE-SVC-CEZPIJSAUFW5MYPQ tcp -- anywhere anywhere /* kubernetes-dashboard/kubernetes-dashboard */ tcp dpt:50000

    对应的 iptables 规则

    1
    2
     -A KUBE-NODEPORTS -p tcp -m comment --comment "kubernetes-dashboard/kubernetes-dashboard" -m tcp --dport 50000 -j KUBE-MARK-MASQ
    -A KUBE-NODEPORTS -p tcp -m comment --comment "kubernetes-dashboard/kubernetes-dashboard" -m tcp --dport 50000 -j KUBE-SVC-CEZPIJSAUFW5MYPQ
  4. 经过 KUBE-SVC-CEZPIJSAUFW5MYPQ 进入 KUBE-SEP-QXJL632FHGWBBYEF

    1
    2
    3
    Chain KUBE-SVC-CEZPIJSAUFW5MYPQ (2 references)
    target prot opt source destination
    KUBE-SEP-QXJL632FHGWBBYEF all -- anywhere anywhere /* kubernetes-dashboard/kubernetes-dashboard */

    对应的 iptables 规则

    1
    -A KUBE-SVC-CEZPIJSAUFW5MYPQ -m comment --comment "kubernetes-dashboard/kubernetes-dashboard" -j KUBE-SEP-QXJL632FHGWBBYEF
  5. 经过 KUBE-SEP-QXJL632FHGWBBYEF 的 DNAT 操作转给了 10.244.0.12。正是 kubernetes-dashboard pods 的 IP。

    1
    2
    3
    4
    Chain KUBE-SEP-QXJL632FHGWBBYEF (1 references)
    target prot opt source destination
    KUBE-MARK-MASQ all -- 10.244.0.12 anywhere /* kubernetes-dashboard/kubernetes-dashboard */
    DNAT tcp -- anywhere anywhere /* kubernetes-dashboard/kubernetes-dashboard */ tcp to:10.244.0.12:8443

    对应的 iptables 规则

    1
    2
    -A KUBE-SEP-QXJL632FHGWBBYEF -s 10.244.0.12/32 -m comment --comment "kubernetes-dashboard/kubernetes-dashboard" -j KUBE-MARK-MASQ
    -A KUBE-SEP-QXJL632FHGWBBYEF -p tcp -m comment --comment "kubernetes-dashboard/kubernetes-dashboard" -m tcp -j DNAT --to-destination 10.244.0.12:8443

解决问题

在知道了原理后,再要解决我们的问题就很容易了,方式其实也有很多种,我直接选择了最粗暴的,那就是在 PREROUTING 中加入一条规则,将不属于公司办公网 sip 的请求直接 DNAT 到65535,一个没有运行任何服务的端口。就解决了问题。

1
iptables -t nat -I PREROUTING -p tcp ! -s x.x.x.x/30 --dport 50000 -j REDIRECT --to-ports 65535

Reference

https://juejin.cn/post/6844904098605563912
https://zhaohuabing.com/istio-practice/content/ingress/nodeport.html
https://www.jianshu.com/p/c6d560d12d50
https://developer.aliyun.com/article/745086