0%

Cilium 实际上手体验

在上篇文章中,我们在阿里云环境安装了 k8s 以及 cilium,这里我们将跟随 cilium 官方上的一个 Demo 示例,来实际体验 cilium。

环境说明

在这个例子中,将启动三个微服务程序:

  • deathstar:在 80 端口上运行一个 HTTP 微服务,该服务通过 k8s service 的形式暴露,从而可以将请求负载分担到上游的两个 Pod 中。从业务角度上来讲,该服务提供提供了 landing 接口,允许整个 empire 的飞船进行 着陆
  • tiefighter:代表一个客户端服务,它会发出一个 empire ship 的 着陆 请求
  • xwing:也代表一个类似的客户端服务,但是发出的是 alliance ship 的 着陆 请求

使用如下命令下载我们的 k8s 配置文件,该 http-sw-app.yaml 文件定义了上述 service 以及 Pod:

1
wget https://raw.githubusercontent.com/cilium/cilium/1.14.4/examples/minikube/http-sw-app.yaml

根据该配置文件,创建应用:

1
2
3
4
5
# kubectl create -f http-sw-app.yaml
service/deathstar created
deployment.apps/deathstar created
pod/tiefighter created
pod/xwing created

之后 kubectl 会在后台部署服务、创建对应的 Pod,使用如下命令确认所有 Service、Pod 都处于 Running 状态:

1
2
3
4
5
6
7
8
9
10
# kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/deathstar-f449b9b55-klltm 1/1 Running 0 14m
pod/deathstar-f449b9b55-psh9w 1/1 Running 0 14m
pod/tiefighter 1/1 Running 0 14m
pod/xwing 1/1 Running 0 14m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/deathstar ClusterIP 10.111.234.48 <none> 80/TCP 14m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14h

每个 Pod 在 cilium 中都会表示为 cilium agent 中的一个 endpoint,可以通过如下命令查看所有的 cilium agent,然后选择某个 cilium agent 查看其中的 endpoint:

1
2
3
4
5
6
7
8
# kubectl -n kube-system get pods -l k8s-app=cilium
NAME READY STATUS RESTARTS AGE
cilium-9sxk7 1/1 Running 0 14h
cilium-fmst8 1/1 Running 0 14h
cilium-xjtb8 1/1 Running 0 14h

# kubectl -n kube-system exec cilium-9sxk7 -- cilium endpoint list
......

检查当前连通性

由于当前没有配置任何网络策略,所以 xwing 和 tiefighter 发出的 着陆 请求都可以被响应:

1
2
3
4
5
# kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed

# kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed

其实这个并不符合业务预期,因为我们只希望 org=empire 的 ship 才能 着陆。后面我们会通过网络策略来解决这个问题。

应用 L3/L4 policy

在 Cilium 中,endpoint 的 IP 其实对安全策略是无关紧要的,因为 Cilium 会使用 Pod 的标签来定义安全策略,所以只要 Pod 的标签能够匹配安全规则,这些规则就会对这些 Pod 生效,而不管这些 Pod 的 IP 是什么。

如下定义了一条网络策略,只允许拥有 org=empire 标签的 endpoint 才能访问 deathstarorg=empire, class=deathstar)的 80 端口,由于该策略是在 IP 层和 TCP 层生效,因此通常称为 L3/L4 网络安全策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "L3-L4 policy to restrict deathstar access to empire ships only"
endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP

应用如下策略:

1
2
# kubectl create -f sw_l3_l4_policy.yaml
ciliumnetworkpolicy.cilium.io/rule1 created

此时再次发送请求,可以看到拥有 org=empire 标签 的 tiefighter 可以访问 deathstar着陆 服务,

1
2
# kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed

xwing 访问则一直卡主:

1
kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing

再次查看 cilium endpoint 的网络策略,可以看到 ingress enabled

1
2
# kubectl -n kube-system exec cilium-9sxk7 -- cilium endpoint list
437 Enabled Disabled 12079 k8s:app.kubernetes.io/name=deathstar

