strongswan--HA

高可用性

  从4.4.0版本开始,IKEv2守护程序使用[最初]两个节点的集群实验性地支持伪主动/主动高可用性和负载共享功能。

问题陈述

  IKEv2 / IPsec协议不适合在主动/主动群集中运行。虽然可以通过群集中的高速链路共享IKE_SAs的状态,但共享内核维护的IPsec ESP SA非常困难。由于ESP数据包的严格序列编号,同步ESP序列号的开销将非常高。

  IETF ipsecme工作组更详细地描述了这些问题。如果它将为解决方案生成标准,则很可能依赖于客户端进行状态同步。但是,这将需要扩展IKEv2协议。连接到高可用性群集的客户端只有在支持此扩展时才会受益于此功能,现有客户端(例如Windows 7附带的客户端)将无法利用此功能。

可能的方法

  节点到节点的同步

    虽然包括序列号的IKE状态的同步在两个节点之间是切合实际的,但是交换ESP安全关联的状态信息是困难的。

    同步每个已处理的IPsec数据包的状态将对节点造成很高的负担。在一定数量的数据包之后和/或在某个超时之后进行同步可以减少负载,但会使故障转移处理变得更加困难,因为我们必须在接收节点上猜测故障节点已处理了多少数据包,但在失败之前无法同步。

    这种方法的另一个问题是没有办法在节点之间进行负载共享。 SA严格绑定到单个节点,直到发生故障。

  客户端到群集的同步

    另一种方法是从客户端请求状态信息。在发生故障的情况下,接管节点可以从客户端请求序列号。但是这种方法与前面的讨论有相同的缺陷。此外,它需要在客户端和网关之间扩展IKE协议,使现有的实现与此方法不兼容。

功能规范

  strongSwan采用略有不同的方法。我们的解决方案提供了如下特性:

    故障检测:如果节点因断电,硬件故障,内核oops或守护程序崩溃而出现故障,则该节点将从群集中删除。

    状态同步:如果由于故障或管理目的而删除节点,则群集应该已经拥有要接管的节点状态的最新副本。

    接管:节点故障检测和状态接管应在1-3秒内完成。

    负载共享:应在群集中的所有活动节点之间共享负载。

    重新集成:可以(重新)将已修复的节点添加到现有集群,从而接管部分负载。

    传统客户端:没有协议扩展,如果连接到群集,任何IKEv2客户端都应该能够从高可用性中受益。

  将客户端迁移到另一个节点不会影响连接,客户端通常不会检测到接管。这允许网关管理员例如从负载共享群集中删除节点,应用安全更新,重新引导并重新集成节点。

精选解决方案

  所选解决方案基于ClusterIP的概念,ClusterIP是一个Linux内核模块,允许一组节点在单个虚拟IP下提供服务。

ClusterIP如何工作

  基于ClusterIP的设置中的所有节点都在单个虚拟IP地址下运行。节点使用组播MAC地址欺骗ARP请求。这将使交换机将数据包转发到群集中的每个节点。

  通过计算它的散列值,将接收的分组与段相关联。在最简单的设置中,源地址被散列,并且以段数为模的散列值导出负责的段号。每个段由集群中的一个节点处理。

  负责数据包的节点会将其传递给上层,其他所有节点只是在netfilter代码中将数据包丢弃。取决于散列值,例如TCP连接保留在同一节点上。如果节点发生故障,剩余的节点将接管该段并为其处理数据包。

