Antrea CNIはv1.7.0でTrafficControl機能をサポートしました。TrafficControlはPodが送受信するトラフィックを制御し、特定のPodのトラフィックをローカルネットワークデバイスやトンネルを利用して転送やミラーすることが可能です。この機能を使うことによりIDS/IPSや外部のネットワークサービスを透過的に利用することが可能になります。今回はこのドキュメントに従ってTrafficControlを利用して、OSSのIDSであるSuricataによるIDS/IPSを利用してみます。
TrafficControlの有効化
TrafficControlはAntreaのFeatureGateで有効化します。
apiVersion: v1
kind: ConfigMap
metadata:
name: antrea-config
namespace: kube-system
labels:
app: antrea
data:
antrea-agent.conf: |
# Enable mirroring or redirecting the traffic Pods send or receive.
TrafficControl: true
以下のマニフェストを利用すれば、githubで公開されているマニフェストを利用を使ってTrafficControlを有効化してAntreaをインストールできます。
$ curl -s https://raw.githubusercontent.com/antrea-io/antrea/main/build/yamls/antrea.yml | \
sed "s/.*TrafficControl:.*/ TrafficControl: true/" | \
kubectl apply -f -
CNIが有効化され、TrafficControl CRDが使えるようになります。
$ kubectl get pod -n kube-system -l app=antrea -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
antrea-agent-bpwbg 2/2 Running 0 12m 192.168.200.101 master <none> <none>
antrea-agent-fbs74 2/2 Running 0 12m 192.168.200.103 node2 <none> <none>
antrea-agent-zcwbg 2/2 Running 0 12m 192.168.200.102 node1 <none> <none>
antrea-controller-7b84f75d88-5zbrd 1/1 Running 0 12m 192.168.200.102 node1 <none> <none>
# kubectl explain trafficcontrols.crd.antrea.io
KIND: TrafficControl
VERSION: crd.antrea.io/v1alpha2
DESCRIPTION:
<empty>
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
spec <Object> -required-
TrafficControlリソースに関して
例えば、以下のTrafficControlリソースを定義することにより、app=web
ラベルのあるPodのトラフィックを10.0.10.2のレシーバにVXLANでカプセル化してミラーリングします。
apiVersion: crd.antrea.io/v1alpha2
kind: TrafficControl
metadata:
name: mirror-web-app
spec:
appliedTo:
podSelector:
matchLabels:
app: web
direction: Both
action: Mirror
targetPort:
vxlan:
remoteIP: 10.0.10.2
destinationPort: 4789
vni: 1
.spec.action
はMirror
かRedirect
を指定することが可能です。Mirror
を指定する場合、パケットをミラーする先のtargetPort
の指定が必要です。Redirect
を指定する場合、targetPort
に加えてreturnPort
の指定が必要です。returnPort
はOVSにトラフィックを送り返し、元の宛先に転送可能なポートを指定します。リダイレクトされたパケットはドロップするか、変更せずにOVSに送り返す必要があります。
.spec.targetPort
は以下の6種類のポートを利用することができます。
- ovsInternal : 各ノードのOVS内部ポート。Podのトラフィックは同じノードのOVSの内部ポートにリダイレクトまたはミラーリングされます。指定された名前でAntreaが自動的にポートを生成します。
- device : すべてのノードに存在すネットワークデバイスにリダイレクトされます。Antreaはノード上のネットワークデバイスをOVSブリッジに接続して、トラフィックをリダイレクトまたはミラーリングします。
- geneve : GENEVEトンネルを利用して宛先にリダイレクトまたはミラーリングします。
remoteIP
を指定する必要があり、オプションとしてdestinationPort
とvni
を指定することが可能です。 - vxlan : VXALANトンネルを利用して宛先にリダイレクトまたはミラーリングします。
remoteIP
を指定する必要があり、オプションとしてdestinationPort
とvni
を指定することが可能です。 - gre : GREトンネルを利用して宛先にリダイレクトまたはミラーリングします。
remoteIP
を指定する必要があり、オプションとしてkey
を指定することが可能です。 - erspan : ERSPANトンネルを利用して宛先にリミラーリングします。
remoteIP
を指定する必要があり、オプションとしてsessionID
とversion
を指定することが可能です。Version 2ではミラートラフィックの方向としてdir
(0:ingress/1:egress)とhardwareID
を指定することが可能です。
TrafficControlの設定
今回は以下のマニフェストでapp: web
ラベルがついたPodの送受信トラフィックをノードのOVSのtap0
という名前のポートにミラーします。tap0
はOVSのinternalPortとして自動的に作成されます。
apiVersion: crd.antrea.io/v1alpha2
kind: TrafficControl
metadata:
name: mirror-web-app-to-tap0
spec:
appliedTo:
podSelector:
matchLabels:
app: web
direction: Both
action: Mirror
targetPort:
ovsInternal:
name: tap0
上記マニフェストを適用すると、Nodeにovsのinternalポートとしてtap0
が生成されたことが確認できます。
# kubectl exec -n kube-system -it antrea-agent-zcwbg -- ovs-vsctl show
64f956f9-bf6a-45f0-8130-47d5ba0222b3
Bridge br-int
datapath_type: system
Port tap0
Interface tap0
type: internal
Port antrea-tun0
Interface antrea-tun0
type: geneve
options: {csum="true", key=flow, remote_ip=flow}
Port coredns--e1e96f
Interface coredns--e1e96f
Port antrea-gw0
Interface antrea-gw0
type: internal
Port coredns--5410d1
Interface coredns--5410d1
ovs_version: "2.17.0"
Suricataのデプロイ
以下のマニフェストでをSuricataをDaemonSetとしてノードにデプロイします。SuricataはhostNetwork
を利用し、起動時のオプションでインターフェースとしてtap0
を指定しているので、tap0インターフェースのトラフィックをモニタリングします。また、ホストのlogディレクトリをマウントしてIDSログをホストのファイルシステムにログとして出力する設定になっています。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: suricata
spec:
selector:
matchLabels:
app: suricata
template:
metadata:
labels:
app: suricata
name: suricata
spec:
hostNetwork: true
containers:
- name: suricata
image: jasonish/suricata:latest
imagePullPolicy: IfNotPresent
command:
- /usr/bin/suricata
- -i
- tap0
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
- SYS_NICE
volumeMounts:
- name: host-var-log-suricata
mountPath: /var/log/suricata
volumes:
- name: host-var-log-suricata
hostPath:
path: /var/log/suricata
type: DirectoryOrCreate
マニフェストを適用してPodの起動を確認します。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
suricata-h64mm 1/1 Running 0 5s 192.168.200.102 node1 <none> <none>
suricata-wx8jz 1/1 Running 0 5s 192.168.200.103 node2 <none> <none>
テスト
Web Podを起動して送受信トラフィックがSuricateによって認識されるかどうかを確認します。
Podを起動してNodePortで公開します。
$ kubectl create deploy web --image nginx:alpine
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
suricata-h64mm 1/1 Running 0 15m 192.168.200.102 node1 <none> <none>
suricata-wx8jz 1/1 Running 0 15m 192.168.200.103 node2 <none> <none>
web-54f54687c6-t2zc7 1/1 Running 0 16s 10.244.2.3 node2 <none> <none>
$ kubectl expose deploy web --type=NodePort --port=80
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 54m
web NodePort 10.97.224.182 <none> 80:30574/TCP 3s
NodePort経由でWeb podにアクセス可能なことを確認します。
$ curl --head http://192.168.200.103:30574
HTTP/1.1 200 OK
Server: nginx/1.23.1
Date: Wed, 14 Sep 2022 02:14:20 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 19 Jul 2022 15:23:19 GMT
Connection: keep-alive
ETag: "62d6cc67-267"
Accept-Ranges: bytes
Web podに対して擬似的に攻撃を行ってみます。トラフィックはミラーされているだけなのでWeb podから404が帰ってきます。
$ curl --head http://192.168.200.103:30574/dlink/hwiz.html
HTTP/1.1 404 Not Found
Server: nginx/1.23.1
Date: Wed, 14 Sep 2022 02:20:16 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive
Podが起動しているノード(192.168.200.103)上で/var/log/suricate/fast.log
を確認すると、Dlink Soho Routerの設定ページに対するアクセスが試みられたことが記録されています。
$ ssh root@192.168.200.103 cat /var/log/suricata/fast.log /var/log/suricata/fast.log
09/14/2022-02:20:16.558811 [**] [1:2008942:8] ET POLICY Dlink Soho Router Config Page Access Attempt [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 10.244.2.1:3612 -> 10.244.2.3:80
次にWeb podから外部ホストに対して攻撃とみなされる通信を行ってみます。こちらも、通信は可能です。
$ kubectl exec deploy/web -- curl -s http://testmynids.org/uid/index.html
uid=0(root) gid=0(root) groups=0(root)
同じようにログファイルを確認すると、Outboundトラフィックに関してもIDSで攻撃の可能性が検出されています。
$ ssh root@192.168.200.103 cat /var/log/suricata/fast.log /var/log/suricata/fast.log
09/14/2022-02:25:03.789861 [**] [1:2013028:7] ET POLICY curl User-Agent Outbound [**] [Classification: Attempted Information Leak] [Priority: 2] {TCP} 10.244.2.3:55468 -> 18.65.168.10:80
09/14/2022-02:25:03.792058 [**] [1:2100498:7] GPL ATTACK_RESPONSE id check returned root [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 18.65.168.10:80 -> 10.244.2.3:55468
まとめ
AntreaのTrafficControlを利用すると、Podのネットワークトラフィックを転送・ミラーリング可能です。VMware NSXにが持つNetwork Intrspectionと同等の機能を提供できるため、将来的にはNSX Intelligenceで可視化されたり、NSX NDRと連携できたりするんじゃないかと思います。