zoukankan      html  css  js  c++  java
  • Cloud Foundry中warden的网络设计实现——iptable规则配置

           

            在Cloud Foundry v2版本号中,该平台使用warden技术来实现用户应用实例执行的资源控制与隔离。


            简要的介绍下warden,就是dea_ng假设须要执行用户应用实例(本文暂不考虑warden container提供staging打包环境),则发送对应请求给warden server,由warden server来创建warden container,并在warden container内部执行应用实例,而warden container的详细实现中使用cgroups等内核虚拟化技术。保证了container内进程组的资源控制与隔离。warden的架构与实现能够參考我的博文:Cloud Foundry中warden的架构与实现



    warden网络架构图


            warden container的功能能够觉得与传统的虚拟机类似,不过传统的虚拟机通过Hypervisor等技术来实现资源的控制与隔离,而warden container通过虚拟化内核来达到该目的。而在网络方面,warden container技术创建出一块虚拟网卡。专门供warden container内部使用,另外为warden container内部的虚拟网卡在warden server所在的宿主机上也配对了一块虚拟网卡,充当container的外部网关。只创建出两块虚拟网卡。原则上能够保证物理上的“连通”,可是却非常难做到网络间通信的“联通”,故在物理资源以及虚拟物理资源完备的情况。warden server在宿主机上通过iptables设计并加入了多条规则,保证整个网络的通信能够满足warden container的要求,同一时候又不影响宿主机的通信。


            下面是warden server所在宿主机的物理环境、iptables所在的网络层、操作系统、用户应用层之间的关系图:


    warden container网络通信方式


            在Cloud Foundry中,因为warden container用来执行用户应用实例,而应用用户实例与外界的通信在Cloud Foundry中眼下无外乎两种:
    1. 用户应用实例作为一个web server,提供HTTP訪问服务;
    2. 用户应用实例使用service服务时,建立与service instance的连接。

            当然有些Cloud Foundry平台开发人员肯定会觉得用户应用实例,能够作为一个client端,通过url訪问其它应用实例或者訪问云外的资源。在这里须要申明的是,首先Cloud Foundry集群默认相应用实例不暴露ip;其次,如果Cloud Foundry内部不存在或者不能连接一个强大的DNS server,用来解析外部资源的url。

            本文即将以上两点作为主要研究的内容。进行warden所在宿主机的网络分析。集中于iptables一块。


    warden iptables配置之net.sh文件

           
            在warden的实现过程中。关于iptables的配置主要包含两个部分:
    1. 启动warden server时,调用文件warden/warden/root/linux/net.sh,制定warden server所在宿主机的部分iptables规则。
    2. 在创建warden container的时候,调用文件warden/warden/root/linux/skeleton/net.sh,制定因为warden container创建后须要加入的iptables规则(包含只创建一个warden container的iptables设置。以及为一个warden container做port映射时的iptables设置)。


    warden server之net.sh

           

    setup_filter

           
            关于warden/warden/root/linux/net.sh,能够看该shell脚本中最为关键的两个方法setup_filter以及setup_nat。

    下面是setup_filter的源代码实现:

    function setup_filter() {
      teardown_filter
    
      # Create or flush forward chain
      iptables -N ${filter_forward_chain} 2> /dev/null || iptables -F ${filter_forward_chain}
      iptables -A ${filter_forward_chain} -j DROP
    
      # Create or flush default chain
      iptables -N ${filter_default_chain} 2> /dev/null || iptables -F ${filter_default_chain}
    
      # Always allow established connections to warden containers
      iptables -A ${filter_default_chain} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    
      for n in ${ALLOW_NETWORKS}; do
        if [ "$n" == "" ]
        then
          break
        fi
    
        iptables -A ${filter_default_chain} --destination "$n" --jump RETURN
      done
    
      for n in ${DENY_NETWORKS}; do
        if [ "$n" == "" ]
        then
          break
        fi
    
        iptables -A ${filter_default_chain} --destination "$n" --jump DROP
      done
    
      iptables -A ${filter_default_chain} --jump REJECT
    
      # Accept packets related to previously established connections
      iptables -I INPUT -m state --state ESTABLISHED,RELATED --jump ACCEPT -m comment --comment 'related-traffic'
    
      # Reject traffic from containers to host
      if [ "$ALLOW_HOST_ACCESS" != "true" ]; then
        iptables -A INPUT -s ${POOL_NETWORK} --jump REJECT -m comment --comment 'block-traffic-to-host'
      fi
    
      # Forward outbound traffic via ${filter_forward_chain}
      iptables -A FORWARD -i w-+ --jump ${filter_forward_chain}
    
      # Forward inbound traffic immediately
      default_interface=$(ip route show | grep default | cut -d' ' -f5 | head -1)
      iptables -I ${filter_forward_chain} -i $default_interface --jump ACCEPT
    }


            下面首先逐步分析每一个部分iptables的制定的意义。随后将各iptables个则串联起来分析其功能。

    关于iptables在内核中的简要流程图例如以下图:



    下面逐步分析filter_setup方法:

           1. 在该net.sh文件里,teardown_filter方法所做的工作是:清除某些warden相关的iptable链以及规则。

    随后的即创建两条链:filter_forward_chain以及filter_forward_chain。增加这两条链先前存在的话,清楚该两条链之后再创建。


           2. 对于已经和warden container建立完成的连接,iptables都运行ACCEPT操作。代码即:
      # Always allow established connections to warden containers
      iptables -A ${filter_default_chain} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
            由凝视能够发现,该规则加入在filter_default_chain链中,当匹配条件为:该数据包表示为是在已建立的连接上传输时。对该数据包採取ACCEPT操作。

    该规则从功能的角度非常好理解,也就是:假如warden container内部发起一个请求,向外建立一个连接,则在该连接上返回数据包时,始终接受这种数据包。

    然而。我始终对该规则的底层实现,抱有非常大的疑惑。

    待下文分析了很多其它的warden 网络背景知识之后,再进行进一步的阐述。


           3.在filter_default_chain中。对于ALLOW_NETWORKS中的网络主机地址。始终採取ACCEPT操作;而对于DENY_NETWORKS中的网络主机地址。使用採取DROP操作。

    在此。能够简要说明一下在iptables中DROP与REJECT的差别。DROP:直接採取丢弃数据包的操作;REJECT:对于原来的数据包採取丢弃措施。随后向数据包的发送方,返回一个类似于ICMP错误的信息包。


            4.之后的shell代码为:iptables -A ${filter_default_chain} --jump REJECT,因为iptables中一个数<br
    据包在同一个链中的规则匹配将从第一条開始匹配,遇到匹配成功则运行对应操作,否则继续向下运行,因此该规则表明这条脸中之前都匹配不成的数据包。内核会採取REJECT操作,将数据包丢弃。并返回一个错误数据包。到此,filter_default_chain中的规则设置基本完成。

            5.在INPUT链中加入规则,所谓INPUT链,即当网卡发现接收到的网络数据包的目的IP。与网卡的IP一致时,将该数据包放入INPUT链运行规则处理,此处的代码例如以下:
      # Accept packets related to previously established connections
      iptables -I INPUT -m state --state ESTABLISHED,RELATED --jump ACCEPT -m comment --comment 'related-traffic'
    
      # Reject traffic from containers to host
      if [ "$ALLOW_HOST_ACCESS" != "true" ]; then
        iptables -A INPUT -s ${POOL_NETWORK} --jump REJECT -m comment --comment 'block-traffic-to-host'
      fi
            在INPUT链中设定规则。保证在warden宿主机上原先与外部建立的连接上。发送来的数据包。都将採取ACCEPT操作。其次通过ALLOW_HOST_ACCESS变量来确定是否同意warden container与warden server所在宿主机之间的通信,若该变量为假。则对于从POOL_NETWORKS中发送来的数据包,都採取REJECT操作。保证warden container内部不能訪问warden server所在的宿主机。

    到此。对于INPUT链的规则制定已经完毕。


            6.制定iptables中的FORWARD链,所谓FOWARD
    创建warden container之net.sh链。即当网络发现接收的网络数据包的目的IP,与该网卡的IP不一致时,则觉得须要为该数据包运行转发(FORWARD)操作。将该数据包放入FORWARD链运行规则处理。此处的代码例如以下:
      # Forward outbound traffic via ${filter_forward_chain}
      iptables -A FORWARD -i w-+ --jump ${filter_forward_chain}
    
      # Forward inbound traffic immediately
      default_interface=$(ip route show | grep default | cut -d' ' -f5 | head -1)
      iptables -I ${filter_forward_chain} -i $default_interface --jump ACCEPT
            当中第一条规则表示:对于从网络设备接口名字为‘w-+’(正則表達式。也就是为warden container而虚拟出的虚拟网卡的设备名字)发来。同一时候又须要运行转发工作的数据包。将该数据包跳转至filter_forward_chain链。这样保证了,全部从warden container发出,又不是发往宿主机的网卡的数据包,进入FORWARD链后直接进入filter_forward_chain链。
     
            而后的规则表明,在filter_forward_chain中的数据包,仅仅要是来自于default_interface。都将採取ACCEPT操作。也就是说。当发现filter_forward_chain中的数据包的来源于warden server所在宿主机的物理网卡时,马上对该数据包採取ACCEPT操作。

            到眼下为止。这个net.sh文件里的filter_setup方法已经分步骤分析完成,然后关于整个运行流,读者可能仍然会有不小的疑惑,比方什么时候。绑定filter_default_chain链,使得数据包进入该链,等等。

    稍后分析完还有一个net.sh文件之后。再统一阐述。



    setup_nat

          
             下面简单分析setup_nat方法:
    function setup_nat() {
         teardown_nat
    
          # Create prerouting chain
         iptables -t nat -N ${nat_prerouting_chain} 2> /dev/null || true
    
          # Bind chain to PREROUTING
         (iptables -t nat -S PREROUTING | grep -q "-j ${nat_prerouting_chain}") ||
                iptables -t nat -A PREROUTING 
                --jump ${nat_prerouting_chain}
    
          # Bind chain to OUTPUT (for traffic originating from same host)
         (iptables -t nat -S OUTPUT | grep -q "-j ${nat_prerouting_chain}") ||
                iptables -t nat -A OUTPUT 
                --out-interface "lo" 
                --jump ${nat_prerouting_chain}
    
          # Create postrouting chain
          iptables -t nat -N ${nat_postrouting_chain} 2> /dev/null || true
          # Bind chain to POSTROUTING
          (iptables -t nat -S POSTROUTING | grep -q "-j ${nat_postrouting_chain}") ||
                iptables -t nat -A POSTROUTING 
                --jump ${nat_postrouting_chain}
    
          # Enable NAT for traffic coming from containers
          (iptables -t nat -S ${nat_postrouting_chain} | grep -q "-j SNAT") ||
                iptables -t nat -A ${nat_postrouting_chain} 
                --source ${POOL_NETWORK} 
                --jump SNAT 
                --to $(external_ip)
    }
    

            该部分的内容比較easy理解:

            1.首先创建nat_prerouting_chain 链,随后将全部的PREROUTING链中的数据包都跳转至nat_prerouting_chain;

            2.随后将链绑定在nat_prerouting_chain上,保证通过lo网络设备的数据包,都跳转到该链;

            3.创建nat_postrouting_chain链,保证将默认的POSTROUTING链中的全部数据包都跳转至创建的该链。

            4.为从warden container中发出的数据包都採取SNAT处理。使得数据包的源IP都替换为warden server宿主机的IP。


            事实上在完毕以上这两步之后,还运行了下面代码:

     # Enable forwarding
    echo 1 > /proc/sys/net/ipv4/ip_forward

            加以保证转发工作能够有效,详细内容能够查看:/proc/sys/net/ipv4/*的各部分作用




    创建warden container之net.sh

           

            前篇分析的是。warden server启动的时候,对warden server所在的宿主机进行的iptbales设置。在整个过程中,不涉及不论什么的container。而这一章节,将介绍分析warden server在启动某个详细warden container的时候,再对warden server所在的宿主机设置iptables规则。

    该文件地址例如以下:warden/warden/root/linux/skeleton/net.sh


    setup_filter


            首先分析setup_filter方法,源代码例如以下:

    function setup_filter() {
           teardown_filter
    
           # Create instance chain
           iptables -N ${filter_instance_chain}
           iptables -A ${filter_instance_chain} 
               --goto ${filter_default_chain}
    
           # Bind instance chain to forward chain
           iptables -I ${filter_forward_chain} 2 
               --in-interface ${network_host_iface} 
               --goto ${filter_instance_chain}
    
           # Create instance log chain
           iptables -N ${filter_instance_log_chain}
           iptables -A ${filter_instance_log_chain} 
               -p tcp -m conntrack --ctstate NEW,UNTRACKED,INVALID -j LOG --log-prefix "${filter_instance_chain} "
    
           iptables -A ${filter_instance_log_chain} 
               --jump RETURN
    }

            1. 该方法创建了关于instance的链。随后,将该warden container instance链内的全部数据包都跳至filter_default_chain,也就是在前一章节中提到的filter_default_chain;

            2.绑定该warden container instance的FORWARD链至filter_foward链,也就是说,全部进入filter_foward_chain的数据包,依据数据包从哪个网络接口设备来,进入对应的filter_instance_chain链。也就是说,从network_host_iface网络设备“接收”到的数据包(network_host_iface即为network_container_iface="w-${id}-1"),都转至filter_instance_chain。

            3.创建log chain。



    setup_nat

           

            在创建warden container的时候,也会设置nat,源代码例如以下:

    function setup_nat() {
          teardown_nat
          # Create instance chain
          iptables -t nat -N ${nat_instance_chain}
          # Bind instance chain to prerouting chain
          iptables -t nat -A ${nat_prerouting_chain} 
                 --jump ${nat_instance_chain}
    }

            简要分析即为:创建对应的nat_instance_chain,随后将nat_prerouting_chain中的数据包都跳转至该nat_instance_chain。


    net_in之port映射实现

           

            使得Cloud Foundry内warden container的用户应用实例能够为外部提供web服务,那么warden container和warden server所在宿主机进行一个port映射操作,也就是warden server提供的net_in接口,真正的运行部分。即为创建容器的该net.sh文件里的in參数运行。


            下面是net_in详细操作的源代码实现:

     "in")
           if [ -z "${HOST_PORT:-}" ]; then
               echo "Please specify HOST_PORT..." 1>&2
               exit 1
           fi
           if [ -z "${CONTAINER_PORT:-}" ]; then
               echo "Please specify CONTAINER_PORT..." 1>&2
               exit 1
           fi
           iptables -t nat -A ${nat_instance_chain} 
               --protocol tcp 
               --destination "${external_ip}" 
               --destination-port "${HOST_PORT}" 
               --jump DNAT 
               --to-destination "${network_container_ip}:${CONTAINER_PORT}"
            ;;

            可见该代码块中最为重要的部分为。创建DNAT规则转换的部分。也就是,在对应的instance规则链中,对于TCP数据包,假设该数据包的目的IP和目的PORT为warden server所在宿主机的IP和映射的PORT。则运行DNAT转换,转换为warden container IP和warden container的port。


    总结

          

            以上便是对Cloud Foundry v2中warden的网络设计之iptables规则制定。做了简要的分析。然而,在这之中,有非常多的设计不过留了接口,并未真正实现。



    另,感谢浙江大学VLIS实验室的实习生高相林的共同研究。


    关于作者:

    孙宏亮。DAOCLOUD软件project师。

    两年来在云计算方面主要研究PaaS领域的相关知识与技术。

    坚信轻量级虚拟化容器的技术,会给PaaS领域带来深度影响,甚至决定未来PaaS技术的走向。


    转载请注明出处。

    本文很多其它出于我本人的理解,肯定在一些地方存在不足和错误。

    希望本文可以对接触warden网络配置的人有些帮助,假设你对这方面感兴趣。并有更好的想法和建议,也请联系我。

    我的邮箱:allen.sun@daocloud.io
    新浪微博:@莲子弗如清


  • 相关阅读:
    在日本被禁止的コンプガチャ設計
    Starling常见问题解决办法
    Flixel引擎学习笔记
    SQLSERVER中修复状态为Suspect的数据库
    T4 (Text Template Transformation Toolkit)实现简单实体代码生成
    创建Linking Server in SQL SERVER 2008
    Linq to Sql 与Linq to Entities 生成的SQL Script与分页实现
    Linq to Entity 的T4 模板生成代码
    在VisualStudio2008 SP1中调试.net framework 源代码
    使用HttpModules实现Asp.net离线应用程序
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/5181292.html
Copyright © 2011-2022 走看看