使用 Calico 网络策略来控制经典集群上的流量

了解如何使用 Calico 策略来允许来自和到达特定 IP 地址的网络流量。

请注意,以下步骤适用于使用经典 LoadBalancers 的经典群集。

缺省情况下,Kubernetes NodePort、LoadBalancer 和 Ingress 服务能使应用程序在所有公共和专用集群网络接口上都可用。 allow-node-port-dnat 缺省 Calico 策略允许来自 NodePort、网络负载均衡器 (NLB) 和 Ingress 应用程序负载均衡器 (ALB) 服务的入局流量流至这些服务公开的应用程序 pod。 Kubernetes 会使用目标网络地址转换 (DNAT) 将服务请求转发到正确的 pod。

但是,出于安全原因,您可能需要仅允许来自特定源 IP 地址的流量流至联网服务。 您可以使用 Calico Pre-DNAT 策略来允许或阻止来自或到达特定 IP 地址的流量。 DNAT 前策略会阻止指定流量到达应用程序,因为会在 Kubernetes 使用常规 DNAT 将流量转发到 pod 之前应用这些策略。 创建 Calico Pre-DNAT 策略时,可选择允许还是阻止源 IP 地址。 对于大多数场景,允许特定流量提供最安全的配置,因为除了来自已知,允许的源 IP 地址的流量外,所有流量都被阻塞。拒绝特定流量通常仅在防止来自一小组 IP 地址的攻击之类的场景中有用。

在此场景中,您扮演的是公关公司的网络管理员角色,并且您注意到应用程序遇到一些异常流量。 本教程中的课程将引导您创建一个示例网络服务器应用程序,使用网络负载平衡器(NLB)服务公开该应用程序,并使用允许列表和阻止列表 Calico 策略保护应用程序免受不需要的异常流量的影响。

目标

  • 了解如何通过创建高位 DNAT 前策略,阻止流至所有节点端口的所有入局流量。
  • 了解如何通过创建低阶 Pre-DNAT 策略,允许特定源 IP 地址访问 NLB 公共 IP 和端口。 低位策略会覆盖高位策略。
  • 了解如何通过创建低阶 Pre-DNAT 策略,阻止特定源 IP 地址访问 NLB 公共 IP 和端口。

受众

本教程适用于希望管理应用程序的网络流量的软件开发者和网络管理员。

先决条件

使用 NLB 部署并公开应用程序

第一课说明如何从多个 IP 地址和端口公开应用程序,以及公共流量进入集群的位置。

首先,部署要在整个教程中使用的样本 Web 服务器应用程序。 echoserver Web 服务器显示有关从客户机与集群建立的连接的数据,并且您可以测试对公关公司集群的访问。 然后,通过创建网络负载均衡器 (NLB) 1.0 服务来公开应用程序。 NLB 1.0 服务通过 NLB 服务 IP 地址和工作程序节点的节点端口使应用程序可用。

