惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Schneier on Security
Schneier on Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
H
Hacker News: Front Page
Google DeepMind News
Google DeepMind News
雷峰网
雷峰网
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cisco Talos Blog
Cisco Talos Blog
T
Tenable Blog
G
Google Developers Blog
A
About on SuperTechFans
The Cloudflare Blog
S
Securelist
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
Cisco Blogs
H
Hackread – Cybersecurity News, Data Breaches, AI and More
aimingoo的专栏
aimingoo的专栏
云风的 BLOG
云风的 BLOG
Forbes - Security
Forbes - Security
腾讯CDC
Application and Cybersecurity Blog
Application and Cybersecurity Blog
V
Vulnerabilities – Threatpost
IT之家
IT之家
博客园_首页
P
Proofpoint News Feed
P
Privacy & Cybersecurity Law Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Project Zero
Project Zero
月光博客
月光博客
NISL@THU
NISL@THU
爱范儿
爱范儿
S
Secure Thoughts
K
Kaspersky official blog
Security Latest
Security Latest
T
Tailwind CSS Blog
博客园 - Franky
D
Darknet – Hacking Tools, Hacker News & Cyber Security
TaoSecurity Blog
TaoSecurity Blog
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog RSS Feed
S
SegmentFault 最新的问题
H
Help Net Security
T
Tor Project blog
L
LINUX DO - 热门话题
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
O
OpenAI News
S
Schneier on Security

陈少文的网站

巨变与机遇的未来十年 Kubernetes 平台管理软件压力测试方案 使用镜像部署 Hexo 静态页面 终于等到你 - GitHub 镜像仓库服务(ghcr.io) 一起来学 Go --(6)Interface 一起来学 Go --(5)Goroutine 和 Channel 什么是函数式编程 如何在 Kubernetes 集群集成 Kata 柯里化与偏函数 使用 PyGithub 自动创建 Label 软件产品是团队能力的输出 Helm 2 、Helm 3 比较 IoT 变现 Kubernetes 中的 DNS 服务 国内的 Helm 镜像源 Harbor 使用自签证书支持 Https 访问 DevOps 工具链之 Prow 如何使用 kfctl 安装 Kubeflow VS Code 无法下载 Go 插件的工具包 工程师更应具有服务精神 你不知道的 Docker 使用技巧 使用 Docker 运行 Tensorflow 论中国 什么是左移 如何清空 Git 仓库全部历史记录 一禅小和尚 有风吹过厨房 时间的玫瑰 如何在 CentOS 安装 GPU 驱动 开发 Tips(19) 使用 Velero 备份 Kubernetes 集群 Kubernetes Cheat Sheet 开发 Tips(18) 如何构建一个 Java 工程 开发 Tips(17) KubeSpray 安装 Kubernetes 报错 ip in ansible_all_ipv4_addresses 基于 Kubernetes 和 Jenkins 搭建自动化测试系统 在 Kubernetes 上动态创建 Jenkins Slave 使用 Jenkins 进行服务拨测 开发 Tips(16) Kubernetes 签发 Ingress 证书及日常故障运维 Kubernetes 中 Deployment 的基本操作 Kubernetes 中的证书 如何使用 KubeBuilder 开发一个 Operator Kubernetes 1.6.0 安装问题汇总 镜像管理工具 -- Harbor 开发 Tips(15) Docker 如何拉取镜像 开发 Tips(14) 使用 Helm 安装 harbor 开发 Tips(13) 使用 S2I 构建云原生应用 在 Kubernetes 中使用 emptyDir、hostPath、localVolume 开发 Tips(12) 开发 Tips(11) 代码质量分析工具 SonarQube 使用 Kubeadm 安装 Kubernetes 集群 一起来学 Go --(4)常用函数 Kubernetes 中的 Ceph Kubernetes 之 Volumes Kubernetes 之 Labels、Selectors 开发 Tips(10) 开源正在重构商业模式 Kubernetes 之网络 Kubernetes 之 API 使用 Helm 和 Operator 快速部署 Prometheus Kubernetes 复杂有状态应用管理框架 -- Operator Kubernetes 的包管理器 -- Helm 一起来学 Go --(3)Go Modules 如何一步一步地优化博客方案 kubectl 实用指南 Kubernetes 中的基本概念 搭建远程 Kubernetes 开发环境 大公司和小公司的 ToB 思路 开发 Tips(9) Go 入门指南 一起来学 Go --(2)数据与逻辑结构 如何预防 Web 富文本中的 XSS 攻击 django-xss-cleaner 云工作时代 一起来学 Go --(1)背景与特点 SaaS 开发团队的不同阶段 你不知道的 Git 使用技巧 输出既服务 微服务设计 继续奔跑 开发 Tips(8) 从账户安全到二次验证 Django 性能之数据库查询优化 Django 性能之分库分表 敏捷开发之研发流程 打造一致性的团队 开发 Tips(7) Pytest 进阶学习之 Mock PaaS 部署之 buildpack Go 开发配置 领域输出才是 PaaS 的核心竞争力 Pytest 入门学习 开发 Tips(6) 如何使用 Jenkins、Docker、GitLab 搭建 Django 自动化部署流程
在 Kubernetes 中如何给 NodePort 配置 NetworkPolicy
微信公众号 · 2021-06-23 · via 陈少文的网站