也可以这样确认:

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
27
28
29
30
31
# kubectl get cnp
NAME AGE
rule1 5m53s

# kubectl describe cnp rule1
Name: rule1
Namespace: default
Labels: <none>
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumNetworkPolicy
Metadata:
Creation Timestamp: 2023-11-30T04:13:23Z
Generation: 1
Resource Version: 110474
UID: 4737ae78-a155-401c-8aa1-893f215b5f5a
Spec:
Description: L3-L4 policy to restrict deathstar access to empire ships only
Endpoint Selector:
Match Labels:
Class: deathstar
Org: empire
Ingress:
From Endpoints:
Match Labels:
Org: empire
To Ports:
Ports:
Port: 80
Protocol: TCP
Events: <none>

能够感知 HTTP 协议的 L7 Policy

对于简单的场景,的确能够控制 tiefighter/xwing 的访问权限,但是如果想实现最小安全原则,例如 tiefighter 虽然能够访问 deathstarlanding 接口,但是不能访问其 maintenance API。对于该需求,L3/L4 网络策略就无能为力了,因为这涉及 HTTP 协议内容,需要 L7 policy

Cilium 支持使用 HTTP 层的 L7 policy 来限制 tiefighter 能够访问的 API 接口,如下是一个示例,它只允许 tiefighter 访问 deathstarPOST /v1/request-landing API,而其他 API 都无法访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "L7 policy to restrict access to specific HTTP call"
endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "POST"
path: "/v1/request-landing"

应用该规则:

1
kubectl apply -f sw_l3_l4_l7_policy.yaml

之后再次访问,可以看到 tiefighter 只能访问 request-landing API,而其他 API 则无法访问:

1
2
3
4
5
# kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed

# kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
Access denied

xwing 仍然无法访问,一直卡主:

1
kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing

可以通过如下方式查看 L7 policy:

1
kubectl describe ciliumnetworkpolicies

或者

1
kubectl -n kube-system exec cilium-9sxk7 -- cilium policy get

可以通过 cilium monitor 实时监控流量:

1
kubectl -n kube-system exec cilium-9sxk7 -- cilium monitor

如下方式只监控 http 请求:

1
2
3
4
5
6
# kubectl exec -it -n kube-system cilium-9sxk7 -- cilium monitor -v --type l7 -n
Defaulted container "cilium-agent" out of: cilium-agent, config (init), mount-cgroup (init), apply-sysctl-overwrites (init), mount-bpf-fs (init), clean-cilium-state (init), install-cni-binaries (init)
Listening for events on 4 CPUs with 64x4096 of shared memory
Press Ctrl-C to quit
<- Request http from 854 ([k8s:app.kubernetes.io/name=tiefighter k8s:class=tiefighter k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default k8s:io.cilium.k8s.policy.cluster=kubernetes k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:org=empire]) to 437 ([k8s:org=empire k8s:app.kubernetes.io/name=deathstar k8s:class=deathstar k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default k8s:io.cilium.k8s.policy.cluster=kubernetes k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default]), identity 1110->12079, verdict Forwarded POST http://deathstar.default.svc.cluster.local/v1/request-landing => 0
<- Response http to 854 ([k8s:app.kubernetes.io/name=tiefighter k8s:class=tiefighter k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default k8s:io.cilium.k8s.policy.cluster=kubernetes k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:org=empire]) from 437 ([k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:org=empire k8s:app.kubernetes.io/name=deathstar k8s:class=deathstar k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default k8s:io.cilium.k8s.policy.cluster=kubernetes]), identity 12079->1110, verdict Forwarded POST http://deathstar.default.svc.cluster.local/v1/request-landing => 200

清理环境

以上就完成了 cilium demo 的体验,如果需要清理环境,可以执行如下命令:

1
2
$ kubectl delete -f http-sw-app.yaml
$ kubectl delete cnp rule1

Reference