第 1 课结束时,网络服务器应用程序已通过公共节点端口和公共 NLB 暴露于互联网。

  1. 部署样本 Web 服务器应用程序。 连接到该 Web 服务器应用程序时,应用程序会使用在连接中接收到的 HTTP 头进行响应。

    kubectl apply -f https://raw.githubusercontent.com/IBM-Cloud/kube-samples/master/deploy-apps-clusters/webserver.yaml
    
  2. 验证 Web 服务器应用程序 pod 的 STATUS 是否为 Running

    kubectl get pods -o wide
    

    示例输出

    NAME                         READY     STATUS    RESTARTS   AGE       IP               NODE
    webserver-855556f688-6dbsn   1/1       Running   0          1m        172.30.xxx.xxx   10.176.48.78
    webserver-855556f688-76rkp   1/1       Running   0          1m        172.30.xxx.xxx   10.176.48.78
    webserver-855556f688-xd849   1/1       Running   0          1m        172.30.xxx.xxx   10.176.48.78
    
  3. 要将应用程序公开到公用因特网,请在文本编辑器中创建名为 webserver-lb.yaml 的 NLB 1.0 服务配置文件。

    apiVersion: v1
    kind: Service
    metadata:
      labels:
        run: webserver                               
      name: webserver-lb
    spec:
      type: LoadBalancer
      selector:
        run: webserver
      ports:
      - name: webserver-port
        protocol: TCP
        port: 8080
        targetPort: 8080 # Optional. By default, the `targetPort` is set to match the `port` value unless specified otherwise.
    
  4. 部署 NLB。

    kubectl apply -f filepath/webserver-lb.yaml
    
  5. 验证是否可以从您的计算机以公共方式访问由 NLB 公开的应用程序。

    1. 获取 NLB 的公共 EXTERNAL-IP 地址。

      kubectl get svc -o wide
      

      示例输出

      NAME           CLUSTER-IP       EXTERNAL-IP        PORT(S)        AGE       SELECTOR
      webserver-lb   172.21.xxx.xxx   169.xx.xxx.xxx     80:31024/TCP   2m        run=webserver
      
    2. 创建备忘单文本文件,并将 NLB IP 复制到该文本文件中。 备忘单将帮助您更快地在后面课程中使用值。

    3. 验证是否能够以公共方式访问 NLB 的外部 IP。

      curl --connect-timeout 10 <loadbalancer_IP>:80
      

      以下输出示例确认 NLB 在 169.1.1.1 公共 NLB IP 地址上公开了应用程序。 webserver-855556f688-76rkp 应用程序 pod 收到了 curl 请求。

      Hostname: webserver-855556f688-76rkp
      Pod Information:
          -no pod information available-
      Server values:
          server_version=nginx: 1.13.3 - lua: 10008
      Request Information:
          client_address=10.176.XX.XX
          method=GET
          real path=/
          query=
          request_version=1.1
          request_scheme=http
          request_uri=http://169.1.1.1:8080/
      Request Headers:
          accept=*/*
          host=169.1.1.1
          user-agent=curl/7.54.0
      Request Body:
          -no body in request-
      
  6. 验证是否可以从您的计算机以公共方式访问由节点端口公开的应用程序。 NLB 服务通过 NLB 服务 IP 地址和工作程序节点的节点端口使应用程序可用。

    1. 获取 NLB 分配给工作程序节点的节点端口。 节点端口的范围是 30000-32767。

      kubectl get svc -o wide
      

      在以下输出示例中,节点端口为 31024

      NAME           CLUSTER-IP       EXTERNAL-IP        PORT(S)        AGE       SELECTOR
      webserver-lb   172.21.xxx.xxx   169.xx.xxx.xxx     80:31024/TCP   2m        run=webserver
      
    2. 对于传统群集,请获取工作节点的公网 IP 地址。 对于 VPC 集群,请改为获取 专用 IP 地址。

      ibmcloud ks worker ls --cluster <cluster_name>
      

      示例输出

      ID                                                 Public IP        Private IP     Machine Type        State    Status   Zone    Version   
      kube-dal10-cr18e61e63c6e94b658596ca93d087eed9-w1   169.xx.xxx.xxx   10.176.48.67   u3c.2x4.encrypted   normal   Ready    dal10   1.35_1513*   
      kube-dal10-cr18e61e63c6e94b658596ca93d087eed9-w2   169.xx.xxx.xxx   10.176.48.79   u3c.2x4.encrypted   normal   Ready    dal10   1.35_1513*   
      kube-dal10-cr18e61e63c6e94b658596ca93d087eed9-w3   169.xx.xxx.xxx   10.176.48.78   u3c.2x4.encrypted   normal   Ready    dal10   1.35_1513*   
      
    3. 将工作程序节点的公共 IP 以及节点端口复制到文本备忘单中,以便在后面的课程中使用。

    4. 验证是否可以通过节点端口访问工作程序节点的公共 IP 地址。 : 由于 VPC 集群中的工作程序节点没有公共 IP 地址,因此仅当您已连接到专用 VPC 网络 (例如,通过 VPN 连接) 时,才能通过 NodePort 访问应用程序。 然后,您可以使用 Worker 节点的私有 IP 地址和 NodePort: <worker_private_IP>:<NodePort>

      curl  --connect-timeout 10 <worker_IP>:<NodePort>
      

      以下输出示例确认对应用程序的请求通过工作程序节点的专用 IP 地址 10.1.1.1 和节点端口 31024 传入。 webserver-855556f688-xd849 应用程序 pod 收到了 curl 请求:

      Hostname: webserver-855556f688-xd849
      Pod Information:
          -no pod information available-
      Server values:
          server_version=nginx: 1.13.3 - lua: 10008
      Request Information:
          client_address=1.1.1.1
          method=GET
          real path=/
          query=
          request_version=1.1
          request_scheme=http
          request_uri=http://10.1.1.1:8080/
      Request Headers:
          accept=*/*
          host=10.1.1.1:31024
          user-agent=curl/7.60.0
      Request Body:
          -no body in request-
      

此时,应用程序已从多个 IP 地址和端口公开。 这些 IP 中大部分都是集群内部 IP,只能通过专用网络进行访问。 只有公共节点端口和公共 NLB 端口会公开到公用因特网。