1. 需求背景

如上图,业务方需要隔离 namespae 的服务,禁止 bar 空间的负载访问,而允许用户从 Load Balancer (LB) 通过 NodePort 访问服务。可以很容易地写出一个网络策略:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: foo
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - ipBlock:
            cidr: 10.2.3.4/32
        - namespaceSelector:
            matchExpressions:
              - key: region
                operator: NotIn
                values:
                  - bar

然而从 LB 访问的流量被完全禁止,不符合预期。在技术社区检索得到的答案可能是,Kubernetes NetworkPolicy 主要针对的是集群内的访问策略,而外部流量经过 SNAT 之后,IP 发生变化无法命中策略。

不同的网络插件,使用不同的模式,配置会有差异。本文仅提供一个思路,以常见的 Calico IPIP 模式为例配置 NodePort 的流量访问策略。

2. 预备知识点

2.1 Kubernetes 中的 NetworkPolicy

在文档 Kubernetes 之网络隔离(内附十多种使用场景) 中,我对 Kubernetes 的 NetworkPolicy 有所描述,给出了很多示例。

NetworkPolicy 是 Kubernetes 中的网络隔离对象,用来描述网络隔离策略,具体实现依赖于网络插件。目前,Calico、Cilium、Weave Net 等网络插件都支持网络隔离功能。

2.2 Calico 的几种工作模式

  • BGP 模式

在 BGP 模式下,集群中的 BGP 客户端两两互联,同步路由信息。

  • Route Reflector 模式

在 BGP 模式下,客户端连接数量达到 N * (N - 1),N 表示节点的数量。这种方式限制了节点的规模,社区建议不超过 100 个节点。

Route Reflector 模式下,BGP 客户端不需要两两同步路由信息,而是将路由信息同步到若干指定的 Route Reflector 。全部 BGP 客户端只需要和 Route Reflector 建立连接即可,连接数量与节点数量线性相关。

  • IPIP 模式

不同于 BGP 模式,IPIP 模式是通过 tunl0 在节点之间建立隧道,实现网络连通。下图描述了 IPIP 模式下 Pod 之间的流量。

3. 为什么网络策略不生效

在前面的文档 Kubernetes 中如何获取客户端真实 IP 中,我描述过 externalTrafficPolicy 对服务流量的影响。

Cluster 模式下,如果访问 node-2:nodeport,流量将被转发到有服务 Pod 的节点 node-1 上。

Local 模式下,如果访问的 node-2:nodeport,流量不会被转发,无法响应请求。

通常我们默认采用的是 Cluster 模式,而 Cluster 模式在转发流量时会进行 SNAT,也就是修改源地址。这会导致访问请求无法命中网络策略,误以为网络策略没有生效。

这里尝试两种解决办法:

  1. 将 SNAT 之后的源地址也添加到访问白名单中
  2. 使用 Local 模式。由于 LB 有探活的功能,能将流量转发到具有服务 Pod 的节点上,从而保留了源地址。

4. NodePort 下的 NetworkPolicy 配置

4.1 测试环境

  • Kubernetes 版本

v1.19.8

  • kube-proxy 转发模式

IPVS

  • 节点信息
1
2
3
4
5
6
kubectl get node -o wide

NAME    STATUS   ROLES           AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION    CONTAINER-RUNTIME
node1   Ready    master,worker   34d   v1.19.8   10.102.123.117   <none>        CentOS Linux 7 (Core)   3.10.0-1127.el7.x86_64   docker://20.10.6
node2   Ready    worker          34d   v1.19.8   10.102.123.104   <none>        CentOS Linux 7 (Core)   3.10.0-1127.el7.x86_64   docker://20.10.6
node3   Ready    worker          34d   v1.19.8   10.102.123.143   <none>        CentOS Linux 7 (Core)   3.10.0-1127.el7.x86_64   docker://20.10.6
  • 测试的负载
1
2
3
4
kubectl -n tekton-pipelines get pod -o wide

NAME                                          READY   STATUS    RESTARTS   AGE   IP         NODE    NOMINATED NODE   READINESS GATES
tekton-dashboard-75c65d785b-xbgk6             1/1     Running   0          14h   10.233.96.32    node2   <none>           <none>

负载运行在 node2 节点上

  • 测试的服务
1
2
3
4
kubectl -n tekton-pipelines get svc