使用ClusterIP的IPsec

  虽然ClusterIP模块本身不是为处理IPsec流量而设计的,甚至不是转发路由器,但ClusterIP的原理是。如果集群中的IKE守护程序可以同步IKE状态和没有序列号的基本IPsec SA状态,则修改后的ClusterIP模块可以完成剩下的工作:

  对于要解密的流量,ESP数据包的SPI包含在哈希计算中,导致SA在所有的节点上传播。对于SA,本地节点不负责,它不时地挑选一个数据包并使用它来推进重放窗口。如果未使用IPsec身份验证算法验证数据包,则序列号不会受到损坏,只有选择性选择和验证的数据包会破坏重播窗口状态。早期的补丁将分组的序列号包括到段散列计算中。这导致单个SA中的多个数据包扩散到多个节点,自动更新重播窗口。但是该方法具有在流中重新排序分组的缺点。

  对于要在群集上加密的流量,查找SA,并使用找到的SA的SPI提供的哈希值。如果段匹配,则进一步处理该分组。如果不是,则仅增加序列号。为避免为多个节点上的不同数据包分配相同的序列号,我们目前将出站流保持在单个节点上。可以传播传出的数据包并在每个节点上分配唯一的序列号,但这会引入其他问题,例如缩短重放窗口。

内核实现

  ClusterIP Netfilter模块使用额外的PREROUTING钩子函数来标记接收的数据包以进行转发。在解密/加密处理(XFRM_IN / XFRM_OUT)之前,IPsec处理中包含两个新的Netfilter钩子函数。

  1. AH,ESP和UDP封装的ESP数据包都被接受。其他流量受基于源IP地址(例如IKE流量)的ClusterIP选择算法的约束。

  2. 使用基于IPsec SA的ClusterIP算法丢弃未解密的IPsec流量。

  3. 解密过程仅在负责节点上完成。非负责节点选择每第n个数据包并验证它以更新重放窗口状态。

  4. 在ClusterIP组播MAC上接收流量,并且必须将其标记为单播流量以通过IP转发进行。

  5. 在IPsec策略查找之后,使用基于IPsec SA的ClusterIP算法丢弃未加密的流量。在打包丢弃之前分配传出序列号,这将使所有节点上的传出序列号保持同步。

  6. 加密过程仅在负责节点上完成。

  原始补丁可用于XFRM输入/输出钩子函数和(允许ClusterIP作用于IPsec的)ClusterIP模块。这些补丁打破了Netfilter ABI,iptables需要一个补丁才能在补丁内核上运行。

  对于针对较新内核的补丁,请查看我们的Linux git树的分支。较新的补丁集不再破坏Netfilter ABI,从而无需修补用户区iptables。适用于不同内核版本的ABI兼容补丁也可从download.strongswan.org/testing获得。

  注意:上面提供的内核<4.4的补丁与pcrypt不兼容,但可以手动修复(参见#2139)。

IKE守护进程实现

  为IKEv2守护程序charon实现的单独高可用性插件负责集群中节点之间的状态同步和简单的监视功能。它目前设计用于两个节点,但将在未来扩展为同步更大的集群。

  要启用高可用性插件,构建strongSwan时请使用--enable-ha。

守护进程钩子

  插件向守护进程的几个钩子注册。这些钩子用于有关SA状态变化的通知,并将信息推送到插件。用到的钩子如下:

  ike_keys():接收IKE密钥材料(DH,nonces,proposal)

  ike_updown()/ ike_rekey():监视IKE SA的状态变化

  message():用于更新IKE消息ID

  child_keys():接收CHILD密钥材料

  child_state_change():监视CHILD SA的状态变化

  该插件在守护程序总线上注册其钩子函数。这些函数足以使所有IKE和CHILD SA与执行IKE和ESP SA故障转移所需的所有状态同步。

同步消息

  钩子函数收集所需的同步数据并准备要发送到集群中其他节点的消息。消息以未加密的UDP数据报发送,用4510端口发送和接收。由于这些消息包含敏感密钥材料,因此建议通过IPsec保护消息。

  目前没有实现数据包确认/重传方案,集群需要一个可靠的网络,只有很少的数据包丢失。将来可能需要使用更可靠的传输协议,尤其是当节点由于CPU过载而开始丢弃数据包时。消息包含协议版本,消息类型和不同的属性。目前定义了以下同步消息类型:

  IKE_ADD:已建立新的IKE_SA。此消息包含用于派生密钥材料的所有信息。如果消息包含REKEY属性,则IKE_SA将继承旧SA中的所有必需参数。

  IKE_UPDATE:使用更新的信息更新IKE_SA(例如,身份验证完成时的身份)。

  IKE_MID_INITIATOR:更新发起方IKE消息ID。

  IKE_MID_RESPONDER:更新响应者IKE消息ID。
  IKE_DELETE:删除已建立的IKE_SA。

  CHILD_ADD:已建立CHILD_SA,包含密钥材料。

  CHILD_DELETE:CHILD_SA已被删除。

状态同步

  解析收到的同步消息,镜像IKE和CHILD_SAs是根据此信息创建的。镜像CHILD_SAs与正常交换的CHILD_SAs没有区别;如果ClusterIP对此负责,它们将安装在内核中并处理数据包。

  IKE_SAs以特殊的PASSIVE状态安装。它们不处理流量,但仅接受来自同步消息的状态更改。被动IKE_SAs在IKE_SA管理器中作为任何其他SA进行管理,并且可以通过如 “ipsec statusall”的方式访问。

  密钥推导在镜像SA上重复,与在真实SA上完成的方式相同。这允许重用现有的安装例程。

控制消息

  除了同步消息之外,HA插件还使用控制消息来通知段的变化以及可选的消息以获取简单的监视功能:

  SEGMENT_DROP:发送节点正在放弃职责的段列表

  SEGMENT_TAKE:发送节点正在承担负责的段列表

  STATUS:心跳消息,包含发送节点负责的段列表

  RESYNC:请求重新同步段列表

  发送take/drop消息以通知其他节点有关守护进程自动所做的更改,或管理员的手动更改。接收节点将自动执行相反的操作来完全处理所有段。

  如果启用了心跳监视,则会定期发送STATUS消息。这允许检测远程节点的活动并接管远程节点未服务的段。它还实现了简单错误的节点故障检测。

  如果安装了故障节点的替换,则可以通过发送重新同步消息来加速节点的重新集成。活动节点将开始重新同步所有SA,允许管理员在之后重新平衡群集中的负载分配。

故障转移

  在故障转移的情况下,完整ClusterIP段的责任从一个节点移动到另一个节点。每个节点都可以启用或禁用段的责任。为此,插件使用相同的散列算法来根据源IP地址计算责任。

  如果激活了一个段,插件将在此段中搜索IKE_SAs,并将所有PASSIVE IKE_SAs的状态设置为ESTABLISHED。无需进一步操作:守护进程像其他每一个一样处理IKE_SA,并发送状态更改的同步消息。

  在段停用时,插件会在这些段中搜索ESTABLISHED状态的IKE_SAs,并将状态设置为PASSIVE。

  CHILD_SAs完全不受激活和取消激活的影响:它们始终处于活动状态并处理由ClusterIP分配的流量。

重新集成

  要将故障节点重新集成到集群中,该节点需要从头开始的状态信息。如果已同步所有必需的状态,重新集成节点可以再次用作故障转移节点。只有在交换了所有必需状态后,才能在重新集成的节点上激活段。

  每个节点在本地缓存当前活动的所有IKE_SAs的IKE_SA特定消息。如果其他节点想要重新集成,则主动节点会将消息缓存推送到新节点。这允许重新集成的节点为所有IKE_SA重新建立状态。

  使用缓存无法同步CHILD_SAs,因为消息不包含内核中管理的序列号信息。要重新集成节点,主动节点将启动对所有CHILD_SA的重新加密。新的CHILD_SA将从内核中的新序列号开始同步。CHILD_SA重新加密的代价小,因为它通常不包括DH交换。

构建HA插件

  必须在./configure阶段启用HA插件。插件必须使用与内核(jhash)相同的散列算法进行分段计算。此算法已针对Linux 2.6.37进行了更改,并且将来可能会再次更改。要使用正确版本的jhash,请针对您实际运行strongSwan的内核的内核头文件构建strongSwan:

./configure --enable-ha --with-linux-headers=/path/to/target/kernel/include/linux

配置

  配置在两个地方完成。手动安装必要的虚拟IP和ClusterIP规则。这显然不是由守护程序完成的,因为规则必须在守护程序关闭或错误条件后保持活动状态。  

  HA插件需要与已安装的ClusterIP规则匹配的配置。集群中的所有节点都需要相同的连接配置,但可以使用不同的凭证(即不同的私钥和证书来验证集群节点)。

集群IP

  扩展ClusterIP模块的配置类似于默认的ClusterIP设置。对于流量转发IPsec网关,通常是集群需要在每个节点上使用内部虚拟IP / MAC和外部虚拟IP / MAC。

ip address add 192.168.0.200/24 dev eth0
iptables -A INPUT -i eth0 -d 192.168.0.200 -j CLUSTERIP --new \
   --hashmode sourceip --clustermac 01:00:5e:00:00:20 \
   --total-nodes 2 --local-node 0

  此示例在接口eth0上安装虚拟IP 192.168.0.200,并添加相应的ClusterIP规则。ClusterIP规则始终添加到INPUT链中。要在内核和HA插件中获得相同的段责任计算结果,必须使用sourceip hashmode和散列初始值0(默认值)。

  total-nodes选项必须与HA插件的配置匹配,并且所有节点都需要相同的虚拟IP / MAC和ClusterIP配置。

  ClusterIP需要存在local-node选项。虽然HA插件在守护程序启动期间重新分配段责任,但建议使用零,因此启动的节点不会处理任何数据包,直到HA插件告知它为止。

HA插件

  HA插件配置在strongswan.conf文件中处理。

charon {
    # ...
  plugins {
    ha {
      local = 10.0.0.2
      remote = 10.0.0.1
      segment_count = 2
      # secret = s!ronG-P5K-s3cret
      fifo_interface = yes
      monitor = yes
      resync = yes
    }
  }
}

  本地和远程地址用于发送和接收同步消息,segment_count定义要使用的段数。

  如果指定了secret选项,则节点会自动为HA同步和控制消息建立预共享密钥认证的IPsec隧道(实验性的)。

  使用fifo_interface选项启用段责任管理界面。monitor参数启用基于心跳的远程节点监视,如果节点加入群集,则resync选项启用自动状态重新同步。可以使用heartbeat_delay和heartbeat_timeout选项配置心跳。

地址池

  使用地址池为客户端分配虚拟IP地址在集群中要复杂一些。只要有一个故障保存MySQL数据库集群,使用基于中央数据库的池应该没问题。

  内存中的地址池不提供任何同步:您不希望将来自两个不同节点的相同地址分配给不同的客户端。一种选择是拆分例如10.0.2.0/24池和10.0.3.0/24池中的10.0.2.0/23池,并在一个节点上使用每个池。但这种方法有其局限性:如果节点发生故障并丢弃IKE_SA,则池状态将丢失。如果它重新集成到集群中并接管相同的IKE_SA,则虚拟IP将再次使用。但重置池不会反映这一事实,可能会将同一地址重新分配给另一个客户端。

  作为替代方案,strongSwan 4.4.1提供了一个启用HA的内存地址池。它配置简单,但只能分发本地节点负责的地址(使用段计算)。此外,它为池中的地址保留由不同节点处理的IKE_SAs。

  要配置启用HA的内存中地址池,请添加地址池子段并定义所需的池。在两个节点上使用完全相同的池配置,不需要使用启用HA的实现拆分池!

# ...
    ha {
      # ...
      pools {
        sales = 10.0.1.0/24
        finance = 10.0.2.0/24
      }
    }

要使用这些池,请在ipsec.conf连接部分中引用它们:

rightsourceip=%sales

管理段责任

  为守护程序更改分段职责,它会将段责任的变化传播到内核。

  HA插件使用非常类似的接口进行分段控制,如ClusterIP。它使用位于/var/run/charon.ha的FIFO而不是proc条目。回声+ 1 / -1将激活/停用段1的责任,附加命令* 3将通过触发段3中所有SA的重定密来强制执行重新同步。

posted @ 2018-09-14 18:52  爱新陀螺  阅读(1565)  评论(0编辑  收藏  举报