接下来,您可以开始创建并应用 Calico 策略来阻止公共流量。

阻止所有节点端口的所有传入流量

要保护公关公司的集群,必须阻止对公开应用程序的 NLB 服务和节点端口的公共访问。 首先是阻止对节点端口的访问。

在第 2 课结束时,网络服务器应用程序仅通过公共 NLB 暴露于互联网。

  1. 在文本编辑器中,创建名为 deny-nodeports.yaml 的高位 DNAT 前策略,以拒绝来自任何源 IP 的入局 TCP 和 UDP 流量流至所有节点端口。

    apiVersion: projectcalico.org/v3
    kind: GlobalNetworkPolicy
    metadata:
      name: deny-nodeports
    spec:
      applyOnForward: true
      preDNAT: true
      ingress:
      - action: Deny
        destination:
          ports:
          - 30000:32767
        protocol: TCP
        source: {}
      - action: Deny
        destination:
          ports:
          - 30000:32767
        protocol: UDP
        source: {}
      selector: ibm.role=='worker_public'
      order: 1100
      types:
      - Ingress
    
  2. 应用该策略。

    • Linux:

      calicoctl apply -f filepath/deny-nodeports.yaml
      
    • Windows 和 OS X:

      calicoctl apply -f filepath/deny-nodeports.yaml --config=filepath/calicoctl.cfg
      

    示例输出

    Successfully applied 1 'GlobalNetworkPolicy' resource(s)
    
  3. 使用备忘单中的值,验证是否无法以公共方式访问工作程序节点公共 IP 地址和节点端口。

    curl  --connect-timeout 10 <worker_IP>:<NodePort>
    

    由于创建的 Calico 策略阻止流至节点端口的流量,因此连接会超时。

    curl: (28) Connection timed out after 10016 milliseconds
    
  4. 将上一课中创建的 LoadBalancer 的 externalTrafficPolicy 从 Cluster 更改为 LocalLocal 可确保在下一步卷曲 LoadBalancer 的外部 IP 时保留系统的源 IP。

    kubectl patch svc webserver-lb -p '{"spec":{"externalTrafficPolicy":"Local"}}'
    
  5. 使用备忘单中的值,验证是否仍能够以公共方式访问 NLB 外部 IP 地址。

    curl --connect-timeout 10 <loadbalancer_IP>:80
    

    示例输出

    Hostname: webserver-855556f688-76rkp
    Pod Information:
        -no pod information available-
    Server values:
        server_version=nginx: 1.13.3 - lua: 10008
    Request Information:
        client_address=1.1.1.1
        method=GET
        real path=/
        query=
        request_version=1.1
        request_scheme=http
        request_uri=http://<loadbalancer_IP>:8080/
    Request Headers:
        accept=*/*
        host=<loadbalancer_IP>
        user-agent=curl/7.54.0
    Request Body:
        -no body in request-
    

    例如,在输出的 Request Information 部分中,源 IP 地址为 client_address=1.1.1.1。 源 IP 地址是用于运行 curl 的系统的公共 IP。 反之,如果通过代理或 VPN 连接到因特网,代理或 VPN 可能会隐藏系统的实际 IP 地址。 在任一情况下,NLB 都会将系统的源 IP 地址视为客户机 IP 地址。

  6. 将系统的源 IP 地址(上一步输出中的 client_address=1.1.1.1)复制到备忘单中以在后面的课程中使用。

太好了! 此时,应用程序仅从公共 NLB 端口公开到公用因特网。 将阻止流至公共节点端口的流量。 已部分锁定集群以阻止不需要的流量。

接下来,您可以创建并应用 Calico 策略,只允许来自特定源 IP 的流量。

允许从特定 IP 进入 NLB 的流量

您现在决定完全锁定公关公司集群的流量,并测试访问,只允许您自己的计算机 IP 地址访问。

首先,除了节点端口外,还必须阻止流至公开应用程序的 NLB 的所有入局流量。 然后,就可以创建一个允许使用系统 IP 地址的策略。 在第 3 课结束时,所有到公共节点端口和 NLB 的流量都会被阻止,只允许来自允许系统 IP 的流量。

  1. 在文本编辑器中,创建名为 deny-lb-port-80.yaml 的高位 DNAT 前策略,以拒绝来自任何源 IP 的所有入局 TCP 和 UDP 流量流至 NLB IP 地址和端口。 将 <loadbalancer_IP> 替换为小抄中的 NLB 公共 IP 地址。

    apiVersion: projectcalico.org/v3
    kind: GlobalNetworkPolicy
    metadata:
      name: deny-lb-port-80
    spec:
      applyOnForward: true
      preDNAT: true
      ingress:
      - action: Deny
        destination:
          nets:
          - <loadbalancer_IP>/32
          ports:
          - 80
        protocol: TCP
        source: {}
      - action: Deny
        destination:
          nets:
          - <loadbalancer_IP>/32
          ports:
          - 80
        protocol: UDP
        source: {}
      selector: ibm.role=='worker_public'
      order: 800
      types:
      - Ingress
    
  2. 应用该策略。

    • Linux:

      calicoctl apply -f filepath/deny-lb-port-80.yaml
      
    • Windows 和 OS X:

      calicoctl apply -f filepath/deny-lb-port-80.yaml --config=filepath/calicoctl.cfg
      
  3. 使用备忘单中的值,验证是否现在无法访问公共 NLB IP 地址。 由于创建的 Calico 策略阻止流至 NLB 的流量,因此连接会超时。

    curl --connect-timeout 10 <loadbalancer_IP>:80
    
  4. 在文本编辑器中,创建名为 allowlist.yaml 的低阶 Pre-DNAT 策略,以允许从系统 IP 到 NLB IP 地址和端口的流量。 使用小抄中的值,将 <loadbalancer_IP> 替换为 NLB 的公共 IP 地址,将 <client_address> 替换为系统源 IP 的公共 IP 地址。 如果记不住系统 IP,可以运行 curl ifconfig.co

    apiVersion: projectcalico.org/v3
    kind: GlobalNetworkPolicy
    metadata:
      name: allowlist
    spec:
      applyOnForward: true
      preDNAT: true
      ingress:
      - action: Allow
        destination:
          nets:
          - <loadbalancer_IP>/32
          ports:
          - 80
        protocol: TCP
        source:
          nets:
          - <client_address>/32
      selector: ibm.role=='worker_public'
      order: 500
      types:
      - Ingress
    
  5. 应用该策略。

    • Linux:

      calicoctl apply -f filepath/allowlist.yaml
      
    • Windows 和 OS X:

      calicoctl apply -f filepath/allowlist.yaml --config=filepath/calicoctl.cfg
      

    您系统的 IP 地址现在已被允许。

  6. 使用备忘单中的值,验证是否现在可以访问公共 NLB IP 地址。

    curl --connect-timeout 10 <loadbalancer_IP>:80
    
  7. 如果您有权访问具有不同 IP 地址的另一个系统,请尝试从该系统访问 NLB。

    curl --connect-timeout 10 <loadbalancer_IP>:80
    

    连接超时,因为该系统的 IP 地址不被允许。

此时,将阻止流至公共节点端口和 NLB 的所有流量。 只允许来自允许系统 IP 的流量。

拒绝从特定 IP 到 NLB 的传入流量

在上一课中,你阻止了所有流量,只允许少数 IP 访问。 针对您希望将访问仅限于若干个受控源 IP 地址的测试目的,该场景非常适用。 但是,公关公司拥有需要可供公众广泛使用的应用程序。 因此,除了发现的来自若干 IP 地址的异常流量外,您需要确保允许其他所有流量。 拒绝列表在这种情况下非常有用,因为它可以帮助你防止来自一小部分 IP 地址的攻击。

在本课程中,阻止来自您自己系统的源 IP 地址的流量。 在第 4 课结束时,将阻止流至公共节点端口的所有流量,但允许流至公共 NLB 的所有流量。 只有从您的特定系统 IP 到 NLB 的流量才会被阻止。

  1. 清理上一课创建的允许列表策略。

    • Linux:
      calicoctl delete GlobalNetworkPolicy deny-lb-port-80
      
      calicoctl delete GlobalNetworkPolicy allowlist
      
    • Windows 和 OS X:
      calicoctl delete GlobalNetworkPolicy deny-lb-port-80 --config=filepath/calicoctl.cfg
      
      calicoctl delete GlobalNetworkPolicy allowlist --config=filepath/calicoctl.cfg
      

    现在,再次允许来自任何源 IP 的所有入局 TCP 和 UDP 流量流至 NLB IP 地址和端口。

  2. 要拒绝从系统源 IP 地址到 NLB IP 地址和端口的所有传入 TCP 和 UDP 流量,请在文本编辑器中创建一个名为 blocklist.yaml 的低阶预 DNAT 策略。 使用小抄中的值,将 <loadbalancer_IP> 替换为 NLB 的公共 IP 地址,将 <client_address> 替换为系统源 IP 的公共 IP 地址。

    apiVersion: projectcalico.org/v3
    kind: GlobalNetworkPolicy
    metadata:
      name: blocklist
    spec:
      applyOnForward: true
      preDNAT: true
      ingress:
      - action: Deny
        destination:
          nets:
          - <loadbalancer_IP>/32
          ports:
          - 80
        protocol: TCP
        source:
          nets:
          - <client_address>/32
      - action: Deny
        destination:
          nets:
          - <loadbalancer_IP>/32
          ports:
          - 80
        protocol: UDP
        source:
          nets:
          - <client_address>/32
     selector: ibm.role=='worker_public'
     order: 500
     types:
     - Ingress
    
  3. 应用该策略。

    • Linux:

      calicoctl apply -f filepath/blocklist.yaml
      
    • Windows 和 OS X:

      calicoctl apply -f filepath/blocklist.yaml --config=filepath/calicoctl.cfg
      

    您系统的 IP 地址已被阻止。

  4. 使用小抄中的值,从系统中验证您是否无法访问 NLB IP,因为您系统的 IP 已被阻止。

    curl --connect-timeout 10 <loadbalancer_IP>:80
    

    此时,将阻止流至公共节点端口的所有流量,但允许流至公共 NLB 的所有流量。 只有从您的特定系统 IP 到 NLB 的流量才会被阻止。

非常好! 通过使用 Calico Pre-DNAT 策略阻止源 IP,您成功控制了进入应用程序的流量。

记录从特定 IP 到 NLB 的阻塞流量

在上一课中,你阻止了从系统 IP 到 NLB 的流量。 在本课中,您可以了解如何记录被拒绝的流量请求。

在本示例中,你所供职的公关公司希望你为持续被某项网络策略拒绝的任何异常流量设置日志跟踪。 为了监控潜在的安全威胁,您需要设置日志记录,以记录每次拦截列表策略拒绝对 NLB IP 进行尝试操作的情况。

  1. 创建名为 log-denied-packets 的 Calico NetworkPolicy。 此日志策略使用与 blocklist 策略相同的选择器,可将此策略添加到 Calico Iptables 规则链中。 通过使用低阶编号(如 300 ),可以确保该规则先于拦截列表策略添加到 Iptables 规则链中。 来自您 IP 的数据包在尝试匹配 blocklist 策略规则并被拒绝之前,会被该策略记录。

    apiVersion: projectcalico.org/v3
    kind: GlobalNetworkPolicy
    metadata:
        name: log-denied-packets
     spec:
      applyOnForward: true
      preDNAT: true
      ingress:
      - action: Log
        destination:
          nets:
          - <loadbalancer_IP>/32
          ports:
          - 80
        protocol: TCP
        source:
          nets:
          - <client_address>/32
      - action: Log
        destination:
          nets:
          - <loadbalancer_IP>/32
          ports:
          - 80
        protocol: UDP
        source:
          nets:
          - <client_address>/32
      selector: ibm.role=='worker_public'
      order: 300
      types:
      - Ingress
    
  2. 应用该策略。

    • Linux:

      calicoctl apply -f /log-denied-packets.yaml
      
    • Windows 和 OS X:

      calicoctl apply -f /log-denied-packets.yaml --config=<filepath>/calicoctl.cfg
      
  3. 通过将请求从系统 IP 发送到 NLB IP 来生成日志条目。 这些请求包在被拒绝之前会进行记录。

    curl --connect-timeout 10 <loadbalancer_IP>:80
    
  4. 检查写入 /var/log/syslog 路径的日志条目。 日志条目类似于以下内容。

    Sep 5 14:34:40 <worker_hostname> kernel: [158271.044316] calico-packet: IN=eth1 OUT= MAC=08:00:27:d5:4e:57:0a:00:27:00:00:00:08:00 SRC=192.XXX.XX.X DST=192.XXX.XX.XX LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=52866 DF PROTO=TCP SPT=42962 DPT=22 WINDOW=29200 RES=0x00 SYN URGP=0
    

非常好! 您可以设置日志记录,以便更轻松地监控被拦截的流量。

如果您想清理拦截列表和日志策略:

  1. 清理拦截列表政策。
    • Linux:
      calicoctl delete GlobalNetworkPolicy blocklist
      
    • Windows 和 OS X:
      calicoctl delete GlobalNetworkPolicy blocklist --config=filepath/calicoctl.cfg
      
  2. 清除日志策略。
    • Linux:
      calicoctl delete GlobalNetworkPolicy log-denied-packets
      
    • Windows 和 OS X:
      calicoctl delete GlobalNetworkPolicy log-denied-packets --config=filepath/calicoctl.cfg
      

后续步骤?