NAME                          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                              AGE
tekton-dashboard              NodePort    10.233.5.155    <none>        9097:31602/TCP                       10m

4.2 NodePort 流量如何转发到 Pod

这里主要考虑两种情况。

  1. 访问不存在 Pod 负载的节点 node1
  • 服务转发规则
ipvsadm  -L

TCP  node1:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

TCP  node1:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

TCP  node1.cluster.local:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

TCP  node1:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

TCP  localhost:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

可以看到访问 node1:31602 的流量被转发到了 10.233.96.32:9097,也就是服务 Pod 的 IP 地址和端口。

  • IP 路由转发规则

接着看路由转发规则,10.233.96.0/24 网段的访问都会被转到 tunl0,经过隧道到达 node2 再转到服务中。

1
2
3
4
5
6
route

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.233.92.0     node3.cluster.l 255.255.255.0   UG    0      0        0 tunl0
10.233.96.0     node2.cluster.l 255.255.255.0   UG    0      0        0 tunl0
  1. 访问存在 Pod 负载的节点 node2
  • 服务转发规则
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
ipvsadm  -L

TCP  node2:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

TCP  node2:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

TCP  node2.cluster.local:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          1

TCP  node2:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

TCP  localhost:31602 rr
  -> 10.233.96.32:9097            Masq    1      0          0

与 node1 一样,访问 node2 上的 NodePort 服务也会被转发到服务 Pod 的 IP 地址和端口上。

  • 路由转发规则

但是路由规则不一样,目的地址为 10.233.96.32 的包会发给 cali73daeaf4b12 。而 cali73daeaf4b12 与 Pod 中的网卡构成一组 veth pair,流量会被直接发往服务 Pod 中。

1
2
3
4
5
6
7
route

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.233.90.0     node1.cluster.l 255.255.255.0   UG    0      0        0 tunl0
10.233.92.0     node3.cluster.l 255.255.255.0   UG    0      0        0 tunl0
10.233.96.32    0.0.0.0         255.255.255.255 UH    0      0        0 cali73daeaf4b12

从上面命令返回可以知道,如果访问不存在 Pod 负载的节点,流量会经过 tunl0 转发;如果访问存在 Pod 负载的节点,流量不经过 tunl0 直接被路由到 Pod 中。

4.3 方案一,将 tunl0 添加到网络策略白名单

  • 查看各个节点的 tunl0 信息

node1

1
2
3
4
ifconfig

tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1440
        inet 10.233.90.0  netmask 255.255.255.255

node2

1
2
3
4
ifconfig

tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1440
        inet 10.233.96.0  netmask 255.255.255.255

node3

1
2
3
4
ifconfig

tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1440
        inet 10.233.92.0  netmask 255.255.255.255
  • 网络策略配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: foo
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 10.2.3.4/32
    - ipBlock:
        cidr: 10.233.90.0/32
    - ipBlock:
        cidr: 10.233.96.0/32
    - ipBlock:
        cidr: 10.233.92.0/32
    - namespaceSelector:
        matchExpressions:
        - key: region
          operator: NotIn
          values:
          - bar
  • 测试验证

不符合预期。全部经过 tunl0 的流量都会被允许。bar 命名空间的负载可以通过访问 node1:31602、node3:31602、tekton-dashboard.tekton-pipelines.svc:9097(非 node2 上的负载) 访问服务,无法对流量进行限制。

4.4 方案二,使用 Local 模式

  • 修改 svc 的 externalTrafficPolicy 为 Local 模式
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
kubectl -n tekton-pipelines get svc tekton-dashboard -o yaml

apiVersion: v1
kind: Service
metadata:
  name: tekton-dashboard
  namespace: tekton-pipelines
spec:
  clusterIP: 10.233.5.155
  externalTrafficPolicy: Local
...
  • 拒绝全部入口流量
1
2
3
4
5
6
7
8
9
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: test-network-policy-deny-all
  namespace: foo
spec:
  podSelector:
    matchLabels: {}
  ingress: []
  • 添加访问白名单
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: foo
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - ipBlock:
            cidr: 10.2.3.4/32
  • 测试验证

符合预期。使用上面的网络策略,可以满足业务需求,屏蔽 bar 命名空间的访问,允许外部通过 LB 转发到 NodePort 的访问。

5. 总结

网络是 Kuberntes 中相对难以掌握的部分,但网络又是对业务影响范围比较大、影响程度比较深远的一个方面。因此,多花一点时间在网络上,是必要而值得的。

本文主要结合业务需求,对 Calico 的网络模式进行了更进一步的阐述,解决了因为 SNAT 导致源 IP 发生变化,最终 NetworkPolicy 不符合预期的问题。

在 Calico 的 IPIP 模式下,针对 NodePort 的访问策略需要使用 externalTrafficPolicy: Local 流量转发模式。再结合网络策略最佳实践,先禁用全部流量之后,添加白名单策略。

6. 参考